/**
   * Perform phase2 of the recovery process
   *
   * <p>Phase2 takes a snapshot of the current translog *without* acquiring the write lock (however,
   * the translog snapshot is a point-in-time view of the translog). It then sends each translog
   * operation to the target node so it can be replayed into the new shard.
   *
   * <p>{@code InternalEngine#recover} is responsible for taking the snapshot of the translog and
   * releasing it once all 3 phases of recovery are complete
   */
  @Override
  public void phase2(Translog.Snapshot snapshot) throws ElasticsearchException {
    if (shard.state() == IndexShardState.CLOSED) {
      throw new IndexShardClosedException(request.shardId());
    }
    cancellableThreads.checkForCancel();
    logger.trace("{} recovery [phase2] to {}: start", request.shardId(), request.targetNode());
    StopWatch stopWatch = new StopWatch().start();
    cancellableThreads.execute(
        new Interruptable() {
          @Override
          public void run() throws InterruptedException {
            // Send a request preparing the new shard's translog to receive
            // operations. This ensures the shard engine is started and disables
            // garbage collection (not the JVM's GC!) of tombstone deletes
            transportService
                .submitRequest(
                    request.targetNode(),
                    RecoveryTarget.Actions.PREPARE_TRANSLOG,
                    new RecoveryPrepareForTranslogOperationsRequest(
                        request.recoveryId(),
                        request.shardId(),
                        shard.translog().estimatedNumberOfOperations()),
                    TransportRequestOptions.options()
                        .withTimeout(recoverySettings.internalActionTimeout()),
                    EmptyTransportResponseHandler.INSTANCE_SAME)
                .txGet();
          }
        });

    stopWatch.stop();
    response.startTime = stopWatch.totalTime().millis();
    logger.trace(
        "{} recovery [phase2] to {}: start took [{}]",
        request.shardId(),
        request.targetNode(),
        stopWatch.totalTime());

    logger.trace(
        "{} recovery [phase2] to {}: updating current mapping to master",
        request.shardId(),
        request.targetNode());
    // Ensure that the mappings are synced with the master node
    updateMappingOnMaster();

    logger.trace(
        "{} recovery [phase2] to {}: sending transaction log operations",
        request.shardId(),
        request.targetNode());
    stopWatch = new StopWatch().start();
    // Send all the snapshot's translog operations to the target
    int totalOperations = sendSnapshot(snapshot);
    stopWatch.stop();
    logger.trace(
        "{} recovery [phase2] to {}: took [{}]",
        request.shardId(),
        request.targetNode(),
        stopWatch.totalTime());
    response.phase2Time = stopWatch.totalTime().millis();
    response.phase2Operations = totalOperations;
  }