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 c164afd..06d2a0e 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 @@ -172,7 +172,7 @@ public abstract class ScanQueryMatcher implements ShipperListener { * Check before the delete logic. * @return null means continue. */ - protected final MatchCode preCheck(Cell cell) { + protected MatchCode preCheck(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 diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/UserScanQueryMatcher.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/UserScanQueryMatcher.java index 7501594..1fb74da 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/UserScanQueryMatcher.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/UserScanQueryMatcher.java @@ -276,4 +276,21 @@ public abstract class UserScanQueryMatcher extends ScanQueryMatcher { hasNullColumn, oldestUnexpiredTS, now); } } + + @Override + protected final MatchCode preCheck(Cell cell) { + MatchCode returnCode = super.preCheck(cell); + if (returnCode != null) { + return returnCode; + } + long timestamp = cell.getTimestamp(); + int tsCmp = tr.compare(timestamp); + if (tsCmp > 0) { + return MatchCode.SKIP; + } + if (tsCmp < 0) { + return columns.getNextRowOrNextColumn(cell); + } + return null; + } } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/querymatcher/TestUserScanQueryMatcher.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/querymatcher/TestUserScanQueryMatcher.java index aec93cb..b51e695 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/querymatcher/TestUserScanQueryMatcher.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/querymatcher/TestUserScanQueryMatcher.java @@ -26,9 +26,10 @@ import java.util.List; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.HConstants; -import org.apache.hadoop.hbase.PrivateCellUtil; import org.apache.hadoop.hbase.KeepDeletedCells; import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.PrivateCellUtil; +import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.regionserver.ScanInfo; import org.apache.hadoop.hbase.regionserver.querymatcher.ScanQueryMatcher.MatchCode; import org.apache.hadoop.hbase.testclassification.RegionServerTests; @@ -236,4 +237,52 @@ public class TestUserScanQueryMatcher extends AbstractTestScanQueryMatcher { assertEquals(expected[i], actual.get(i)); } } + + /** + * Verify that {@link ScanQueryMatcher} doesn't eclipse the cells with deleted cell which are out + * of time range in the scan. + * @throws IOException + */ + @Test + public void testMatch_timeRangeScan() throws IOException { + MatchCode[] expected = + new MatchCode[] { ScanQueryMatcher.MatchCode.SKIP, ScanQueryMatcher.MatchCode.INCLUDE, + ScanQueryMatcher.MatchCode.SKIP, ScanQueryMatcher.MatchCode.INCLUDE, + ScanQueryMatcher.MatchCode.SKIP, ScanQueryMatcher.MatchCode.INCLUDE, + ScanQueryMatcher.MatchCode.INCLUDE, ScanQueryMatcher.MatchCode.INCLUDE, + ScanQueryMatcher.MatchCode.SKIP, ScanQueryMatcher.MatchCode.DONE }; + + long now = EnvironmentEdgeManager.currentTime(); + Scan timeRangeScan=new Scan(scan); + timeRangeScan.setTimeRange(0, now); + UserScanQueryMatcher qm = UserScanQueryMatcher.create(timeRangeScan, new ScanInfo(this.conf, fam2, 0, 1, + ttl, KeepDeletedCells.FALSE, HConstants.DEFAULT_BLOCKSIZE, 0, rowComparator, false), + null, 0 , now, null); + + KeyValue[] kvs = + new KeyValue[] { new KeyValue(row1, fam2, col1, now + 10, KeyValue.Type.DeleteFamily), + new KeyValue(row1, fam2, col1, now - 100, data), + new KeyValue(row1, fam2, col2, now + 10, data), + new KeyValue(row1, fam2, col2, now - 50, data), + new KeyValue(row1, fam2, col2, now + 50, data), + new KeyValue(row1, fam2, col3, now - 5000, data), + new KeyValue(row1, fam2, col4, now - 500, data), + new KeyValue(row1, fam2, col5, now - 10000, data), + new KeyValue(row2, fam1, col1, now + 10, KeyValue.Type.Delete), + new KeyValue(row2, fam1, col1, now - 10, data)}; + KeyValue k = kvs[0]; + qm.setToNewRow(k); + + List actual = new ArrayList<>(kvs.length); + for (KeyValue kv : kvs) { + actual.add(qm.match(kv)); + } + + assertEquals(expected.length, actual.size()); + for (int i = 0; i < expected.length; i++) { + LOG.debug("expected " + expected[i] + ", actual " + actual.get(i)); + assertEquals("For KV["+i+"]",expected[i], actual.get(i)); + } + } + }