anchor.diff:                 (phase3, phase_2_4()) -> phase3();
                  (phase4, phase_2_4()) -> phase4().
   set_phase(Phase, JoinState) -> setelement(1, JoinState, Phase).
  +-spec set_new_join_uuid(Phase) -> Phase when is_subtype(Phase, phase_2_4()).
  +set_new_join_uuid(JoinState) -> setelement(2, JoinState, util:get_pids_uid()).
   -spec set_join_ids(JoinIds::[?RT:key()], phase_2_4()) -> phase_2_4().
  -set_join_ids(JoinIds, JoinState) -> setelement(5, JoinState, JoinIds).
  +set_join_ids(JoinIds, JoinState) -> setelement(6, JoinState, JoinIds).
   -spec remove_join_id(JoinId::?RT:key(), phase_2_4()) -> phase_2_4().
  -remove_join_id(JoinIdToRemove, {Phase, Options, CurIdVersion, ContactNodes, JoinIds, Candidates}) ->
  -    {Phase, Options, CurIdVersion, ContactNodes,
  +remove_join_id(JoinIdToRemove, {Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, Candidates}) ->
  +    {Phase, JoinUUId, Options, CurIdVersion, ContactNodes,
        [Id || Id <- JoinIds, Id =/= JoinIdToRemove], Candidates}.
   -spec add_candidate_front(Candidate::lb_op:lb_op(), phase_2_4()) -> phase_2_4().
  -add_candidate_front(Candidate, {Phase, Options, CurIdVersion, ContactNodes, JoinIds, Candidates}) ->
  -    {Phase, Options, CurIdVersion, ContactNodes, JoinIds, [Candidate | Candidates]}.
  +add_candidate_front(Candidate, {Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, Candidates}) ->
  +    {Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, [Candidate | Candidates]}.
   -spec add_candidate_back(Candidate::lb_op:lb_op(), phase_2_4()) -> phase_2_4().
  -add_candidate_back(Candidate, {Phase, Options, CurIdVersion, ContactNodes, JoinIds, Candidates}) ->
  -    {Phase, Options, CurIdVersion, ContactNodes, JoinIds, lists:append(Candidates, [Candidate])}.
  +add_candidate_back(Candidate, {Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, Candidates}) ->
  +    {Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, lists:append(Candidates, [Candidate])}.
   -spec sort_candidates(phase_2_4()) -> phase_2_4().
  -sort_candidates({Phase, Options, CurIdVersion, ContactNodes, JoinIds, Candidates} = JoinState) ->
  +sort_candidates({Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, Candidates} = JoinState) ->
       LbPsv = get_lb_psv(JoinState),
  -    {Phase, Options, CurIdVersion, ContactNodes, JoinIds, LbPsv:sort_candidates(Candidates)}.
  +    {Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, LbPsv:sort_candidates(Candidates)}.
   -spec remove_candidate(CandId::lb_op:id(), phase_2_4()) -> phase_2_4().
  -remove_candidate(CandId, {Phase, Options, CurIdVersion, Connections, JoinIds, Candidates}) ->
  +remove_candidate(CandId, {Phase, JoinUUId, Options, CurIdVersion, Connections, JoinIds, Candidates}) ->
       NewCandidates = [C || C <- Candidates, lb_op:get(C, id) =/= CandId],
  -    {Phase, Options, CurIdVersion, Connections, JoinIds, NewCandidates}.
  +    {Phase, JoinUUId, Options, CurIdVersion, Connections, JoinIds, NewCandidates}.
   -spec remove_candidate_front(phase_2_4()) -> phase_2_4().
  -remove_candidate_front({Phase, Options, CurIdVersion, ContactNodes, JoinIds, []}) ->
  -    {Phase, Options, CurIdVersion, ContactNodes, JoinIds, []};
  -remove_candidate_front({Phase, Options, CurIdVersion, ContactNodes, JoinIds, [_ | Candidates]}) ->
  -    {Phase, Options, CurIdVersion, ContactNodes, JoinIds, Candidates}.
  +remove_candidate_front({Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, []}) ->
  +    {Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, []};
  +remove_candidate_front({Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, [_ | Candidates]}) ->
  +    {Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, Candidates}.
   -spec remove_candidate_front_keep_id(phase_2_4()) -> phase_2_4().
  -remove_candidate_front_keep_id({Phase, Options, CurIdVersion, ContactNodes, JoinIds, []}) ->
  -    {Phase, Options, CurIdVersion, ContactNodes, JoinIds, []};
  -remove_candidate_front_keep_id({Phase, Options, CurIdVersion, ContactNodes, JoinIds, [Front | Candidates]}) ->
  +remove_candidate_front_keep_id({Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, []}) ->
  +    {Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, []};
  +remove_candidate_front_keep_id({Phase, JoinUUId, Options, CurIdVersion, ContactNodes, JoinIds, [Front | Candidates]}) ->
       IdFront = node_details:get(lb_op:get(Front, n1_new), new_key),
  -    {Phase, Options, CurIdVersion, ContactNodes, [IdFront | JoinIds], Candidates}.
  +    {Phase, JoinUUId, Options, CurIdVersion, ContactNodes, [IdFront | JoinIds], Candidates}.
   -spec skip_psv_lb(phase_2_4()) -> boolean().
  -skip_psv_lb({_Phase, Options, _CurIdVersion, _ContactNodes, _JoinIds, _Candidates}) ->
  +skip_psv_lb({_Phase, _JoinUUId, Options, _CurIdVersion, _ContactNodes, _JoinIds, _Candidates}) ->
       lists:member({skip_psv_lb}, Options).
   
   -spec new_connection(OldConn::{null | pos_integer(), comm:mypid()})
  
positive.code:                                               "trying next candidate", [self()]),
                                  try_next_candidate(JoinState)
                          end,
                      {join, NewJoinState, QueuedMessages}
              end
      end;
  
  % ignore message arriving in other phases:
  process_join_state({join, join_request_timeout, _Timeouts, _CandId} = _Msg,
                     {join, _JoinState, _QueuedMessages} = State) ->
      ?TRACE1(_Msg, {join, _JoinState}),
      State;
  
negative.code: %% userdevguide-end dht_node_join:join_other_p4
  
  % a join timeout message re-starts the complete join
  process_join_state({join, timeout, JoinUUId} = _Msg, {join, JoinState, QueuedMessages})
    when (element(1, JoinState) =:= phase2 orelse
              element(1, JoinState) =:= phase2b orelse
              element(1, JoinState) =:= phase3 orelse
              element(1, JoinState) =:= phase4) andalso
             element(2, JoinState) =:= JoinUUId ->
      ?TRACE1(_Msg, {join, JoinState}),
      log:log(warn, "[ Node ~w ] join timeout hit, starting over...", [self()]),
      NewJoinState = start_over(JoinState),
      ?TRACE_STATE(NewJoinState),
      {join, NewJoinState, QueuedMessages};
  
  % ignore unrelated join timeout message:
  process_join_state({join, timeout, _JoinId} = _Msg,
                     {join, _JoinState, _QueuedMessages} = State) ->
      ?TRACE1(_Msg, {join, _JoinState}),
      State;
  
  process_join_state({web_debug_info, Requestor} = _Msg,
                     {join, JoinState, QueuedMessages} = State) ->
  