/**
  * Utility method to create either an index or a create operation depending on the {@link OpType}
  * of the request.
  */
 private final Engine.Index prepareIndexOperationOnPrimary(
     BulkShardRequest shardRequest, IndexRequest request, IndexShard indexShard) {
   SourceToParse sourceToParse =
       SourceToParse.source(SourceToParse.Origin.PRIMARY, request.source())
           .index(request.index())
           .type(request.type())
           .id(request.id())
           .routing(request.routing())
           .parent(request.parent())
           .timestamp(request.timestamp())
           .ttl(request.ttl());
   return indexShard.prepareIndex(
       sourceToParse, request.version(), request.versionType(), Engine.Operation.Origin.PRIMARY);
 }
  @Override
  protected void shardOperationOnReplica(ShardId shardId, IndexRequest request) {
    IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex());
    IndexShard indexShard = indexService.shardSafe(shardId.id());
    SourceToParse sourceToParse =
        SourceToParse.source(SourceToParse.Origin.REPLICA, request.source())
            .index(shardId.getIndex())
            .type(request.type())
            .id(request.id())
            .routing(request.routing())
            .parent(request.parent())
            .timestamp(request.timestamp())
            .ttl(request.ttl());

    final Engine.IndexingOperation operation;
    if (request.opType() == IndexRequest.OpType.INDEX) {
      operation =
          indexShard.prepareIndex(
              sourceToParse,
              request.version(),
              request.versionType(),
              Engine.Operation.Origin.REPLICA);
    } else {
      assert request.opType() == IndexRequest.OpType.CREATE : request.opType();
      operation =
          indexShard.prepareCreate(
              sourceToParse,
              request.version(),
              request.versionType(),
              Engine.Operation.Origin.REPLICA);
    }
    Mapping update = operation.parsedDoc().dynamicMappingsUpdate();
    if (update != null) {
      throw new RetryOnReplicaException(
          shardId, "Mappings are not available on the replica yet, triggered update: " + update);
    }
    operation.execute(indexShard);
    processAfter(request.refresh(), indexShard, operation.getTranslogLocation());
  }
  @Override
  protected void shardOperationOnReplica(ShardId shardId, BulkShardRequest request) {
    IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex());
    IndexShard indexShard = indexService.shardSafe(shardId.id());
    Translog.Location location = null;
    for (int i = 0; i < request.items().length; i++) {
      BulkItemRequest item = request.items()[i];
      if (item == null || item.isIgnoreOnReplica()) {
        continue;
      }
      if (item.request() instanceof IndexRequest) {
        IndexRequest indexRequest = (IndexRequest) item.request();
        try {
          SourceToParse sourceToParse =
              SourceToParse.source(SourceToParse.Origin.REPLICA, indexRequest.source())
                  .index(shardId.getIndex())
                  .type(indexRequest.type())
                  .id(indexRequest.id())
                  .routing(indexRequest.routing())
                  .parent(indexRequest.parent())
                  .timestamp(indexRequest.timestamp())
                  .ttl(indexRequest.ttl());

          final Engine.IndexingOperation operation;
          if (indexRequest.opType() == IndexRequest.OpType.INDEX) {
            operation =
                indexShard.prepareIndex(
                    sourceToParse,
                    indexRequest.version(),
                    indexRequest.versionType(),
                    Engine.Operation.Origin.REPLICA,
                    request.canHaveDuplicates() || indexRequest.canHaveDuplicates());
          } else {
            assert indexRequest.opType() == IndexRequest.OpType.CREATE : indexRequest.opType();
            operation =
                indexShard.prepareCreate(
                    sourceToParse,
                    indexRequest.version(),
                    indexRequest.versionType(),
                    Engine.Operation.Origin.REPLICA,
                    request.canHaveDuplicates() || indexRequest.canHaveDuplicates(),
                    indexRequest.autoGeneratedId());
          }
          Mapping update = operation.parsedDoc().dynamicMappingsUpdate();
          if (update != null) {
            throw new RetryOnReplicaException(
                shardId,
                "Mappings are not available on the replica yet, triggered update: " + update);
          }
          operation.execute(indexShard);
          location = locationToSync(location, operation.getTranslogLocation());
        } catch (Throwable e) {
          // if its not an ignore replica failure, we need to make sure to bubble up the failure
          // so we will fail the shard
          if (!ignoreReplicaException(e)) {
            throw e;
          }
        }
      } else if (item.request() instanceof DeleteRequest) {
        DeleteRequest deleteRequest = (DeleteRequest) item.request();
        try {
          Engine.Delete delete =
              indexShard.prepareDelete(
                  deleteRequest.type(),
                  deleteRequest.id(),
                  deleteRequest.version(),
                  deleteRequest.versionType(),
                  Engine.Operation.Origin.REPLICA);
          indexShard.delete(delete);
          location = locationToSync(location, delete.getTranslogLocation());
        } catch (Throwable e) {
          // if its not an ignore replica failure, we need to make sure to bubble up the failure
          // so we will fail the shard
          if (!ignoreReplicaException(e)) {
            throw e;
          }
        }
      } else {
        throw new IllegalStateException("Unexpected index operation: " + item.request());
      }
    }

    processAfter(request.refresh(), indexShard, location);
  }