This is an automated email from the ASF dual-hosted git repository.
junegunn pushed a commit to branch branch-2.5
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/branch-2.5 by this push:
new 0a32fbcd9b5 HBASE-29896 Raw scan incorrectly skips cells expired by
cell-level TTL (#7749)
0a32fbcd9b5 is described below
commit 0a32fbcd9b51a1e358b02b7354530bc33074c7de
Author: Junegunn Choi <[email protected]>
AuthorDate: Sat Feb 14 20:40:52 2026 +0900
HBASE-29896 Raw scan incorrectly skips cells expired by cell-level TTL
(#7749)
Signed-off-by: Duo Zhang <[email protected]>
Reviewed-by: Liu Xiao <[email protected]>
---
.../querymatcher/RawScanQueryMatcher.java | 2 +-
.../querymatcher/ScanQueryMatcher.java | 22 ++++++++++++++++++----
.../hbase/client/TestScannersFromClientSide.java | 19 +++++++++++++++++++
3 files changed, 38 insertions(+), 5 deletions(-)
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/RawScanQueryMatcher.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/RawScanQueryMatcher.java
index 180d2dd2ed3..b2a3e0368e1 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/RawScanQueryMatcher.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/RawScanQueryMatcher.java
@@ -39,7 +39,7 @@ public abstract class RawScanQueryMatcher extends
UserScanQueryMatcher {
if (filter != null && filter.filterAllRemaining()) {
return MatchCode.DONE_SCAN;
}
- MatchCode returnCode = preCheck(cell);
+ MatchCode returnCode = preCheckRaw(cell);
if (returnCode != null) {
return returnCode;
}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/ScanQueryMatcher.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/ScanQueryMatcher.java
index 2ab3d68fca1..a44baf2832e 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/ScanQueryMatcher.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/ScanQueryMatcher.java
@@ -168,6 +168,24 @@ public abstract class ScanQueryMatcher implements
ShipperListener {
* @return null means continue.
*/
protected final MatchCode preCheck(Cell cell) {
+ final MatchCode code = preCheckRaw(cell);
+ if (code != null) {
+ return code;
+ }
+
+ // check if the cell is expired by cell TTL
+ if (isCellTTLExpired(cell, this.oldestUnexpiredTS, this.now)) {
+ return MatchCode.SKIP;
+ }
+
+ return null;
+ }
+
+ /**
+ * preCheck for raw scan. This should not skip expired cells.
+ * @return null means continue.
+ */
+ protected final MatchCode preCheckRaw(Cell cell) {
if (currentRow == null) {
// Since the curCell is null it means we are already sure that we have
moved over to the next
// row
@@ -189,10 +207,6 @@ public abstract class ScanQueryMatcher implements
ShipperListener {
if (timestamp == HConstants.OLDEST_TIMESTAMP || columns.isDone(timestamp))
{
return columns.getNextRowOrNextColumn(cell);
}
- // check if the cell is expired by cell TTL
- if (isCellTTLExpired(cell, this.oldestUnexpiredTS, this.now)) {
- return MatchCode.SKIP;
- }
return null;
}
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestScannersFromClientSide.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestScannersFromClientSide.java
index ba7734c24d0..c151020bf63 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestScannersFromClientSide.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestScannersFromClientSide.java
@@ -641,6 +641,25 @@ public class TestScannersFromClientSide {
verifyResult(result, kvListExp, toLog, "Testing offset + multiple CFs +
maxResults");
}
+ @Test
+ public void testRawScanExpiredCell() throws Exception {
+ final TableName tableName = name.getTableName();
+ try (final Table table = TEST_UTIL.createTable(tableName, FAMILY)) {
+ final Put put = new Put(ROW);
+ put.addColumn(FAMILY, QUALIFIER, VALUE);
+ put.setTTL(0);
+ table.put(put);
+ final Scan scan = new Scan().setRaw(true);
+ try (final ResultScanner scanner = table.getScanner(scan)) {
+ final Result result = scanner.next();
+ assertArrayEquals(VALUE, result.getValue(FAMILY, QUALIFIER));
+ assertNull(scanner.next());
+ }
+ } finally {
+ TEST_UTIL.deleteTable(tableName);
+ }
+ }
+
@Test
public void testScanRawDeleteFamilyVersion() throws Exception {
TableName tableName = name.getTableName();