public void testV0LegacyTranslogVersion() throws Exception {
    Path translogFile = getDataPath("/org/elasticsearch/index/translog/translog-v0.binary");
    assertThat("test file should exist", Files.exists(translogFile), equalTo(true));
    try (ImmutableTranslogReader reader = openReader(translogFile, 0)) {
      assertThat(
          "a version0 stream is returned", reader instanceof LegacyTranslogReader, equalTo(true));
      try (final Translog.Snapshot snapshot = reader.newSnapshot()) {
        final Translog.Operation operation = snapshot.next();
        assertThat(
            "operation is the correct type correctly",
            operation.opType() == Translog.Operation.Type.INDEX,
            equalTo(true));
        Translog.Index op = (Translog.Index) operation;
        assertThat(op.id(), equalTo("1"));
        assertThat(op.type(), equalTo("doc"));
        assertThat(
            op.source().toUtf8(),
            equalTo("{\"body\": \"worda wordb wordc wordd \\\"worde\\\" wordf\"}"));
        assertThat(op.routing(), equalTo(null));
        assertThat(op.parent(), equalTo(null));
        assertThat(op.version(), equalTo(1L));
        assertThat(op.timestamp(), equalTo(1407312091791L));
        assertThat(op.ttl(), equalTo(-1L));
        assertThat(op.versionType(), equalTo(VersionType.INTERNAL));

        assertNull(snapshot.next());
      }
    }
  }
  public void testV1ChecksummedTranslogVersion() throws Exception {
    Path translogFile = getDataPath("/org/elasticsearch/index/translog/translog-v1.binary");
    assertThat("test file should exist", Files.exists(translogFile), equalTo(true));
    try (ImmutableTranslogReader reader = openReader(translogFile, 0)) {
      try (final Translog.Snapshot snapshot = reader.newSnapshot()) {

        assertThat(
            "a version1 stream is returned",
            reader instanceof ImmutableTranslogReader,
            equalTo(true));

        Translog.Operation operation = snapshot.next();

        assertThat(
            "operation is the correct type correctly",
            operation.opType() == Translog.Operation.Type.INDEX,
            equalTo(true));
        Translog.Index op = (Translog.Index) operation;
        assertThat(op.id(), equalTo("Bwiq98KFSb6YjJQGeSpeiw"));
        assertThat(op.type(), equalTo("doc"));
        assertThat(op.source().toUtf8(), equalTo("{\"body\": \"foo\"}"));
        assertThat(op.routing(), equalTo(null));
        assertThat(op.parent(), equalTo(null));
        assertThat(op.version(), equalTo(1L));
        assertThat(op.timestamp(), equalTo(1408627184844L));
        assertThat(op.ttl(), equalTo(-1L));
        assertThat(op.versionType(), equalTo(VersionType.INTERNAL));

        // There are more operations
        int opNum = 1;
        while (snapshot.next() != null) {
          opNum++;
        }
        assertThat("there should be 5 translog operations", opNum, equalTo(5));
      }
    }
  }
  public void testCorruptedTranslogs() throws Exception {
    try {
      Path translogFile =
          getDataPath("/org/elasticsearch/index/translog/translog-v1-corrupted-magic.binary");
      assertThat("test file should exist", Files.exists(translogFile), equalTo(true));
      openReader(translogFile, 0);
      fail("should have thrown an exception about the header being corrupt");
    } catch (TranslogCorruptedException e) {
      assertThat(
          "translog corruption from header: " + e.getMessage(),
          e.getMessage()
              .contains("translog looks like version 1 or later, but has corrupted header"),
          equalTo(true));
    }

    try {
      Path translogFile =
          getDataPath("/org/elasticsearch/index/translog/translog-invalid-first-byte.binary");
      assertThat("test file should exist", Files.exists(translogFile), equalTo(true));
      openReader(translogFile, 0);
      fail("should have thrown an exception about the header being corrupt");
    } catch (TranslogCorruptedException e) {
      assertThat(
          "translog corruption from header: " + e.getMessage(),
          e.getMessage()
              .contains("Invalid first byte in translog file, got: 1, expected 0x00 or 0x3f"),
          equalTo(true));
    }

    try {
      Path translogFile =
          getDataPath("/org/elasticsearch/index/translog/translog-v1-corrupted-body.binary");
      assertThat("test file should exist", Files.exists(translogFile), equalTo(true));
      try (ImmutableTranslogReader reader = openReader(translogFile, 0)) {
        try (final Translog.Snapshot snapshot = reader.newSnapshot()) {
          while (snapshot.next() != null) {}
        }
      }
      fail("should have thrown an exception about the body being corrupted");
    } catch (TranslogCorruptedException e) {
      assertThat(
          "translog corruption from body: " + e.getMessage(),
          e.getMessage().contains("translog corruption while reading from stream"),
          equalTo(true));
    }
  }
 public void testTruncatedTranslog() throws Exception {
   try {
     Path translogFile =
         getDataPath("/org/elasticsearch/index/translog/translog-v1-truncated.binary");
     assertThat("test file should exist", Files.exists(translogFile), equalTo(true));
     try (ImmutableTranslogReader reader = openReader(translogFile, 0)) {
       try (final Translog.Snapshot snapshot = reader.newSnapshot()) {
         while (snapshot.next() != null) {}
       }
     }
     fail("should have thrown an exception about the body being truncated");
   } catch (TranslogCorruptedException e) {
     assertThat(
         "translog truncated: " + e.getMessage(),
         e.getMessage().contains("operation size is corrupted must be"),
         equalTo(true));
   }
 }
  /**
   * Send the given snapshot's operations to this handler's target node.
   *
   * <p>Operations are bulked into a single request depending on an operation count limit or
   * size-in-bytes limit
   *
   * @return the total number of translog operations that were sent
   */
  protected int sendSnapshot(final Translog.Snapshot snapshot) {
    int ops = 0;
    long size = 0;
    int totalOperations = 0;
    final List<Translog.Operation> operations = new ArrayList<>();
    Translog.Operation operation;
    try {
      operation = snapshot.next(); // this ex should bubble up
    } catch (IOException ex) {
      throw new ElasticsearchException("failed to get next operation from translog", ex);
    }

    if (operation == null) {
      logger.trace(
          "[{}][{}] no translog operations to send to {}",
          indexName,
          shardId,
          request.targetNode());
    }
    while (operation != null) {
      if (shard.state() == IndexShardState.CLOSED) {
        throw new IndexShardClosedException(request.shardId());
      }
      cancellableThreads.checkForCancel();
      operations.add(operation);
      ops += 1;
      size += operation.estimateSize();
      totalOperations++;

      // Check if this request is past bytes threshold, and
      // if so, send it off
      if (size >= chunkSizeInBytes) {

        // don't throttle translog, since we lock for phase3 indexing,
        // so we need to move it as fast as possible. Note, since we
        // index docs to replicas while the index files are recovered
        // the lock can potentially be removed, in which case, it might
        // make sense to re-enable throttling in this phase
        cancellableThreads.execute(
            () -> recoveryTarget.indexTranslogOperations(operations, snapshot.totalOperations()));
        if (logger.isTraceEnabled()) {
          logger.trace(
              "[{}][{}] sent batch of [{}][{}] (total: [{}]) translog operations to {}",
              indexName,
              shardId,
              ops,
              new ByteSizeValue(size),
              snapshot.totalOperations(),
              request.targetNode());
        }

        ops = 0;
        size = 0;
        operations.clear();
      }
      try {
        operation = snapshot.next(); // this ex should bubble up
      } catch (IOException ex) {
        throw new ElasticsearchException("failed to get next operation from translog", ex);
      }
    }
    // send the leftover
    if (!operations.isEmpty()) {
      cancellableThreads.execute(
          () -> recoveryTarget.indexTranslogOperations(operations, snapshot.totalOperations()));
    }
    if (logger.isTraceEnabled()) {
      logger.trace(
          "[{}][{}] sent final batch of [{}][{}] (total: [{}]) translog operations to {}",
          indexName,
          shardId,
          ops,
          new ByteSizeValue(size),
          snapshot.totalOperations(),
          request.targetNode());
    }
    return totalOperations;
  }
  /**
   * Send the given snapshot's operations to this handler's target node.
   *
   * <p>Operations are bulked into a single request depending on an operation count limit or
   * size-in-bytes limit
   *
   * @return the total number of translog operations that were sent
   */
  protected int sendSnapshot(Translog.Snapshot snapshot) throws ElasticsearchException {
    int ops = 0;
    long size = 0;
    int totalOperations = 0;
    final List<Translog.Operation> operations = Lists.newArrayList();
    Translog.Operation operation = snapshot.next();

    final TransportRequestOptions recoveryOptions =
        TransportRequestOptions.options()
            .withCompress(recoverySettings.compress())
            .withType(TransportRequestOptions.Type.RECOVERY)
            .withTimeout(recoverySettings.internalActionLongTimeout());

    if (operation == null) {
      logger.trace(
          "[{}][{}] no translog operations (id: [{}]) to send to {}",
          indexName,
          shardId,
          snapshot.translogId(),
          request.targetNode());
    }
    while (operation != null) {
      if (shard.state() == IndexShardState.CLOSED) {
        throw new IndexShardClosedException(request.shardId());
      }
      cancellableThreads.checkForCancel();
      operations.add(operation);
      ops += 1;
      size += operation.estimateSize();
      totalOperations++;

      // Check if this request is past the size or bytes threshold, and
      // if so, send it off
      if (ops >= recoverySettings.translogOps()
          || size >= recoverySettings.translogSize().bytes()) {

        // don't throttle translog, since we lock for phase3 indexing,
        // so we need to move it as fast as possible. Note, since we
        // index docs to replicas while the index files are recovered
        // the lock can potentially be removed, in which case, it might
        // make sense to re-enable throttling in this phase
        //                if (recoverySettings.rateLimiter() != null) {
        //                    recoverySettings.rateLimiter().pause(size);
        //                }

        if (logger.isTraceEnabled()) {
          logger.trace(
              "[{}][{}] sending batch of [{}][{}] (total: [{}], id: [{}]) translog operations to {}",
              indexName,
              shardId,
              ops,
              new ByteSizeValue(size),
              shard.translog().estimatedNumberOfOperations(),
              snapshot.translogId(),
              request.targetNode());
        }
        cancellableThreads.execute(
            new Interruptable() {
              @Override
              public void run() throws InterruptedException {
                final RecoveryTranslogOperationsRequest translogOperationsRequest =
                    new RecoveryTranslogOperationsRequest(
                        request.recoveryId(),
                        request.shardId(),
                        operations,
                        shard.translog().estimatedNumberOfOperations());
                transportService
                    .submitRequest(
                        request.targetNode(),
                        RecoveryTarget.Actions.TRANSLOG_OPS,
                        translogOperationsRequest,
                        recoveryOptions,
                        EmptyTransportResponseHandler.INSTANCE_SAME)
                    .txGet();
              }
            });

        ops = 0;
        size = 0;
        operations.clear();
      }
      operation = snapshot.next();
    }
    // send the leftover
    if (logger.isTraceEnabled()) {
      logger.trace(
          "[{}][{}] sending final batch of [{}][{}] (total: [{}], id: [{}]) translog operations to {}",
          indexName,
          shardId,
          ops,
          new ByteSizeValue(size),
          shard.translog().estimatedNumberOfOperations(),
          snapshot.translogId(),
          request.targetNode());
    }
    if (!operations.isEmpty()) {
      cancellableThreads.execute(
          new Interruptable() {
            @Override
            public void run() throws InterruptedException {
              RecoveryTranslogOperationsRequest translogOperationsRequest =
                  new RecoveryTranslogOperationsRequest(
                      request.recoveryId(),
                      request.shardId(),
                      operations,
                      shard.translog().estimatedNumberOfOperations());
              transportService
                  .submitRequest(
                      request.targetNode(),
                      RecoveryTarget.Actions.TRANSLOG_OPS,
                      translogOperationsRequest,
                      recoveryOptions,
                      EmptyTransportResponseHandler.INSTANCE_SAME)
                  .txGet();
            }
          });
    }
    return totalOperations;
  }