private Set<ByteArray> makeSlopKeys(
      Multimap<Integer, ByteArray> failedKeys, Slop.Operation operation) {
    Set<ByteArray> slopKeys = Sets.newHashSet();

    for (Map.Entry<Integer, ByteArray> entry : failedKeys.entries()) {
      byte[] opCode = new byte[] {operation.getOpCode()};
      byte[] spacer = new byte[] {(byte) 0};
      byte[] storeName = ByteUtils.getBytes(STORE_NAME, "UTF-8");
      byte[] nodeIdBytes = new byte[ByteUtils.SIZE_OF_INT];
      ByteUtils.writeInt(nodeIdBytes, entry.getKey(), 0);
      ByteArray slopKey =
          new ByteArray(
              ByteUtils.cat(
                  opCode, spacer, storeName, spacer, nodeIdBytes, spacer, entry.getValue().get()));
      slopKeys.add(slopKey);
    }
    return slopKeys;
  }
  /**
   * Create the voldemort key and value from the input key and value and map it out for each of the
   * responsible voldemort nodes
   *
   * <p>The output key is the md5 of the serialized key returned by makeKey(). The output value is
   * the node_id & partition_id of the responsible node followed by serialized value returned by
   * makeValue() OR if we have setKeys flag on the serialized key and serialized value
   */
  public void map(
      K key, V value, OutputCollector<BytesWritable, BytesWritable> output, Reporter reporter)
      throws IOException {
    byte[] keyBytes = keySerializer.toBytes(makeKey(key, value));
    byte[] valBytes = valueSerializer.toBytes(makeValue(key, value));

    // Compress key and values if required
    if (keySerializerDefinition.hasCompression()) {
      keyBytes = keyCompressor.deflate(keyBytes);
    }

    if (valueSerializerDefinition.hasCompression()) {
      valBytes = valueCompressor.deflate(valBytes);
    }

    // Get the output byte arrays ready to populate
    byte[] outputValue;
    BytesWritable outputKey;

    // Leave initial offset for (a) node id (b) partition id
    // since they are written later
    int offsetTillNow = 2 * ByteUtils.SIZE_OF_INT;

    // In order - 4 ( for node id ) + 4 ( partition id ) + 1 ( replica
    // type - primary | secondary | tertiary... ] + 4 ( key size )
    // size ) + 4 ( value size ) + key + value
    outputValue =
        new byte
            [valBytes.length
                + keyBytes.length
                + ByteUtils.SIZE_OF_BYTE
                + 4 * ByteUtils.SIZE_OF_INT];

    // Write key length - leave byte for replica type
    offsetTillNow += ByteUtils.SIZE_OF_BYTE;
    ByteUtils.writeInt(outputValue, keyBytes.length, offsetTillNow);

    // Write value length
    offsetTillNow += ByteUtils.SIZE_OF_INT;
    ByteUtils.writeInt(outputValue, valBytes.length, offsetTillNow);

    // Write key
    offsetTillNow += ByteUtils.SIZE_OF_INT;
    System.arraycopy(keyBytes, 0, outputValue, offsetTillNow, keyBytes.length);

    // Write value
    offsetTillNow += keyBytes.length;
    System.arraycopy(valBytes, 0, outputValue, offsetTillNow, valBytes.length);

    // Generate MR key - 16 byte md5
    outputKey = new BytesWritable(keyBytes);

    // Generate partition and node list this key is destined for
    List<Integer> partitionList = routingStrategy.getPartitionList(keyBytes);
    Node[] partitionToNode = routingStrategy.getPartitionToNode();

    for (int replicaType = 0; replicaType < partitionList.size(); replicaType++) {

      // Node id
      ByteUtils.writeInt(outputValue, partitionToNode[partitionList.get(replicaType)].getId(), 0);

      // Primary partition id
      ByteUtils.writeInt(outputValue, partitionList.get(0), ByteUtils.SIZE_OF_INT);

      // Replica type
      ByteUtils.writeBytes(
          outputValue, replicaType, 2 * ByteUtils.SIZE_OF_INT, ByteUtils.SIZE_OF_BYTE);

      BytesWritable outputVal = new BytesWritable(outputValue);

      output.collect(outputKey, outputVal);
    }
  }