class_docstr: A rendezvous implementation that uses `etcd <https://etcd.io/>`__ as the backend store.
class_docstr_tok: ["A","rendezvous","implementation","that","uses","`","etcd","<","https",":","\/\/etcd.io\/",">","`","__","as","the","backend","store","."]
docstr: After we reach min number of workers, one particular worker takes on the
  responsibility of waiting an additional timeout before closing the join window.
  If the worker responsible for this fails, the rendezvous will be destroyed due
  to expiring TTL, and the other participants will re-rendezvous.
  
  Here we expect to see state <joinable, expected_version>
  Exit gracefully if either:
  
  1. state becomes <frozen, expected_version>
  2. timeout happens (reaching deadline), in which case
     we try the transition to <frozen, expected_version>
  
  Exit with exception otherwise.
docstr_tok: ["After","we","reach","min","number","of","workers",",","one","particular","worker","takes","on","the","responsibility","of","waiting","an","additional","timeout","before","closing","the","join","window",".","If","the","worker","responsible","for","this","fails",",","the","rendezvous","will","be","destroyed","due","to","expiring","TTL",",","and","the","other","participants","will","re-rendezvous",".","Here","we","expect","to","see","state","<","joinable",",","expected_version",">","Exit","gracefully","if","either",":","1.","state","becomes","<","frozen",",","expected_version",">","2.","timeout","happens","(","reaching","deadline",")",",","in","which","case","we","try","the","transition","to","<","frozen",",","expected_version",">","Exit","with","exception","otherwise","."]
code: def handle_join_last_call(self, expected_version, deadline):
      """
      After we reach min number of workers, one particular worker takes on the
      responsibility of waiting an additional timeout before closing the join window.
      If the worker responsible for this fails, the rendezvous will be destroyed due
      to expiring TTL, and the other participants will re-rendezvous.
  
      Here we expect to see state <joinable, expected_version>
      Exit gracefully if either:
  
      1. state becomes <frozen, expected_version>
      2. timeout happens (reaching deadline), in which case
         we try the transition to <frozen, expected_version>
  
      Exit with exception otherwise.
      """
      active_version, state = self.get_rdzv_state()
      while True:
          if (
              state["status"] == "frozen"
              and state["version"] == expected_version
          ):
              # Worker set became frozen before last-call timeout. This is possible
              # when num_max_workers is reached before the timeout.
              return
  
          if (
              state["status"] != "joinable"
              or state["version"] != expected_version
          ):
              raise EtcdRendezvousRetryableFailure(
                  "Rendezvous state transition no longer possible. Must re-enter."
              )
  
          # If timeout occurred, attempt a state transition (joinable -> frozen)
          if time.time() >= deadline:
              state["status"] = "frozen"
              state["keep_alives"] = []
              try:
                  active_version = self.client.test_and_set(
                      key=self.get_path("/rdzv/active_version"),
                      value=json.dumps(state),
                      prev_value=active_version.value,
                      ttl=CONST_ETCD_FROZEN_TTL,
                  )
                  # We successfully made this rendezvous frozen.
                  return
              except etcd.EtcdCompareFailed:
                  log.info(
                      "Join last-call transition CAS unsuccessful. Will retry"
                  )
                  cas_delay()
                  active_version, state = self.get_rdzv_state()
                  continue
  
          # Timeout did not occur, so we must refresh TTL, and wait for
          # further changes. Note: we only want TTL to be refreshed if
          # state is still joinable, hence we use CAS for that here,
          # even though we don't change any of the data.
          try:
              active_version = self.client.test_and_set(
                  key=self.get_path("/rdzv/active_version"),
                  value=active_version.value,
                  prev_value=active_version.value,
                  ttl=CONST_ETCD_JOINABLE_EPHEMERAL_TTL,
              )
  
              # Minimize "oversleeping":
              timeout = min(
                  CONST_ETCD_JOINABLE_EPHEMERAL_TTL / 2,
                  deadline
                  - time.time()
                  + 1.0,  # Oversleeping by 1s is ok.
              )
              active_version, state = self.try_wait_for_state_change(
                  etcd_index=active_version.etcd_index + 1,
                  timeout=timeout,
              )
          except etcd.EtcdCompareFailed:
              log.info(
                  "Join last-call TTL refresh CAS unsuccessful, will retry"
              )
              cas_delay()
              active_version, state = self.get_rdzv_state()
  
imports: import json
  import logging
  import sys
  import threading
  import time
  from typing import Optional
  import etcd
  from torch.distributed.elastic.rendezvous import RendezvousClosedError, RendezvousError, RendezvousHandler, RendezvousParameters, RendezvousTimeoutError
  from .utils import parse_rendezvous_endpoint
  from .etcd_store import EtcdStore, cas_delay
label_desc: Use image node_id 15 for calling the EtcdRendezvous obj's underlying member method code with example usage: obj.handle_join_last_call(expected_version, deadline) without return types