@Override
  protected void map(NullWritable key, PhoenixIndexDBWritable record, Context context)
      throws IOException, InterruptedException {

    context.getCounter(PhoenixJobCounters.INPUT_RECORDS).increment(1);

    try {
      final List<Object> values = record.getValues();
      indxWritable.setValues(values);
      indxWritable.write(this.pStatement);
      this.pStatement.execute();

      final PhoenixConnection pconn = connection.unwrap(PhoenixConnection.class);
      MutationState currentMutationState = pconn.getMutationState();
      if (mutationState == null) {
        mutationState = currentMutationState;
        return;
      }
      // Keep accumulating Mutations till batch size
      mutationState.join(currentMutationState);

      // Write Mutation Batch
      if (context.getCounter(PhoenixJobCounters.INPUT_RECORDS).getValue() % batchSize == 0) {
        writeBatch(mutationState, context);
        mutationState = null;
      }

      // Make sure progress is reported to Application Master.
      context.progress();
    } catch (SQLException e) {
      LOG.error(" Error {}  while read/write of a record ", e.getMessage());
      context.getCounter(PhoenixJobCounters.FAILED_RECORDS).increment(1);
      throw new RuntimeException(e);
    }
  }
  private void writeBatch(MutationState mutationState, Context context)
      throws IOException, SQLException, InterruptedException {
    final Iterator<Pair<byte[], List<Mutation>>> iterator = mutationState.toMutations(true, null);
    while (iterator.hasNext()) {
      Pair<byte[], List<Mutation>> mutationPair = iterator.next();

      writer.write(mutationPair.getSecond());
      context
          .getCounter(PhoenixJobCounters.OUTPUT_RECORDS)
          .increment(mutationPair.getSecond().size());
    }
    connection.rollback();
  }
  @Override
  public PeekingResultIterator newIterator(
      final StatementContext parentContext,
      ResultIterator iterator,
      Scan scan,
      String tableName,
      QueryPlan plan)
      throws SQLException {
    final PhoenixConnection clonedConnection = new PhoenixConnection(this.connection);

    MutationState state = mutate(parentContext, iterator, clonedConnection);

    long totalRowCount = state.getUpdateCount();
    if (clonedConnection.getAutoCommit()) {
      clonedConnection.getMutationState().join(state);
      state = clonedConnection.getMutationState();
    }
    final MutationState finalState = state;

    byte[] value = PLong.INSTANCE.toBytes(totalRowCount);
    KeyValue keyValue =
        KeyValueUtil.newKeyValue(
            UNGROUPED_AGG_ROW_KEY,
            SINGLE_COLUMN_FAMILY,
            SINGLE_COLUMN,
            AGG_TIMESTAMP,
            value,
            0,
            value.length);
    final Tuple tuple = new SingleKeyValueTuple(keyValue);
    return new PeekingResultIterator() {
      private boolean done = false;

      @Override
      public Tuple next() throws SQLException {
        if (done) {
          return null;
        }
        done = true;
        return tuple;
      }

      @Override
      public void explain(List<String> planSteps) {}

      @Override
      public void close() throws SQLException {
        try {
          /*
           * Join the child mutation states in close, since this is called in a single threaded manner
           * after the parallel results have been processed.
           * If auto-commit is on for the cloned child connection, then the finalState here is an empty mutation
           * state (with no mutations). However, it still has the metrics for mutation work done by the
           * mutating-iterator. Joining the mutation state makes sure those metrics are passed over
           * to the parent connection.
           */
          MutatingParallelIteratorFactory.this.connection.getMutationState().join(finalState);
        } finally {
          clonedConnection.close();
        }
      }

      @Override
      public Tuple peek() throws SQLException {
        return done ? null : tuple;
      }
    };
  }