code: private void joinSynchronization()
        throws IOException, TimeoutException, InterruptedException {
      Message sync = MessageBuilder.buildSyncHistory(persistence.getLatestZxid());
      sendMessage(this.electedLeader, sync);
      MessageTuple tuple =
        filter.getExpectedMessage(MessageType.SYNC_HISTORY_REPLY, electedLeader,
                                  config.getTimeoutMs());
      Message msg = tuple.getMessage();
      // Updates the sync timeout based on leader's suggestion.
      setSyncTimeoutMs(msg.getSyncHistoryReply().getSyncTimeout());
      // Waits for the first synchronization completes.
      waitForSync(this.electedLeader);
  
      // Gets the last zxid in disk after first synchronization.
      Zxid lastZxid = persistence.getLatestZxid();
      LOG.debug("After first synchronization, the last zxid is {}", lastZxid);
      // Then issues the JOIN message.
      Message join = MessageBuilder.buildJoin(lastZxid);
      sendMessage(this.electedLeader, join);
  
      /* -- Synchronizing phase -- */
      changePhase(Phase.SYNCHRONIZING);
      waitForSync(this.electedLeader);
      waitForNewLeaderMessage();
      waitForCommitMessage();
      persistence.setProposedEpoch(persistence.getAckEpoch());
    }
docs: depends on the length of the synchronization.