1717 */
1818package org .apache .hadoop .hbase .client ;
1919
20+ import static org .junit .Assert .assertEquals ;
21+ import static org .junit .Assert .assertFalse ;
2022import static org .junit .Assert .assertNotNull ;
2123import static org .junit .Assert .assertNull ;
2224import static org .junit .Assert .assertTrue ;
25+ import static org .mockito .ArgumentMatchers .anyList ;
26+ import static org .mockito .Mockito .spy ;
27+ import static org .mockito .Mockito .times ;
28+ import static org .mockito .Mockito .verify ;
2329
2430import java .io .IOException ;
31+ import java .util .Arrays ;
2532import org .apache .hadoop .conf .Configuration ;
2633import org .apache .hadoop .fs .FileSystem ;
2734import org .apache .hadoop .fs .Path ;
35+ import org .apache .hadoop .hbase .Cell ;
2836import org .apache .hadoop .hbase .HBaseClassTestRule ;
2937import org .apache .hadoop .hbase .HBaseTestingUtility ;
3038import org .apache .hadoop .hbase .HConstants ;
3139import org .apache .hadoop .hbase .TableName ;
40+ import org .apache .hadoop .hbase .filter .FilterBase ;
3241import org .apache .hadoop .hbase .io .hfile .BlockCache ;
3342import org .apache .hadoop .hbase .io .hfile .IndexOnlyLruBlockCache ;
43+ import org .apache .hadoop .hbase .regionserver .RegionScanner ;
44+ import org .apache .hadoop .hbase .regionserver .StoreScanner ;
3445import org .apache .hadoop .hbase .testclassification .ClientTests ;
3546import org .apache .hadoop .hbase .testclassification .SmallTests ;
47+ import org .apache .hadoop .hbase .util .Bytes ;
3648import org .junit .AfterClass ;
3749import org .junit .Before ;
3850import org .junit .BeforeClass ;
@@ -47,6 +59,8 @@ public class TestClientSideRegionScanner {
4759 HBaseClassTestRule .forClass (TestClientSideRegionScanner .class );
4860
4961 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility ();
62+ private static final TableName TABLE_NAME = TableName .valueOf ("test" );
63+ private static final byte [] FAM_NAME = Bytes .toBytes ("f" );
5064
5165 private Configuration conf ;
5266 private Path rootDir ;
@@ -113,4 +127,78 @@ public void testNoBlockCache() throws IOException {
113127 BlockCache blockCache = clientSideRegionScanner .getRegion ().getBlockCache ();
114128 assertNull (blockCache );
115129 }
130+
131+ @ Test
132+ public void testContinuesToScanIfHasMore () throws IOException {
133+ // Conditions for this test to set up RegionScannerImpl to bail on the scan
134+ // after a single iteration
135+ // 1. Configure preadMaxBytes to something small to trigger scannerContext#returnImmediately
136+ // 2. Configure a filter to filter out some rows, in this case rows with values < 5
137+ // 3. Configure the filter's hasFilterRow to return true so RegionScannerImpl sets
138+ // the limitScope to something with a depth of 0, so we bail on the scan after the first
139+ // iteration
140+
141+ Configuration copyConf = new Configuration (conf );
142+ copyConf .setLong (StoreScanner .STORESCANNER_PREAD_MAX_BYTES , 1 );
143+ Scan scan = new Scan ();
144+ scan .setFilter (new FiltersRowsLessThan5 ());
145+ scan .setLimit (1 );
146+
147+ try (Table table = TEST_UTIL .createTable (TABLE_NAME , FAM_NAME )) {
148+ TableDescriptor htd = TEST_UTIL .getAdmin ().getDescriptor (TABLE_NAME );
149+ RegionInfo hri = TEST_UTIL .getAdmin ().getRegions (TABLE_NAME ).get (0 );
150+
151+ for (int i = 0 ; i < 10 ; ++i ) {
152+ table .put (createPut (i ));
153+ }
154+
155+ // Flush contents to disk so we can scan the fs
156+ TEST_UTIL .getAdmin ().flush (TABLE_NAME );
157+
158+ ClientSideRegionScanner clientSideRegionScanner =
159+ new ClientSideRegionScanner (copyConf , fs , rootDir , htd , hri , scan , null );
160+ RegionScanner scannerSpy = spy (clientSideRegionScanner .scanner );
161+ clientSideRegionScanner .scanner = scannerSpy ;
162+ Result result = clientSideRegionScanner .next ();
163+
164+ verify (scannerSpy , times (6 )).nextRaw (anyList ());
165+ assertNotNull (result );
166+ assertEquals (Bytes .toInt (result .getRow ()), 5 );
167+ assertTrue (clientSideRegionScanner .hasMore );
168+
169+ for (int i = 6 ; i < 10 ; ++i ) {
170+ result = clientSideRegionScanner .next ();
171+ verify (scannerSpy , times (i + 1 )).nextRaw (anyList ());
172+ assertNotNull (result );
173+ assertEquals (Bytes .toInt (result .getRow ()), i );
174+ }
175+
176+ result = clientSideRegionScanner .next ();
177+ assertNull (result );
178+ assertFalse (clientSideRegionScanner .hasMore );
179+ }
180+ }
181+
182+ private static Put createPut (int rowAsInt ) {
183+ byte [] row = Bytes .toBytes (rowAsInt );
184+ Put put = new Put (row );
185+ put .addColumn (FAM_NAME , row , row );
186+ return put ;
187+ }
188+
189+ private static class FiltersRowsLessThan5 extends FilterBase {
190+
191+ @ Override
192+ public boolean filterRowKey (Cell cell ) {
193+ byte [] rowKey = Arrays .copyOfRange (cell .getRowArray (), cell .getRowOffset (),
194+ cell .getRowLength () + cell .getRowOffset ());
195+ int intValue = Bytes .toInt (rowKey );
196+ return intValue < 5 ;
197+ }
198+
199+ @ Override
200+ public boolean hasFilterRow () {
201+ return true ;
202+ }
203+ }
116204}
0 commit comments