@InterfaceAudience.Private
  protected Pair<List<LoadQueueItem>, String> groupOrSplit(AsyncClusterConnection conn,
    TableName tableName, Multimap<ByteBuffer, LoadQueueItem> regionGroups, LoadQueueItem item,
    List<Pair<byte[], byte[]>> startEndKeys) throws IOException {
    Path hfilePath = item.getFilePath();
    Optional<byte[]> first, last;
    try (HFile.Reader hfr = HFile.createReader(hfilePath.getFileSystem(getConf()), hfilePath,
      CacheConfig.DISABLED, true, getConf())) {
      first = hfr.getFirstRowKey();
      last = hfr.getLastRowKey();
    } catch (FileNotFoundException fnfe) {
      LOG.debug("encountered", fnfe);
      return new Pair<>(null, hfilePath.getName());
    }

    LOG.info("Trying to load hfile=" + hfilePath + " first=" + first.map(Bytes::toStringBinary)
      + " last=" + last.map(Bytes::toStringBinary));
    if (!first.isPresent() || !last.isPresent()) {
      assert !first.isPresent() && !last.isPresent();
      // TODO what if this is due to a bad HFile?
      LOG.info("hfile " + hfilePath + " has no entries, skipping");
      return null;
    }
    if (Bytes.compareTo(first.get(), last.get()) > 0) {
      throw new IllegalArgumentException("Invalid range: " + Bytes.toStringBinary(first.get())
        + " > " + Bytes.toStringBinary(last.get()));
    }
    int firstKeyRegionIdx = getRegionIndex(startEndKeys, first.get());
    checkRegionIndexValid(firstKeyRegionIdx, startEndKeys, tableName);
    boolean lastKeyInRange =
      Bytes.compareTo(last.get(), startEndKeys.get(firstKeyRegionIdx).getSecond()) < 0 || Bytes
        .equals(startEndKeys.get(firstKeyRegionIdx).getSecond(), HConstants.EMPTY_BYTE_ARRAY);
    if (!lastKeyInRange) {
      if (failIfNeedSplitHFile) {
        throw new IOException(
          "The key range of hfile=" + hfilePath + " fits into no region. " + "And because "
            + FAIL_IF_NEED_SPLIT_HFILE + " was set to true, we just skip the next steps.");
      }
      int lastKeyRegionIdx = getRegionIndex(startEndKeys, last.get());
      int splitIdx = (firstKeyRegionIdx + lastKeyRegionIdx) / 2;
      // make sure the splitPoint is valid in case region overlap occur, maybe the splitPoint bigger
      // than hfile.endkey w/o this check
      if (splitIdx != firstKeyRegionIdx) {
        checkRegionIndexValid(splitIdx, startEndKeys, tableName);
      }
      byte[] splitPoint = startEndKeys.get(splitIdx).getSecond();
      List<LoadQueueItem> lqis = splitStoreFile(conn.getRegionLocator(tableName), item,
        FutureUtils.get(conn.getAdmin().getDescriptor(tableName)), splitPoint);

      return new Pair<>(lqis, null);
    }

    // group regions.
    regionGroups.put(ByteBuffer.wrap(startEndKeys.get(firstKeyRegionIdx).getFirst()), item);
    return null;
  }
Attempt to assign the given load queue item into its target region group. If the hfile boundary
no longer fits into a region, physically splits the hfile such that the new bottom half will
fit and returns the list of LQI's corresponding to the resultant hfiles.
<p/>
protected for testing
@throws IOException if an IO failure is encountered
hbase-server/src/main/java/org/apache/hadoop/hbase/tool/BulkLoadHFilesTool.java