2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org
.apache
.hadoop
.hbase
;
20 import static org
.junit
.Assert
.assertArrayEquals
;
21 import static org
.junit
.Assert
.assertEquals
;
22 import static org
.junit
.Assert
.assertFalse
;
23 import static org
.junit
.Assert
.assertNull
;
24 import static org
.junit
.Assert
.assertTrue
;
25 import static org
.junit
.Assert
.fail
;
27 import java
.io
.IOException
;
28 import java
.util
.ArrayList
;
29 import java
.util
.LinkedHashSet
;
30 import java
.util
.List
;
32 import org
.apache
.hadoop
.hbase
.client
.Delete
;
33 import org
.apache
.hadoop
.hbase
.client
.Put
;
34 import org
.apache
.hadoop
.hbase
.client
.RegionInfo
;
35 import org
.apache
.hadoop
.hbase
.client
.Result
;
36 import org
.apache
.hadoop
.hbase
.client
.ResultScanner
;
37 import org
.apache
.hadoop
.hbase
.client
.Scan
;
38 import org
.apache
.hadoop
.hbase
.client
.Table
;
39 import org
.apache
.hadoop
.hbase
.filter
.ColumnPrefixFilter
;
40 import org
.apache
.hadoop
.hbase
.filter
.ColumnRangeFilter
;
41 import org
.apache
.hadoop
.hbase
.filter
.Filter
;
42 import org
.apache
.hadoop
.hbase
.filter
.FirstKeyOnlyFilter
;
43 import org
.apache
.hadoop
.hbase
.filter
.FirstKeyValueMatchingQualifiersFilter
;
44 import org
.apache
.hadoop
.hbase
.filter
.RandomRowFilter
;
45 import org
.apache
.hadoop
.hbase
.testclassification
.LargeTests
;
46 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
47 import org
.apache
.hadoop
.hbase
.util
.ClassSize
;
48 import org
.apache
.hadoop
.hbase
.util
.Pair
;
49 import org
.junit
.AfterClass
;
50 import org
.junit
.BeforeClass
;
51 import org
.junit
.ClassRule
;
52 import org
.junit
.Rule
;
53 import org
.junit
.Test
;
54 import org
.junit
.experimental
.categories
.Category
;
55 import org
.junit
.rules
.TestName
;
56 import org
.slf4j
.Logger
;
57 import org
.slf4j
.LoggerFactory
;
60 * These tests are focused on testing how partial results appear to a client. Partial results are
61 * {@link Result}s that contain only a portion of a row's complete list of cells. Partial results
62 * are formed when the server breaches its maximum result size when trying to service a client's RPC
63 * request. It is the responsibility of the scanner on the client side to recognize when partial
64 * results have been returned and to take action to form the complete results.
66 * Unless the flag {@link Scan#setAllowPartialResults(boolean)} has been set to true, the caller of
67 * {@link ResultScanner#next()} should never see partial results.
69 @Category(LargeTests
.class)
70 public class TestPartialResultsFromClientSide
{
73 public static final HBaseClassTestRule CLASS_RULE
=
74 HBaseClassTestRule
.forClass(TestPartialResultsFromClientSide
.class);
76 private static final Logger LOG
= LoggerFactory
.getLogger(TestPartialResultsFromClientSide
.class);
78 private final static HBaseTestingUtility TEST_UTIL
= new HBaseTestingUtility();
79 private final static int MINICLUSTER_SIZE
= 5;
80 private static Table TABLE
= null;
85 private static TableName TABLE_NAME
= TableName
.valueOf("testTable");
87 private static int NUM_ROWS
= 5;
88 private static byte[] ROW
= Bytes
.toBytes("testRow");
89 private static byte[][] ROWS
= HTestConst
.makeNAscii(ROW
, NUM_ROWS
);
91 // Should keep this value below 10 to keep generation of expected kv's simple. If above 10 then
92 // table/row/cf1/... will be followed by table/row/cf10/... instead of table/row/cf2/... which
93 // breaks the simple generation of expected kv's
94 private static int NUM_FAMILIES
= 10;
95 private static byte[] FAMILY
= Bytes
.toBytes("testFamily");
96 private static byte[][] FAMILIES
= HTestConst
.makeNAscii(FAMILY
, NUM_FAMILIES
);
98 private static int NUM_QUALIFIERS
= 10;
99 private static byte[] QUALIFIER
= Bytes
.toBytes("testQualifier");
100 private static byte[][] QUALIFIERS
= HTestConst
.makeNAscii(QUALIFIER
, NUM_QUALIFIERS
);
102 private static int VALUE_SIZE
= 1024;
103 private static byte[] VALUE
= Bytes
.createMaxByteArray(VALUE_SIZE
);
105 private static int NUM_COLS
= NUM_FAMILIES
* NUM_QUALIFIERS
;
107 // Approximation of how large the heap size of cells in our table. Should be accessed through
108 // getCellHeapSize().
109 private static long CELL_HEAP_SIZE
= -1;
111 private static long timeout
= 10000;
114 public TestName name
= new TestName();
117 public static void setUpBeforeClass() throws Exception
{
118 TEST_UTIL
.getConfiguration().setLong(HConstants
.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD
, timeout
);
119 TEST_UTIL
.startMiniCluster(MINICLUSTER_SIZE
);
120 TEST_UTIL
.getAdmin().balancerSwitch(false, true);
121 TABLE
= createTestTable(TABLE_NAME
, ROWS
, FAMILIES
, QUALIFIERS
, VALUE
);
124 static Table
createTestTable(TableName name
, byte[][] rows
, byte[][] families
,
125 byte[][] qualifiers
, byte[] cellValue
) throws IOException
{
126 Table ht
= TEST_UTIL
.createTable(name
, families
);
127 List
<Put
> puts
= createPuts(rows
, families
, qualifiers
, cellValue
);
134 public static void tearDownAfterClass() throws Exception
{
135 TEST_UTIL
.shutdownMiniCluster();
139 * Ensure that the expected key values appear in a result returned from a scanner that is
140 * combining partial results into complete results
144 public void testExpectedValuesOfPartialResults() throws Exception
{
145 testExpectedValuesOfPartialResults(false);
146 testExpectedValuesOfPartialResults(true);
149 public void testExpectedValuesOfPartialResults(boolean reversed
) throws Exception
{
150 Scan partialScan
= new Scan();
151 partialScan
.setMaxVersions();
152 // Max result size of 1 ensures that each RPC request will return a single cell. The scanner
153 // will need to reconstruct the results into a complete result before returning to the caller
154 partialScan
.setMaxResultSize(1);
155 partialScan
.setReversed(reversed
);
156 ResultScanner partialScanner
= TABLE
.getScanner(partialScan
);
158 final int startRow
= reversed ? ROWS
.length
- 1 : 0;
159 final int endRow
= reversed ?
-1 : ROWS
.length
;
160 final int loopDelta
= reversed ?
-1 : 1;
163 for (int row
= startRow
; row
!= endRow
; row
= row
+ loopDelta
) {
164 message
= "Ensuring the expected keyValues are present for row " + row
;
165 List
<Cell
> expectedKeyValues
= createKeyValuesForRow(ROWS
[row
], FAMILIES
, QUALIFIERS
, VALUE
);
166 Result result
= partialScanner
.next();
167 assertFalse(result
.mayHaveMoreCellsInRow());
168 verifyResult(result
, expectedKeyValues
, message
);
171 partialScanner
.close();
175 * Ensure that we only see Results marked as partial when the allowPartial flag is set
179 public void testAllowPartialResults() throws Exception
{
180 Scan scan
= new Scan();
181 scan
.setAllowPartialResults(true);
182 scan
.setMaxResultSize(1);
183 ResultScanner scanner
= TABLE
.getScanner(scan
);
184 Result result
= scanner
.next();
186 assertTrue(result
!= null);
187 assertTrue(result
.mayHaveMoreCellsInRow());
188 assertTrue(result
.rawCells() != null);
189 assertTrue(result
.rawCells().length
== 1);
193 scan
.setAllowPartialResults(false);
194 scanner
= TABLE
.getScanner(scan
);
195 result
= scanner
.next();
197 assertTrue(result
!= null);
198 assertTrue(!result
.mayHaveMoreCellsInRow());
199 assertTrue(result
.rawCells() != null);
200 assertTrue(result
.rawCells().length
== NUM_COLS
);
206 * Ensure that the results returned from a scanner that retrieves all results in a single RPC call
207 * matches the results that are returned from a scanner that must incrementally combine partial
208 * results into complete results. A variety of scan configurations can be tested
212 public void testEquivalenceOfScanResults() throws Exception
{
213 Scan oneShotScan
= new Scan();
214 oneShotScan
.setMaxResultSize(Long
.MAX_VALUE
);
216 Scan partialScan
= new Scan(oneShotScan
);
217 partialScan
.setMaxResultSize(1);
219 testEquivalenceOfScanResults(TABLE
, oneShotScan
, partialScan
);
222 public void testEquivalenceOfScanResults(Table table
, Scan scan1
, Scan scan2
) throws Exception
{
223 ResultScanner scanner1
= table
.getScanner(scan1
);
224 ResultScanner scanner2
= table
.getScanner(scan2
);
230 while ((r1
= scanner1
.next()) != null) {
231 r2
= scanner2
.next();
233 assertTrue(r2
!= null);
234 compareResults(r1
, r2
, "Comparing result #" + count
);
238 r2
= scanner2
.next();
239 assertTrue("r2: " + r2
+ " Should be null", r2
== null);
246 * Order of cells in partial results matches the ordering of cells from complete results
250 public void testOrderingOfCellsInPartialResults() throws Exception
{
251 Scan scan
= new Scan();
253 for (int col
= 1; col
<= NUM_COLS
; col
++) {
254 scan
.setMaxResultSize(getResultSizeForNumberOfCells(col
));
255 testOrderingOfCellsInPartialResults(scan
);
257 // Test again with a reversed scanner
258 scan
.setReversed(true);
259 testOrderingOfCellsInPartialResults(scan
);
263 public void testOrderingOfCellsInPartialResults(final Scan basePartialScan
) throws Exception
{
264 // Scan that retrieves results in pieces (partials). By setting allowPartialResults to be true
265 // the results will NOT be reconstructed and instead the caller will see the partial results
266 // returned by the server
267 Scan partialScan
= new Scan(basePartialScan
);
268 partialScan
.setAllowPartialResults(true);
269 ResultScanner partialScanner
= TABLE
.getScanner(partialScan
);
271 // Scan that retrieves all table results in single RPC request
272 Scan oneShotScan
= new Scan(basePartialScan
);
273 oneShotScan
.setMaxResultSize(Long
.MAX_VALUE
);
274 oneShotScan
.setCaching(ROWS
.length
);
275 ResultScanner oneShotScanner
= TABLE
.getScanner(oneShotScan
);
277 Result oneShotResult
= oneShotScanner
.next();
278 Result partialResult
= null;
279 int iterationCount
= 0;
281 while (oneShotResult
!= null && oneShotResult
.rawCells() != null) {
282 List
<Cell
> aggregatePartialCells
= new ArrayList
<>();
284 partialResult
= partialScanner
.next();
285 assertTrue("Partial Result is null. iteration: " + iterationCount
, partialResult
!= null);
286 assertTrue("Partial cells are null. iteration: " + iterationCount
,
287 partialResult
.rawCells() != null);
289 for (Cell c
: partialResult
.rawCells()) {
290 aggregatePartialCells
.add(c
);
292 } while (partialResult
.mayHaveMoreCellsInRow());
294 assertTrue("Number of cells differs. iteration: " + iterationCount
,
295 oneShotResult
.rawCells().length
== aggregatePartialCells
.size());
296 final Cell
[] oneShotCells
= oneShotResult
.rawCells();
297 for (int cell
= 0; cell
< oneShotCells
.length
; cell
++) {
298 Cell oneShotCell
= oneShotCells
[cell
];
299 Cell partialCell
= aggregatePartialCells
.get(cell
);
301 assertTrue("One shot cell was null", oneShotCell
!= null);
302 assertTrue("Partial cell was null", partialCell
!= null);
303 assertTrue("Cell differs. oneShotCell:" + oneShotCell
+ " partialCell:" + partialCell
,
304 oneShotCell
.equals(partialCell
));
307 oneShotResult
= oneShotScanner
.next();
311 assertTrue(partialScanner
.next() == null);
313 partialScanner
.close();
314 oneShotScanner
.close();
318 * Setting the max result size allows us to control how many cells we expect to see on each call
319 * to next on the scanner. Test a variety of different sizes for correctness
323 public void testExpectedNumberOfCellsPerPartialResult() throws Exception
{
324 Scan scan
= new Scan();
325 testExpectedNumberOfCellsPerPartialResult(scan
);
327 scan
.setReversed(true);
328 testExpectedNumberOfCellsPerPartialResult(scan
);
331 public void testExpectedNumberOfCellsPerPartialResult(Scan baseScan
) throws Exception
{
332 for (int expectedCells
= 1; expectedCells
<= NUM_COLS
; expectedCells
++) {
333 testExpectedNumberOfCellsPerPartialResult(baseScan
, expectedCells
);
337 public void testExpectedNumberOfCellsPerPartialResult(Scan baseScan
, int expectedNumberOfCells
)
340 if (LOG
.isInfoEnabled()) LOG
.info("groupSize:" + expectedNumberOfCells
);
342 // Use the cellHeapSize to set maxResultSize such that we know how many cells to expect back
343 // from the call. The returned results should NOT exceed expectedNumberOfCells but may be less
344 // than it in cases where expectedNumberOfCells is not an exact multiple of the number of
345 // columns in the table.
346 Scan scan
= new Scan(baseScan
);
347 scan
.setAllowPartialResults(true);
348 scan
.setMaxResultSize(getResultSizeForNumberOfCells(expectedNumberOfCells
));
350 ResultScanner scanner
= TABLE
.getScanner(scan
);
351 Result result
= null;
352 byte[] prevRow
= null;
353 while ((result
= scanner
.next()) != null) {
354 assertTrue(result
.rawCells() != null);
356 // Cases when cell count won't equal expectedNumberOfCells:
357 // 1. Returned result is the final result needed to form the complete result for that row
358 // 2. It is the first result we have seen for that row and thus may have been fetched as
359 // the last group of cells that fit inside the maxResultSize
361 "Result's cell count differed from expected number. result: " + result
,
362 result
.rawCells().length
== expectedNumberOfCells
|| !result
.mayHaveMoreCellsInRow()
363 || !Bytes
.equals(prevRow
, result
.getRow()));
364 prevRow
= result
.getRow();
371 * @return The approximate heap size of a cell in the test table. All cells should have
372 * approximately the same heap size, so the value is cached to avoid repeating the
376 private long getCellHeapSize() throws Exception
{
377 if (CELL_HEAP_SIZE
== -1) {
378 // Do a partial scan that will return a single result with a single cell
379 Scan scan
= new Scan();
380 scan
.setMaxResultSize(2);
381 scan
.setAllowPartialResults(true);
382 ResultScanner scanner
= TABLE
.getScanner(scan
);
384 Result result
= scanner
.next();
386 assertTrue(result
!= null);
387 assertTrue(result
.rawCells() != null);
388 assertTrue(result
.rawCells().length
== 1);
390 // Estimate the cell heap size. One difference is that on server side, the KV Heap size is
391 // estimated differently in case the cell is backed up by MSLAB byte[] (no overhead for
392 // backing array). Thus below calculation is a bit brittle.
393 CELL_HEAP_SIZE
= result
.rawCells()[0].heapSize() - (ClassSize
.ARRAY
+ 3);
394 if (LOG
.isInfoEnabled()) LOG
.info("Cell heap size: " + CELL_HEAP_SIZE
);
398 return CELL_HEAP_SIZE
;
402 * @param numberOfCells
403 * @return the result size that should be used in {@link Scan#setMaxResultSize(long)} if you want
404 * the server to return exactly numberOfCells cells
407 private long getResultSizeForNumberOfCells(int numberOfCells
) throws Exception
{
408 return getCellHeapSize() * numberOfCells
;
412 * Test various combinations of batching and partial results for correctness
415 public void testPartialResultsAndBatch() throws Exception
{
416 for (int batch
= 1; batch
<= NUM_COLS
/ 4; batch
++) {
417 for (int cellsPerPartial
= 1; cellsPerPartial
<= NUM_COLS
/ 4; cellsPerPartial
++) {
418 testPartialResultsAndBatch(batch
, cellsPerPartial
);
423 public void testPartialResultsAndBatch(final int batch
, final int cellsPerPartialResult
)
425 if (LOG
.isInfoEnabled()) {
426 LOG
.info("batch: " + batch
+ " cellsPerPartialResult: " + cellsPerPartialResult
);
429 Scan scan
= new Scan();
430 scan
.setMaxResultSize(getResultSizeForNumberOfCells(cellsPerPartialResult
));
431 scan
.setBatch(batch
);
432 ResultScanner scanner
= TABLE
.getScanner(scan
);
433 Result result
= scanner
.next();
436 while ((result
= scanner
.next()) != null) {
437 assertTrue(result
.rawCells() != null);
439 if (result
.mayHaveMoreCellsInRow()) {
441 "Cells:" + result
.rawCells().length
+ " Batch size:" + batch
442 + " cellsPerPartialResult:" + cellsPerPartialResult
+ " rep:" + repCount
;
443 assertTrue(error
, result
.rawCells().length
== batch
);
445 assertTrue(result
.rawCells().length
<= batch
);
454 * Test the method {@link Result#createCompleteResult(Iterable)}
458 public void testPartialResultsReassembly() throws Exception
{
459 Scan scan
= new Scan();
460 testPartialResultsReassembly(scan
);
461 scan
.setReversed(true);
462 testPartialResultsReassembly(scan
);
465 public void testPartialResultsReassembly(Scan scanBase
) throws Exception
{
466 Scan partialScan
= new Scan(scanBase
);
467 partialScan
.setMaxResultSize(1);
468 partialScan
.setAllowPartialResults(true);
469 ResultScanner partialScanner
= TABLE
.getScanner(partialScan
);
471 Scan oneShotScan
= new Scan(scanBase
);
472 oneShotScan
.setMaxResultSize(Long
.MAX_VALUE
);
473 ResultScanner oneShotScanner
= TABLE
.getScanner(oneShotScan
);
475 ArrayList
<Result
> partials
= new ArrayList
<>();
476 for (int i
= 0; i
< NUM_ROWS
; i
++) {
477 Result partialResult
= null;
478 Result completeResult
= null;
479 Result oneShotResult
= null;
483 partialResult
= partialScanner
.next();
484 partials
.add(partialResult
);
485 } while (partialResult
!= null && partialResult
.mayHaveMoreCellsInRow());
487 completeResult
= Result
.createCompleteResult(partials
);
488 oneShotResult
= oneShotScanner
.next();
490 compareResults(completeResult
, oneShotResult
, null);
493 assertTrue(oneShotScanner
.next() == null);
494 assertTrue(partialScanner
.next() == null);
496 oneShotScanner
.close();
497 partialScanner
.close();
501 * When reconstructing the complete result from its partials we ensure that the row of each
502 * partial result is the same. If one of the rows differs, an exception is thrown.
505 public void testExceptionThrownOnMismatchedPartialResults() throws IOException
{
506 assertTrue(NUM_ROWS
>= 2);
508 ArrayList
<Result
> partials
= new ArrayList
<>();
509 Scan scan
= new Scan();
510 scan
.setMaxResultSize(Long
.MAX_VALUE
);
511 ResultScanner scanner
= TABLE
.getScanner(scan
);
512 Result r1
= scanner
.next();
514 Result r2
= scanner
.next();
517 assertFalse(Bytes
.equals(r1
.getRow(), r2
.getRow()));
520 Result
.createCompleteResult(partials
);
521 fail("r1 and r2 are from different rows. It should not be possible to combine them into"
522 + " a single result");
523 } catch (IOException e
) {
530 * When a scan has a filter where {@link org.apache.hadoop.hbase.filter.Filter#hasFilterRow()} is
531 * true, the scanner should not return partial results. The scanner cannot return partial results
532 * because the entire row needs to be read for the include/exclude decision to be made
535 public void testNoPartialResultsWhenRowFilterPresent() throws Exception
{
536 Scan scan
= new Scan();
537 scan
.setMaxResultSize(1);
538 scan
.setAllowPartialResults(true);
539 // If a filter hasFilter() is true then partial results should not be returned else filter
540 // application server side would break.
541 scan
.setFilter(new RandomRowFilter(1.0f
));
542 ResultScanner scanner
= TABLE
.getScanner(scan
);
545 while ((r
= scanner
.next()) != null) {
546 assertFalse(r
.mayHaveMoreCellsInRow());
553 * Examine the interaction between the maxResultSize and caching. If the caching limit is reached
554 * before the maxResultSize limit, we should not see partial results. On the other hand, if the
555 * maxResultSize limit is reached before the caching limit, it is likely that partial results will
560 public void testPartialResultsAndCaching() throws Exception
{
561 for (int caching
= 1; caching
<= NUM_ROWS
; caching
++) {
562 for (int maxResultRows
= 0; maxResultRows
<= NUM_ROWS
; maxResultRows
++) {
563 testPartialResultsAndCaching(maxResultRows
, caching
);
569 * @param resultSizeRowLimit The row limit that will be enforced through maxResultSize
570 * @param cachingRowLimit The row limit that will be enforced through caching
572 public void testPartialResultsAndCaching(int resultSizeRowLimit
, int cachingRowLimit
)
574 Scan scan
= new Scan();
575 scan
.setAllowPartialResults(true);
577 // The number of cells specified in the call to getResultSizeForNumberOfCells is offset to
578 // ensure that the result size we specify is not an exact multiple of the number of cells
579 // in a row. This ensures that partial results will be returned when the result size limit
580 // is reached before the caching limit.
581 int cellOffset
= NUM_COLS
/ 3;
582 long maxResultSize
= getResultSizeForNumberOfCells(resultSizeRowLimit
* NUM_COLS
+ cellOffset
);
583 scan
.setMaxResultSize(maxResultSize
);
584 scan
.setCaching(cachingRowLimit
);
586 try (ResultScanner scanner
= TABLE
.getScanner(scan
)) {
588 // Approximate the number of rows we expect will fit into the specified max rsult size. If
589 // this approximation is less than caching, then we expect that the max result size limit will
590 // be hit before the caching limit and thus partial results may be seen
591 boolean expectToSeePartialResults
= resultSizeRowLimit
< cachingRowLimit
;
592 while ((r
= scanner
.next()) != null) {
593 assertTrue(!r
.mayHaveMoreCellsInRow() || expectToSeePartialResults
);
599 * Make puts to put the input value into each combination of row, family, and qualifier
600 * @param rows the rows to use
601 * @param families the families to use
602 * @param qualifiers the qualifiers to use
603 * @param value the values to use
604 * @return the dot product of the given rows, families, qualifiers, and values
605 * @throws IOException if there is a problem creating one of the Put objects
607 static ArrayList
<Put
> createPuts(byte[][] rows
, byte[][] families
, byte[][] qualifiers
,
608 byte[] value
) throws IOException
{
610 ArrayList
<Put
> puts
= new ArrayList
<>();
612 for (int row
= 0; row
< rows
.length
; row
++) {
613 put
= new Put(rows
[row
]);
614 for (int fam
= 0; fam
< families
.length
; fam
++) {
615 for (int qual
= 0; qual
< qualifiers
.length
; qual
++) {
616 KeyValue kv
= new KeyValue(rows
[row
], families
[fam
], qualifiers
[qual
], qual
, value
);
627 * Make key values to represent each possible combination of family and qualifier in the specified
629 * @param row the row to use
630 * @param families the families to use
631 * @param qualifiers the qualifiers to use
632 * @param value the values to use
633 * @return the dot product of the given families, qualifiers, and values for a given row
635 static ArrayList
<Cell
> createKeyValuesForRow(byte[] row
, byte[][] families
, byte[][] qualifiers
,
637 ArrayList
<Cell
> outList
= new ArrayList
<>();
638 for (int fam
= 0; fam
< families
.length
; fam
++) {
639 for (int qual
= 0; qual
< qualifiers
.length
; qual
++) {
640 outList
.add(new KeyValue(row
, families
[fam
], qualifiers
[qual
], qual
, value
));
647 * Verifies that result contains all the key values within expKvList. Fails the test otherwise
652 static void verifyResult(Result result
, List
<Cell
> expKvList
, String msg
) {
653 if (LOG
.isInfoEnabled()) {
655 LOG
.info("Expected count: " + expKvList
.size());
656 LOG
.info("Actual count: " + result
.size());
659 if (expKvList
.isEmpty()) return;
662 for (Cell kv
: result
.rawCells()) {
663 if (i
>= expKvList
.size()) {
664 break; // we will check the size later
667 Cell kvExp
= expKvList
.get(i
++);
668 assertTrue("Not equal. get kv: " + kv
.toString() + " exp kv: " + kvExp
.toString(),
672 assertEquals(expKvList
.size(), result
.size());
676 * Compares two results and fails the test if the results are different
681 static void compareResults(Result r1
, Result r2
, final String message
) {
682 if (LOG
.isInfoEnabled()) {
683 if (message
!= null) LOG
.info(message
);
684 LOG
.info("r1: " + r1
);
685 LOG
.info("r2: " + r2
);
688 final String failureMessage
= "Results r1:" + r1
+ " \nr2:" + r2
+ " are not equivalent";
689 if (r1
== null && r2
== null) fail(failureMessage
);
690 else if (r1
== null || r2
== null) fail(failureMessage
);
693 Result
.compareResults(r1
, r2
);
694 } catch (Exception e
) {
695 fail(failureMessage
);
700 public void testReadPointAndPartialResults() throws Exception
{
701 final TableName tableName
= TableName
.valueOf(name
.getMethodName());
704 int numQualifiers
= 5;
705 byte[][] rows
= HTestConst
.makeNAscii(Bytes
.toBytes("testRow"), numRows
);
706 byte[][] families
= HTestConst
.makeNAscii(Bytes
.toBytes("testFamily"), numFamilies
);
707 byte[][] qualifiers
= HTestConst
.makeNAscii(Bytes
.toBytes("testQualifier"), numQualifiers
);
708 byte[] value
= Bytes
.createMaxByteArray(100);
710 Table tmpTable
= createTestTable(tableName
, rows
, families
, qualifiers
, value
);
711 // Open scanner before deletes
712 ResultScanner scanner
=
713 tmpTable
.getScanner(new Scan().setMaxResultSize(1).setAllowPartialResults(true));
714 // now the openScanner will also fetch data and will be executed lazily, i.e, only openScanner
715 // when you call next, so here we need to make a next call to open scanner. The maxResultSize
716 // limit can make sure that we will not fetch all the data at once, so the test sill works.
717 int scannerCount
= scanner
.next().rawCells().length
;
718 Delete delete1
= new Delete(rows
[0]);
719 delete1
.addColumn(families
[0], qualifiers
[0], 0);
720 tmpTable
.delete(delete1
);
722 Delete delete2
= new Delete(rows
[1]);
723 delete2
.addColumn(families
[1], qualifiers
[1], 1);
724 tmpTable
.delete(delete2
);
726 // Should see all cells because scanner was opened prior to deletes
727 scannerCount
+= countCellsFromScanner(scanner
);
728 int expectedCount
= numRows
* numFamilies
* numQualifiers
;
729 assertTrue("scannerCount: " + scannerCount
+ " expectedCount: " + expectedCount
,
730 scannerCount
== expectedCount
);
732 // Minus 2 for the two cells that were deleted
733 scanner
= tmpTable
.getScanner(new Scan().setMaxResultSize(1).setAllowPartialResults(true));
734 scannerCount
= countCellsFromScanner(scanner
);
735 expectedCount
= numRows
* numFamilies
* numQualifiers
- 2;
736 assertTrue("scannerCount: " + scannerCount
+ " expectedCount: " + expectedCount
,
737 scannerCount
== expectedCount
);
739 scanner
= tmpTable
.getScanner(new Scan().setMaxResultSize(1).setAllowPartialResults(true));
740 scannerCount
= scanner
.next().rawCells().length
;
741 // Put in 2 new rows. The timestamps differ from the deleted rows
742 Put put1
= new Put(rows
[0]);
743 put1
.add(new KeyValue(rows
[0], families
[0], qualifiers
[0], 1, value
));
746 Put put2
= new Put(rows
[1]);
747 put2
.add(new KeyValue(rows
[1], families
[1], qualifiers
[1], 2, value
));
750 // Scanner opened prior to puts. Cell count shouldn't have changed
751 scannerCount
+= countCellsFromScanner(scanner
);
752 expectedCount
= numRows
* numFamilies
* numQualifiers
- 2;
753 assertTrue("scannerCount: " + scannerCount
+ " expectedCount: " + expectedCount
,
754 scannerCount
== expectedCount
);
756 // Now the scanner should see the cells that were added by puts
757 scanner
= tmpTable
.getScanner(new Scan().setMaxResultSize(1).setAllowPartialResults(true));
758 scannerCount
= countCellsFromScanner(scanner
);
759 expectedCount
= numRows
* numFamilies
* numQualifiers
;
760 assertTrue("scannerCount: " + scannerCount
+ " expectedCount: " + expectedCount
,
761 scannerCount
== expectedCount
);
763 TEST_UTIL
.deleteTable(tableName
);
767 * Exhausts the scanner by calling next repetitively. Once completely exhausted, close scanner and
768 * return total cell count
769 * @param scanner the scanner to exhaust
770 * @return the number of cells counted
771 * @throws Exception if there is a problem retrieving cells from the scanner
773 private int countCellsFromScanner(ResultScanner scanner
) throws Exception
{
774 Result result
= null;
776 while ((result
= scanner
.next()) != null) {
777 numCells
+= result
.rawCells().length
;
785 * Test partial Result re-assembly in the presence of different filters. The Results from the
786 * partial scanner should match the Results returned from a scanner that receives all of the
787 * results in one RPC to the server. The partial scanner is tested with a variety of different
788 * result sizes (all of which are less than the size necessary to fetch an entire row)
792 public void testPartialResultsWithColumnFilter() throws Exception
{
793 testPartialResultsWithColumnFilter(new FirstKeyOnlyFilter());
794 testPartialResultsWithColumnFilter(new ColumnPrefixFilter(Bytes
.toBytes("testQualifier5")));
795 testPartialResultsWithColumnFilter(new ColumnRangeFilter(Bytes
.toBytes("testQualifer1"), true,
796 Bytes
.toBytes("testQualifier7"), true));
798 Set
<byte[]> qualifiers
= new LinkedHashSet
<>();
799 qualifiers
.add(Bytes
.toBytes("testQualifier5"));
800 testPartialResultsWithColumnFilter(new FirstKeyValueMatchingQualifiersFilter(qualifiers
));
803 public void testPartialResultsWithColumnFilter(Filter filter
) throws Exception
{
804 assertTrue(!filter
.hasFilterRow());
806 Scan partialScan
= new Scan();
807 partialScan
.setFilter(filter
);
809 Scan oneshotScan
= new Scan();
810 oneshotScan
.setFilter(filter
);
811 oneshotScan
.setMaxResultSize(Long
.MAX_VALUE
);
813 for (int i
= 1; i
<= NUM_COLS
; i
++) {
814 partialScan
.setMaxResultSize(getResultSizeForNumberOfCells(i
));
815 testEquivalenceOfScanResults(TABLE
, partialScan
, oneshotScan
);
819 private void moveRegion(Table table
, int index
) throws IOException
{
820 List
<Pair
<RegionInfo
, ServerName
>> regions
= MetaTableAccessor
821 .getTableRegionsAndLocations(TEST_UTIL
.getConnection(),
823 assertEquals(1, regions
.size());
824 RegionInfo regionInfo
= regions
.get(0).getFirst();
825 ServerName name
= TEST_UTIL
.getHBaseCluster().getRegionServer(index
).getServerName();
826 TEST_UTIL
.getAdmin().move(regionInfo
.getEncodedNameAsBytes(), name
);
829 private void assertCell(Cell cell
, byte[] row
, byte[] cf
, byte[] cq
) {
830 assertArrayEquals(row
,
831 Bytes
.copy(cell
.getRowArray(), cell
.getRowOffset(), cell
.getRowLength()));
832 assertArrayEquals(cf
,
833 Bytes
.copy(cell
.getFamilyArray(), cell
.getFamilyOffset(), cell
.getFamilyLength()));
834 assertArrayEquals(cq
,
835 Bytes
.copy(cell
.getQualifierArray(), cell
.getQualifierOffset(), cell
.getQualifierLength()));
839 public void testPartialResultWhenRegionMove() throws IOException
{
840 Table table
= createTestTable(TableName
.valueOf(name
.getMethodName()),
841 ROWS
, FAMILIES
, QUALIFIERS
, VALUE
);
843 moveRegion(table
, 1);
845 Scan scan
= new Scan();
846 scan
.setMaxResultSize(1);
847 scan
.setAllowPartialResults(true);
848 ResultScanner scanner
= table
.getScanner(scan
);
849 for (int i
= 0; i
< NUM_FAMILIES
* NUM_QUALIFIERS
- 1; i
++) {
852 Result result1
= scanner
.next();
853 assertEquals(1, result1
.rawCells().length
);
854 Cell c1
= result1
.rawCells()[0];
855 assertCell(c1
, ROWS
[0], FAMILIES
[NUM_FAMILIES
- 1], QUALIFIERS
[NUM_QUALIFIERS
- 1]);
856 assertFalse(result1
.mayHaveMoreCellsInRow());
858 moveRegion(table
, 2);
860 Result result2
= scanner
.next();
861 assertEquals(1, result2
.rawCells().length
);
862 Cell c2
= result2
.rawCells()[0];
863 assertCell(c2
, ROWS
[1], FAMILIES
[0], QUALIFIERS
[0]);
864 assertTrue(result2
.mayHaveMoreCellsInRow());
866 moveRegion(table
, 3);
868 Result result3
= scanner
.next();
869 assertEquals(1, result3
.rawCells().length
);
870 Cell c3
= result3
.rawCells()[0];
871 assertCell(c3
, ROWS
[1], FAMILIES
[0], QUALIFIERS
[1]);
872 assertTrue(result3
.mayHaveMoreCellsInRow());
877 public void testReversedPartialResultWhenRegionMove() throws IOException
{
878 Table table
= createTestTable(TableName
.valueOf(name
.getMethodName()),
879 ROWS
, FAMILIES
, QUALIFIERS
, VALUE
);
881 moveRegion(table
, 1);
883 Scan scan
= new Scan();
884 scan
.setMaxResultSize(1);
885 scan
.setAllowPartialResults(true);
886 scan
.setReversed(true);
887 ResultScanner scanner
= table
.getScanner(scan
);
888 for (int i
= 0; i
< NUM_FAMILIES
* NUM_QUALIFIERS
-1; i
++) {
891 Result result1
= scanner
.next();
892 assertEquals(1, result1
.rawCells().length
);
893 Cell c1
= result1
.rawCells()[0];
894 assertCell(c1
, ROWS
[NUM_ROWS
-1], FAMILIES
[NUM_FAMILIES
- 1], QUALIFIERS
[NUM_QUALIFIERS
- 1]);
895 assertFalse(result1
.mayHaveMoreCellsInRow());
897 moveRegion(table
, 2);
899 Result result2
= scanner
.next();
900 assertEquals(1, result2
.rawCells().length
);
901 Cell c2
= result2
.rawCells()[0];
902 assertCell(c2
, ROWS
[NUM_ROWS
-2], FAMILIES
[0], QUALIFIERS
[0]);
903 assertTrue(result2
.mayHaveMoreCellsInRow());
905 moveRegion(table
, 3);
907 Result result3
= scanner
.next();
908 assertEquals(1, result3
.rawCells().length
);
909 Cell c3
= result3
.rawCells()[0];
910 assertCell(c3
, ROWS
[NUM_ROWS
-2], FAMILIES
[0], QUALIFIERS
[1]);
911 assertTrue(result3
.mayHaveMoreCellsInRow());
916 public void testCompleteResultWhenRegionMove() throws IOException
{
917 Table table
= createTestTable(TableName
.valueOf(name
.getMethodName()),
918 ROWS
, FAMILIES
, QUALIFIERS
, VALUE
);
920 moveRegion(table
, 1);
922 Scan scan
= new Scan();
923 scan
.setMaxResultSize(1);
925 ResultScanner scanner
= table
.getScanner(scan
);
927 Result result1
= scanner
.next();
928 assertEquals(NUM_FAMILIES
* NUM_QUALIFIERS
, result1
.rawCells().length
);
929 Cell c1
= result1
.rawCells()[0];
930 assertCell(c1
, ROWS
[0], FAMILIES
[0], QUALIFIERS
[0]);
931 assertFalse(result1
.mayHaveMoreCellsInRow());
933 moveRegion(table
, 2);
935 Result result2
= scanner
.next();
936 assertEquals(NUM_FAMILIES
* NUM_QUALIFIERS
, result2
.rawCells().length
);
937 Cell c2
= result2
.rawCells()[0];
938 assertCell(c2
, ROWS
[1], FAMILIES
[0], QUALIFIERS
[0]);
939 assertFalse(result2
.mayHaveMoreCellsInRow());
941 moveRegion(table
, 3);
943 Result result3
= scanner
.next();
944 assertEquals(NUM_FAMILIES
* NUM_QUALIFIERS
, result3
.rawCells().length
);
945 Cell c3
= result3
.rawCells()[0];
946 assertCell(c3
, ROWS
[2], FAMILIES
[0], QUALIFIERS
[0]);
947 assertFalse(result3
.mayHaveMoreCellsInRow());
952 public void testReversedCompleteResultWhenRegionMove() throws IOException
{
953 Table table
= createTestTable(TableName
.valueOf(name
.getMethodName()),
954 ROWS
, FAMILIES
, QUALIFIERS
, VALUE
);
956 moveRegion(table
, 1);
958 Scan scan
= new Scan();
959 scan
.setMaxResultSize(1);
961 scan
.setReversed(true);
962 ResultScanner scanner
= table
.getScanner(scan
);
964 Result result1
= scanner
.next();
965 assertEquals(NUM_FAMILIES
*NUM_QUALIFIERS
, result1
.rawCells().length
);
966 Cell c1
= result1
.rawCells()[0];
967 assertCell(c1
, ROWS
[NUM_ROWS
-1], FAMILIES
[0], QUALIFIERS
[0]);
968 assertFalse(result1
.mayHaveMoreCellsInRow());
970 moveRegion(table
, 2);
972 Result result2
= scanner
.next();
973 assertEquals(NUM_FAMILIES
*NUM_QUALIFIERS
, result2
.rawCells().length
);
974 Cell c2
= result2
.rawCells()[0];
975 assertCell(c2
, ROWS
[NUM_ROWS
-2], FAMILIES
[0], QUALIFIERS
[0]);
976 assertFalse(result2
.mayHaveMoreCellsInRow());
978 moveRegion(table
, 3);
980 Result result3
= scanner
.next();
981 assertEquals(NUM_FAMILIES
*NUM_QUALIFIERS
, result3
.rawCells().length
);
982 Cell c3
= result3
.rawCells()[0];
983 assertCell(c3
, ROWS
[NUM_ROWS
-3], FAMILIES
[0], QUALIFIERS
[0]);
984 assertFalse(result3
.mayHaveMoreCellsInRow());
989 public void testBatchingResultWhenRegionMove() throws IOException
{
990 // If user setBatch(5) and rpc returns 3+5+5+5+3 cells,
991 // we should return 5+5+5+5+1 to user.
992 // setBatch doesn't mean setAllowPartialResult(true)
993 Table table
= createTestTable(TableName
.valueOf(name
.getMethodName()), ROWS
, FAMILIES
,
996 Put put
= new Put(ROWS
[1]);
997 put
.addColumn(FAMILIES
[0], QUALIFIERS
[1], new byte[VALUE_SIZE
* 10]);
999 Delete delete
= new Delete(ROWS
[1]);
1000 delete
.addColumn(FAMILIES
[NUM_FAMILIES
- 1], QUALIFIERS
[NUM_QUALIFIERS
- 1]);
1001 table
.delete(delete
);
1003 moveRegion(table
, 1);
1005 Scan scan
= new Scan();
1008 scan
.setMaxResultSize(VALUE_SIZE
* 6);
1010 ResultScanner scanner
= table
.getScanner(scan
);
1011 for (int i
= 0; i
< NUM_FAMILIES
* NUM_QUALIFIERS
/ 5 - 1; i
++) {
1012 assertTrue(scanner
.next().mayHaveMoreCellsInRow());
1014 Result result1
= scanner
.next();
1015 assertEquals(5, result1
.rawCells().length
);
1016 assertCell(result1
.rawCells()[0], ROWS
[0], FAMILIES
[NUM_FAMILIES
- 1],
1017 QUALIFIERS
[NUM_QUALIFIERS
- 5]);
1018 assertCell(result1
.rawCells()[4], ROWS
[0], FAMILIES
[NUM_FAMILIES
- 1],
1019 QUALIFIERS
[NUM_QUALIFIERS
- 1]);
1020 assertFalse(result1
.mayHaveMoreCellsInRow());
1022 moveRegion(table
, 2);
1024 Result result2
= scanner
.next();
1025 assertEquals(5, result2
.rawCells().length
);
1026 assertCell(result2
.rawCells()[0], ROWS
[1], FAMILIES
[0], QUALIFIERS
[0]);
1027 assertCell(result2
.rawCells()[4], ROWS
[1], FAMILIES
[0], QUALIFIERS
[4]);
1028 assertTrue(result2
.mayHaveMoreCellsInRow());
1030 moveRegion(table
, 3);
1032 Result result3
= scanner
.next();
1033 assertEquals(5, result3
.rawCells().length
);
1034 assertCell(result3
.rawCells()[0], ROWS
[1], FAMILIES
[0], QUALIFIERS
[5]);
1035 assertCell(result3
.rawCells()[4], ROWS
[1], FAMILIES
[0], QUALIFIERS
[9]);
1036 assertTrue(result3
.mayHaveMoreCellsInRow());
1038 for (int i
= 0; i
< NUM_FAMILIES
* NUM_QUALIFIERS
/ 5 - 3; i
++) {
1039 Result result
= scanner
.next();
1040 assertEquals(5, result
.rawCells().length
);
1041 assertTrue(result
.mayHaveMoreCellsInRow());
1043 Result result
= scanner
.next();
1044 assertEquals(4, result
.rawCells().length
);
1045 assertFalse(result
.mayHaveMoreCellsInRow());
1048 for (int i
= 2; i
< NUM_ROWS
; i
++) {
1049 for (int j
= 0; j
< NUM_FAMILIES
; j
++) {
1050 for (int k
= 0; k
< NUM_QUALIFIERS
; k
+= 5) {
1051 result
= scanner
.next();
1052 assertCell(result
.rawCells()[0], ROWS
[i
], FAMILIES
[j
], QUALIFIERS
[k
]);
1053 assertEquals(5, result
.rawCells().length
);
1054 if (j
== NUM_FAMILIES
- 1 && k
== NUM_QUALIFIERS
- 5) {
1055 assertFalse(result
.mayHaveMoreCellsInRow());
1057 assertTrue(result
.mayHaveMoreCellsInRow());
1062 assertNull(scanner
.next());
1066 public void testDontThrowUnknowScannerExceptionToClient() throws Exception
{
1067 Table table
= createTestTable(TableName
.valueOf(name
.getMethodName()), ROWS
, FAMILIES
,
1069 Scan scan
= new Scan();
1071 ResultScanner scanner
= table
.getScanner(scan
);
1073 Thread
.sleep(timeout
* 2);
1075 while (scanner
.next() != null) {
1078 assertEquals(NUM_ROWS
, count
);
1083 public void testMayHaveMoreCellsInRowReturnsTrueAndSetBatch() throws IOException
{
1084 Table table
= createTestTable(TableName
.valueOf(name
.getMethodName()), ROWS
, FAMILIES
,
1086 Scan scan
= new Scan();
1088 scan
.setFilter(new FirstKeyOnlyFilter());
1089 ResultScanner scanner
= table
.getScanner(scan
);
1091 while ((result
= scanner
.next()) != null) {
1092 assertTrue(result
.rawCells() != null);
1093 assertEquals(1, result
.rawCells().length
);