private Map<LoadQueueItem, ByteBuffer> performBulkLoad(AsyncClusterConnection conn,
    TableName tableName, Deque<LoadQueueItem> queue, ExecutorService pool, boolean copyFile)
    throws IOException {
    int count = 0;

    fsDelegationToken.acquireDelegationToken(queue.peek().getFilePath().getFileSystem(getConf()));
    bulkToken = FutureUtils.get(conn.prepareBulkLoad(tableName));
    Pair<Multimap<ByteBuffer, LoadQueueItem>, Set<String>> pair = null;

    Map<LoadQueueItem, ByteBuffer> item2RegionMap = new HashMap<>();
    // Assumes that region splits can happen while this occurs.
    while (!queue.isEmpty()) {
      // need to reload split keys each iteration.
      final List<Pair<byte[], byte[]>> startEndKeys =
        FutureUtils.get(conn.getRegionLocator(tableName).getStartEndKeys());
      if (count != 0) {
        LOG.info("Split occurred while grouping HFiles, retry attempt " + count + " with "
          + queue.size() + " files remaining to group or split");
      }

      int maxRetries = getConf().getInt(HConstants.BULKLOAD_MAX_RETRIES_NUMBER, 10);
      maxRetries = Math.max(maxRetries, startEndKeys.size() + 1);
      if (maxRetries != 0 && count >= maxRetries) {
        throw new IOException(
          "Retry attempted " + count + " times without completing, bailing out");
      }
      count++;

      // Using ByteBuffer for byte[] equality semantics
      pair = groupOrSplitPhase(conn, tableName, pool, queue, startEndKeys);
      Multimap<ByteBuffer, LoadQueueItem> regionGroups = pair.getFirst();

      if (!checkHFilesCountPerRegionPerFamily(regionGroups)) {
        // Error is logged inside checkHFilesCountPerRegionPerFamily.
        throw new IOException("Trying to load more than " + maxFilesPerRegionPerFamily
          + " hfiles to one family of one region");
      }

      bulkLoadPhase(conn, tableName, queue, regionGroups, copyFile, item2RegionMap);

      // NOTE: The next iteration's split / group could happen in parallel to
      // atomic bulkloads assuming that there are splits and no merges, and
      // that we can atomically pull out the groups we want to retry.
    }

    return item2RegionMap;
  }
If the table is created for the first time, then "completebulkload" reads the files twice. More
modifications necessary if we want to avoid doing it.
hbase-server/src/main/java/org/apache/hadoop/hbase/tool/BulkLoadHFilesTool.java