/** performs the recovery from the local engine to the target */
  public RecoveryResponse recoverToTarget() throws IOException {
    try (Translog.View translogView = shard.acquireTranslogView()) {
      logger.trace("captured translog id [{}] for recovery", translogView.minTranslogGeneration());
      final IndexCommit phase1Snapshot;
      try {
        phase1Snapshot = shard.acquireIndexCommit(false);
      } catch (Exception e) {
        IOUtils.closeWhileHandlingException(translogView);
        throw new RecoveryEngineException(shard.shardId(), 1, "Snapshot failed", e);
      }

      try {
        phase1(phase1Snapshot, translogView);
      } catch (Exception e) {
        throw new RecoveryEngineException(shard.shardId(), 1, "phase1 failed", e);
      } finally {
        try {
          shard.releaseIndexCommit(phase1Snapshot);
        } catch (IOException ex) {
          logger.warn("releasing snapshot caused exception", ex);
        }
      }

      // engine was just started at the end of phase 1
      if (shard.state() == IndexShardState.RELOCATED) {
        /**
         * The primary shard has been relocated while we copied files. This means that we can't
         * guarantee any more that all operations that were replicated during the file copy (when
         * the target engine was not yet opened) will be present in the local translog and thus will
         * be resent on phase 2. The reason is that an operation replicated by the target primary is
         * sent to the recovery target and the local shard (old primary) concurrently, meaning it
         * may have arrived at the recovery target before we opened the engine and is still
         * in-flight on the local shard.
         *
         * <p>Checking the relocated status here, after we opened the engine on the target, is safe
         * because primary relocation waits for all ongoing operations to complete and be fully
         * replicated. Therefore all future operation by the new primary are guaranteed to reach the
         * target shard when it's engine is open.
         */
        throw new IndexShardRelocatedException(request.shardId());
      }

      logger.trace(
          "{} snapshot translog for recovery. current size is [{}]",
          shard.shardId(),
          translogView.totalOperations());
      try {
        phase2(translogView.snapshot());
      } catch (Exception e) {
        throw new RecoveryEngineException(shard.shardId(), 2, "phase2 failed", e);
      }

      finalizeRecovery();
    }
    return response;
  }