This is an automated email from the ASF dual-hosted git repository.
junegunn pushed a commit to branch branch-3
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/branch-3 by this push:
new bea1c73cf37 HBASE-29896 Raw scan incorrectly skips cells expired by
cell-level TTL (#7749)
bea1c73cf37 is described below
commit bea1c73cf375f7709d449702aac7780d6863f7c5
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 dcffbb140ed..c9884b70701 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 dc3259f03d3..587b784a7d8 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
@@ -170,6 +170,24 @@ public abstract class ScanQueryMatcher implements
ShipperListener {
* @return null means continue.
*/
protected final MatchCode preCheck(ExtendedCell 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(ExtendedCell cell) {
if (currentRow == null) {
// Since the curCell is null it means we are already sure that we have
moved over to the next
// row
@@ -191,10 +209,6 @@ public abstract class ScanQueryMatcher implements
ShipperListener {
if (timestamp == PrivateConstants.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 308481586e3..99439a48229 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
@@ -625,6 +625,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();