@Override
      public void write(ImmutableBytesWritable row, V cell) throws IOException {
        // null input == user explicitly wants to flush
        if (row == null && cell == null) {
          rollWriters(null);
          return;
        }

        ExtendedCell kv = PrivateCellUtil.ensureExtendedCell(cell);
        byte[] rowKey = CellUtil.cloneRow(kv);
        int length = (PrivateCellUtil.estimatedSerializedSizeOf(kv)) - Bytes.SIZEOF_INT;
        byte[] family = CellUtil.cloneFamily(kv);
        if (writeMultipleTables) {
          tableNameBytes = MultiTableHFileOutputFormat.getTableName(row.get());
          tableNameBytes = TableName.valueOf(tableNameBytes).getNameWithNamespaceInclAsString()
            .getBytes(Charset.defaultCharset());
          if (!allTableNames.contains(Bytes.toString(tableNameBytes))) {
            throw new IllegalArgumentException(
              "TableName " + Bytes.toString(tableNameBytes) + " not expected");
          }
        }
        byte[] tableAndFamily = getTableNameSuffixedWithFamily(tableNameBytes, family);

        WriterLength wl = this.writers.get(tableAndFamily);

        // If this is a new column family, verify that the directory exists
        if (wl == null) {
          Path writerPath = null;
          if (writeMultipleTables) {
            Path tableRelPath = getTableRelativePath(tableNameBytes);
            writerPath = new Path(outputDir, new Path(tableRelPath, Bytes.toString(family)));
          } else {
            writerPath = new Path(outputDir, Bytes.toString(family));
          }
          fs.mkdirs(writerPath);
          configureStoragePolicy(conf, fs, tableAndFamily, writerPath);
        }

        // This can only happen once a row is finished though
        if (
          wl != null && wl.written + length >= maxsize
            && Bytes.compareTo(this.previousRows.get(family), rowKey) != 0
        ) {
          rollWriters(wl);
        }

        // create a new WAL writer, if necessary
        if (wl == null || wl.writer == null) {
          InetSocketAddress[] favoredNodes = null;
          if (conf.getBoolean(LOCALITY_SENSITIVE_CONF_KEY, DEFAULT_LOCALITY_SENSITIVE)) {
            HRegionLocation loc = null;
            String tableName = Bytes.toString(tableNameBytes);
            if (tableName != null) {
              try (
                Connection connection =
                  ConnectionFactory.createConnection(createRemoteClusterConf(conf));
                RegionLocator locator = connection.getRegionLocator(TableName.valueOf(tableName))) {
                loc = locator.getRegionLocation(rowKey);
              } catch (Throwable e) {
                LOG.warn("Something wrong locating rowkey {} in {}", Bytes.toString(rowKey),
                  tableName, e);
                loc = null;
              }
            }
            if (null == loc) {
              LOG.trace("Failed get of location, use default writer {}", Bytes.toString(rowKey));
            } else {
              LOG.debug("First rowkey: [{}]", Bytes.toString(rowKey));
              InetSocketAddress initialIsa =
                new InetSocketAddress(loc.getHostname(), loc.getPort());
              if (initialIsa.isUnresolved()) {
                LOG.trace("Failed resolve address {}, use default writer", loc.getHostnamePort());
              } else {
                LOG.debug("Use favored nodes writer: {}", initialIsa.getHostString());
                favoredNodes = new InetSocketAddress[] { initialIsa };
              }
            }
          }
          wl = getNewWriter(tableNameBytes, family, conf, favoredNodes);

        }

        // we now have the proper WAL writer. full steam ahead
        PrivateCellUtil.updateLatestStamp(kv, this.now);
        wl.writer.append((ExtendedCell) kv);
        wl.written += length;

        // Copy the row so we know when a row transition.
        this.previousRows.put(family, rowKey);
      }
ExtendedCell and ExtendedCellSerialization are InterfaceAudience.Private. We expose this config
for internal usage in jobs like WALPlayer which need to use features of ExtendedCell.
hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mapreduce/HFileOutputFormat2.java