Bug: Reverse scan over rows with tags throw exceptions when using DataBlockEncoding
Status: Resolved | Priority: Major | Resolution: Fixed
Created: 19/Jan/23 16:07

This is easily reproducible, see test below. All you need to do is create a table with a DBE, write some puts with setTTL, then do a reverse scan. All 3 compressing DBE's fail. 

PREFIX throws an exception:
{code:java}
Caused by: java.lang.IndexOutOfBoundsException: index (0) must be less than size (0)
    at org.apache.hbase.thirdparty.com.google.common.base.Preconditions.checkElementIndex(Preconditions.java:1355)
    at org.apache.hbase.thirdparty.com.google.common.base.Preconditions.checkElementIndex(Preconditions.java:1337)
    at org.apache.hadoop.hbase.io.util.LRUDictionary$BidirectionalLRUMap.get(LRUDictionary.java:153)
    at org.apache.hadoop.hbase.io.util.LRUDictionary$BidirectionalLRUMap.access$000(LRUDictionary.java:79)
    at org.apache.hadoop.hbase.io.util.LRUDictionary.getEntry(LRUDictionary.java:43)
    at org.apache.hadoop.hbase.io.TagCompressionContext.uncompressTags(TagCompressionContext.java:152)
    at org.apache.hadoop.hbase.io.encoding.BufferedDataBlockEncoder$BufferedEncodedSeeker.decodeTags(BufferedDataBlockEncoder.java:819)
    at org.apache.hadoop.hbase.io.encoding.PrefixKeyDeltaEncoder$SeekerStateBufferedEncodedSeeker.decodeNext(PrefixKeyDeltaEncoder.java:209)
    at org.apache.hadoop.hbase.io.encoding.BufferedDataBlockEncoder$BufferedEncodedSeeker.seekToKeyInBlock(BufferedDataBlockEncoder.java:920) {code}
 

DIFF throws an exception:
{code:java}
Caused by: java.lang.ArrayIndexOutOfBoundsException: arraycopy: length -22 is negative
    at java.base/java.lang.System.arraycopy(Native Method)
    at org.apache.hadoop.hbase.util.ByteBufferUtils.copyFromBufferToArray(ByteBufferUtils.java:1140)
    at org.apache.hadoop.hbase.nio.SingleByteBuff.get(SingleByteBuff.java:213)
    at org.apache.hadoop.hbase.io.encoding.DiffKeyDeltaEncoder$DiffSeekerStateBufferedEncodedSeeker.decode(DiffKeyDeltaEncoder.java:431)
    at org.apache.hadoop.hbase.io.encoding.DiffKeyDeltaEncoder$DiffSeekerStateBufferedEncodedSeeker.decodeNext(DiffKeyDeltaEncoder.java:502)
    at org.apache.hadoop.hbase.io.encoding.BufferedDataBlockEncoder$BufferedEncodedSeeker.seekToKeyInBlock(BufferedDataBlockEncoder.java:920) {code}
 

FAST_DIFF throws a similar exception:
{code:java}
Caused by: java.lang.ArrayIndexOutOfBoundsException: arraycopy: length -22 is negative
    at java.base/java.lang.System.arraycopy(Native Method)
    at org.apache.hadoop.hbase.util.ByteBufferUtils.copyFromBufferToArray(ByteBufferUtils.java:1140)
    at org.apache.hadoop.hbase.nio.SingleByteBuff.get(SingleByteBuff.java:213)
    at org.apache.hadoop.hbase.io.encoding.FastDiffDeltaEncoder$FastDiffSeekerStateBufferedEncodedSeeker.decode(FastDiffDeltaEncoder.java:424)
    at org.apache.hadoop.hbase.io.encoding.FastDiffDeltaEncoder$FastDiffSeekerStateBufferedEncodedSeeker.decodeNext(FastDiffDeltaEncoder.java:490)
    at org.apache.hadoop.hbase.io.encoding.BufferedDataBlockEncoder$BufferedEncodedSeeker.seekToKeyInBlock(BufferedDataBlockEncoder.java:920) {code}
 

Reproduce with:
{code:java}
private static final Logger LOG = LoggerFactory.getLogger(TestTags.class);
@Test
public void testReverseScanWithDBE() throws IOException {
  byte[] family = Bytes.toBytes("0");

  Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
  conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1);

  try (Connection connection = ConnectionFactory.createConnection(conf)) {

    for (DataBlockEncoding encoding : DataBlockEncoding.values()) {
      boolean isCompressing = encoding != DataBlockEncoding.NONE && encoding != DataBlockEncoding.ROW_INDEX_V1;
      try {
        testReverseScanWithDBE(connection, encoding, family);
        if (isCompressing) {
          Assert.fail("Expected to throw exception for DBE " + encoding);
        }
      } catch (Exception e) {
        LOG.info("Got exception for DBE {}", encoding, e);
        assertTrue("Only expected compressing encodings to fail, but failed on " + encoding, isCompressing);
      }
    }
  }
}

private void testReverseScanWithDBE(Connection conn, DataBlockEncoding encoding, byte[] family)
  throws IOException {
  LOG.info("Running test with DBE={}", encoding);
  TableName tableName = TableName.valueOf(TEST_NAME.getMethodName() + "-" + encoding);
  TEST_UTIL.createTable(
    TableDescriptorBuilder.newBuilder(tableName)
      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(family)
        .setDataBlockEncoding(encoding)
        .build())
      .build(),
    null);

  Table table = conn.getTable(tableName);

  for (int i = 0; i < 10; i++) {
    table.put(new Put(Bytes.toBytes(i)).addColumn(family, Bytes.toBytes(0), new byte[10]).setTTL(600_000));
  }

  TEST_UTIL.flush(table.getName());

  Scan scan = new Scan();
  scan.setReversed(true);

  try (ResultScanner scanner = table.getScanner(scan)) {
    // should fail for compressing encodings
    scanner.next();
  }
} {code}