View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with this
4    * work for additional information regarding copyright ownership. The ASF
5    * licenses this file to you under the Apache License, Version 2.0 (the
6    * "License"); you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14   * License for the specific language governing permissions and limitations
15   * under the License.
16   */
17  package org.apache.hadoop.hbase.client;
18  
19  import static org.junit.Assert.assertArrayEquals;
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertNull;
22  import static org.junit.Assert.assertTrue;
23  
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.conf.Configuration;
31  import org.apache.hadoop.hbase.Cell;
32  import org.apache.hadoop.hbase.HBaseTestingUtility;
33  import org.apache.hadoop.hbase.HColumnDescriptor;
34  import org.apache.hadoop.hbase.HConstants;
35  import org.apache.hadoop.hbase.HRegionInfo;
36  import org.apache.hadoop.hbase.HRegionLocation;
37  import org.apache.hadoop.hbase.HTestConst;
38  import org.apache.hadoop.hbase.KeyValue;
39  import org.apache.hadoop.hbase.testclassification.MediumTests;
40  import org.apache.hadoop.hbase.MiniHBaseCluster;
41  import org.apache.hadoop.hbase.TableName;
42  import org.apache.hadoop.hbase.filter.ColumnPrefixFilter;
43  import org.apache.hadoop.hbase.filter.ColumnRangeFilter;
44  import org.apache.hadoop.hbase.master.HMaster;
45  import org.apache.hadoop.hbase.master.RegionState.State;
46  import org.apache.hadoop.hbase.master.RegionStates;
47  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
48  import org.apache.hadoop.hbase.regionserver.HRegionServer;
49  import org.apache.hadoop.hbase.util.Bytes;
50  import org.apache.hadoop.hbase.util.ConfigUtil;
51  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
52  import org.apache.hadoop.hbase.zookeeper.ZKAssign;
53  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
54  import org.junit.After;
55  import org.junit.AfterClass;
56  import org.junit.Before;
57  import org.junit.BeforeClass;
58  import org.junit.Test;
59  import org.junit.experimental.categories.Category;
60  
61  /**
62   * A client-side test, mostly testing scanners with various parameters.
63   */
64  @Category(MediumTests.class)
65  public class TestScannersFromClientSide {
66    private static final Log LOG = LogFactory.getLog(TestScannersFromClientSide.class);
67  
68    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
69    private static byte [] ROW = Bytes.toBytes("testRow");
70    private static byte [] FAMILY = Bytes.toBytes("testFamily");
71    private static byte [] QUALIFIER = Bytes.toBytes("testQualifier");
72    private static byte [] VALUE = Bytes.toBytes("testValue");
73  
74    /**
75     * @throws java.lang.Exception
76     */
77    @BeforeClass
78    public static void setUpBeforeClass() throws Exception {
79      Configuration conf = TEST_UTIL.getConfiguration();
80      conf.setLong(HConstants.HBASE_CLIENT_SCANNER_MAX_RESULT_SIZE_KEY, 10 * 1024 * 1024);
81      TEST_UTIL.startMiniCluster(3);
82    }
83  
84    /**
85     * @throws java.lang.Exception
86     */
87    @AfterClass
88    public static void tearDownAfterClass() throws Exception {
89      TEST_UTIL.shutdownMiniCluster();
90    }
91  
92    /**
93     * @throws java.lang.Exception
94     */
95    @Before
96    public void setUp() throws Exception {
97      // Nothing to do.
98    }
99  
100   /**
101    * @throws java.lang.Exception
102    */
103   @After
104   public void tearDown() throws Exception {
105     // Nothing to do.
106   }
107 
108   /**
109    * Test from client side for batch of scan
110    *
111    * @throws Exception
112    */
113   @Test
114   public void testScanBatch() throws Exception {
115     TableName TABLE = TableName.valueOf("testScanBatch");
116     byte [][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, 8);
117 
118     Table ht = TEST_UTIL.createTable(TABLE, FAMILY);
119 
120     Put put;
121     Scan scan;
122     Delete delete;
123     Result result;
124     ResultScanner scanner;
125     boolean toLog = true;
126     List<Cell> kvListExp;
127 
128     // table: row, family, c0:0, c1:1, ... , c7:7
129     put = new Put(ROW);
130     for (int i=0; i < QUALIFIERS.length; i++) {
131       KeyValue kv = new KeyValue(ROW, FAMILY, QUALIFIERS[i], i, VALUE);
132       put.add(kv);
133     }
134     ht.put(put);
135 
136     // table: row, family, c0:0, c1:1, ..., c6:2, c6:6 , c7:7
137     put = new Put(ROW);
138     KeyValue kv = new KeyValue(ROW, FAMILY, QUALIFIERS[6], 2, VALUE);
139     put.add(kv);
140     ht.put(put);
141 
142     // delete upto ts: 3
143     delete = new Delete(ROW);
144     delete.deleteFamily(FAMILY, 3);
145     ht.delete(delete);
146 
147     // without batch
148     scan = new Scan().withStartRow(ROW);
149     scan.setMaxVersions();
150     scanner = ht.getScanner(scan);
151 
152     // c4:4, c5:5, c6:6, c7:7
153     kvListExp = new ArrayList<Cell>();
154     kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[4], 4, VALUE));
155     kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[5], 5, VALUE));
156     kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[6], 6, VALUE));
157     kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[7], 7, VALUE));
158     result = scanner.next();
159     verifyResult(result, kvListExp, toLog, "Testing first batch of scan");
160 
161     // with batch
162     scan =  new Scan().withStartRow(ROW);
163     scan.setMaxVersions();
164     scan.setBatch(2);
165     scanner = ht.getScanner(scan);
166 
167     // First batch: c4:4, c5:5
168     kvListExp = new ArrayList<Cell>();
169     kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[4], 4, VALUE));
170     kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[5], 5, VALUE));
171     result = scanner.next();
172     verifyResult(result, kvListExp, toLog, "Testing first batch of scan");
173 
174     // Second batch: c6:6, c7:7
175     kvListExp = new ArrayList<Cell>();
176     kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[6], 6, VALUE));
177     kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[7], 7, VALUE));
178     result = scanner.next();
179     verifyResult(result, kvListExp, toLog, "Testing second batch of scan");
180 
181   }
182 
183   @Test
184   public void testMaxResultSizeIsSetToDefault() throws Exception {
185     TableName TABLE = TableName.valueOf("testMaxResultSizeIsSetToDefault");
186     Table ht = TEST_UTIL.createTable(TABLE, FAMILY);
187 
188     // The max result size we expect the scan to use by default.
189     long expectedMaxResultSize =
190         TEST_UTIL.getConfiguration().getLong(HConstants.HBASE_CLIENT_SCANNER_MAX_RESULT_SIZE_KEY,
191           HConstants.DEFAULT_HBASE_CLIENT_SCANNER_MAX_RESULT_SIZE);
192 
193     int numRows = 5;
194     byte[][] ROWS = HTestConst.makeNAscii(ROW, numRows);
195 
196     int numQualifiers = 10;
197     byte[][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, numQualifiers);
198 
199     // Specify the cell size such that a single row will be larger than the default
200     // value of maxResultSize. This means that Scan RPCs should return at most a single
201     // result back to the client.
202     int cellSize = (int) (expectedMaxResultSize / (numQualifiers - 1));
203     byte[] cellValue = Bytes.createMaxByteArray(cellSize);
204 
205     Put put;
206     List<Put> puts = new ArrayList<Put>();
207     for (int row = 0; row < ROWS.length; row++) {
208       put = new Put(ROWS[row]);
209       for (int qual = 0; qual < QUALIFIERS.length; qual++) {
210         KeyValue kv = new KeyValue(ROWS[row], FAMILY, QUALIFIERS[qual], cellValue);
211         put.add(kv);
212       }
213       puts.add(put);
214     }
215     ht.put(puts);
216 
217     // Create a scan with the default configuration.
218     Scan scan = new Scan();
219 
220     ResultScanner scanner = ht.getScanner(scan);
221     assertTrue(scanner instanceof ClientScanner);
222     ClientScanner clientScanner = (ClientScanner) scanner;
223 
224     // Call next to issue a single RPC to the server
225     scanner.next();
226 
227     // The scanner should have, at most, a single result in its cache. If there more results exists
228     // in the cache it means that more than the expected max result size was fetched.
229     assertTrue("The cache contains: " + clientScanner.getCacheSize() + " results",
230       clientScanner.getCacheSize() <= 1);
231   }
232 
233   @Test
234   public void testSmallScan() throws Exception {
235     TableName TABLE = TableName.valueOf("testSmallScan");
236 
237     int numRows = 10;
238     byte[][] ROWS = HTestConst.makeNAscii(ROW, numRows);
239 
240     int numQualifiers = 10;
241     byte[][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, numQualifiers);
242 
243     Table ht = TEST_UTIL.createTable(TABLE, FAMILY);
244 
245     Put put;
246     List<Put> puts = new ArrayList<Put>();
247     for (int row = 0; row < ROWS.length; row++) {
248       put = new Put(ROWS[row]);
249       for (int qual = 0; qual < QUALIFIERS.length; qual++) {
250         KeyValue kv = new KeyValue(ROWS[row], FAMILY, QUALIFIERS[qual], VALUE);
251         put.add(kv);
252       }
253       puts.add(put);
254     }
255     ht.put(puts);
256 
257     int expectedRows = numRows;
258     int expectedCols = numRows * numQualifiers;
259 
260     // Test normal and reversed
261     testSmallScan(ht, true, expectedRows, expectedCols);
262     testSmallScan(ht, false, expectedRows, expectedCols);
263   }
264 
265   /**
266    * Run through a variety of test configurations with a small scan
267    * @param table
268    * @param reversed
269    * @param rows
270    * @param columns
271    * @throws Exception
272    */
273   private void testSmallScan(Table table, boolean reversed, int rows, int columns) throws Exception {
274     Scan baseScan = new Scan();
275     baseScan.setReversed(reversed);
276     baseScan.setSmall(true);
277 
278     Scan scan = new Scan(baseScan);
279     verifyExpectedCounts(table, scan, rows, columns);
280 
281     scan = new Scan(baseScan);
282     scan.setMaxResultSize(1);
283     verifyExpectedCounts(table, scan, rows, columns);
284 
285     scan = new Scan(baseScan);
286     scan.setMaxResultSize(1);
287     scan.setCaching(Integer.MAX_VALUE);
288     verifyExpectedCounts(table, scan, rows, columns);
289   }
290 
291   private void verifyExpectedCounts(Table table, Scan scan, int expectedRowCount,
292       int expectedCellCount) throws Exception {
293     ResultScanner scanner = table.getScanner(scan);
294 
295     int rowCount = 0;
296     int cellCount = 0;
297     Result r = null;
298     while ((r = scanner.next()) != null) {
299       rowCount++;
300       cellCount += r.rawCells().length;
301     }
302 
303     assertTrue("Expected row count: " + expectedRowCount + " Actual row count: " + rowCount,
304       expectedRowCount == rowCount);
305     assertTrue("Expected cell count: " + expectedCellCount + " Actual cell count: " + cellCount,
306       expectedCellCount == cellCount);
307     scanner.close();
308   }
309 
310   /**
311    * Test from client side for get with maxResultPerCF set
312    *
313    * @throws Exception
314    */
315   @Test
316   public void testGetMaxResults() throws Exception {
317     byte [] TABLE = Bytes.toBytes("testGetMaxResults");
318     byte [][] FAMILIES = HTestConst.makeNAscii(FAMILY, 3);
319     byte [][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, 20);
320 
321     Table ht = TEST_UTIL.createTable(TABLE, FAMILIES);
322 
323     Get get;
324     Put put;
325     Result result;
326     boolean toLog = true;
327     List<Cell> kvListExp;
328 
329     kvListExp = new ArrayList<Cell>();
330     // Insert one CF for row[0]
331     put = new Put(ROW);
332     for (int i=0; i < 10; i++) {
333       KeyValue kv = new KeyValue(ROW, FAMILIES[0], QUALIFIERS[i], 1, VALUE);
334       put.add(kv);
335       kvListExp.add(kv);
336     }
337     ht.put(put);
338 
339     get = new Get(ROW);
340     result = ht.get(get);
341     verifyResult(result, kvListExp, toLog, "Testing without setting maxResults");
342 
343     get = new Get(ROW);
344     get.setMaxResultsPerColumnFamily(2);
345     result = ht.get(get);
346     kvListExp = new ArrayList<Cell>();
347     kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[0], 1, VALUE));
348     kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[1], 1, VALUE));
349     verifyResult(result, kvListExp, toLog, "Testing basic setMaxResults");
350 
351     // Filters: ColumnRangeFilter
352     get = new Get(ROW);
353     get.setMaxResultsPerColumnFamily(5);
354     get.setFilter(new ColumnRangeFilter(QUALIFIERS[2], true, QUALIFIERS[5],
355                                         true));
356     result = ht.get(get);
357     kvListExp = new ArrayList<Cell>();
358     kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[2], 1, VALUE));
359     kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[3], 1, VALUE));
360     kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[4], 1, VALUE));
361     kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[5], 1, VALUE));
362     verifyResult(result, kvListExp, toLog, "Testing single CF with CRF");
363 
364     // Insert two more CF for row[0]
365     // 20 columns for CF2, 10 columns for CF1
366     put = new Put(ROW);
367     for (int i=0; i < QUALIFIERS.length; i++) {
368       KeyValue kv = new KeyValue(ROW, FAMILIES[2], QUALIFIERS[i], 1, VALUE);
369       put.add(kv);
370     }
371     ht.put(put);
372 
373     put = new Put(ROW);
374     for (int i=0; i < 10; i++) {
375       KeyValue kv = new KeyValue(ROW, FAMILIES[1], QUALIFIERS[i], 1, VALUE);
376       put.add(kv);
377     }
378     ht.put(put);
379 
380     get = new Get(ROW);
381     get.setMaxResultsPerColumnFamily(12);
382     get.addFamily(FAMILIES[1]);
383     get.addFamily(FAMILIES[2]);
384     result = ht.get(get);
385     kvListExp = new ArrayList<Cell>();
386     //Exp: CF1:q0, ..., q9, CF2: q0, q1, q10, q11, ..., q19
387     for (int i=0; i < 10; i++) {
388       kvListExp.add(new KeyValue(ROW, FAMILIES[1], QUALIFIERS[i], 1, VALUE));
389     }
390     for (int i=0; i < 2; i++) {
391         kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[i], 1, VALUE));
392       }
393     for (int i=10; i < 20; i++) {
394       kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[i], 1, VALUE));
395     }
396     verifyResult(result, kvListExp, toLog, "Testing multiple CFs");
397 
398     // Filters: ColumnRangeFilter and ColumnPrefixFilter
399     get = new Get(ROW);
400     get.setMaxResultsPerColumnFamily(3);
401     get.setFilter(new ColumnRangeFilter(QUALIFIERS[2], true, null, true));
402     result = ht.get(get);
403     kvListExp = new ArrayList<Cell>();
404     for (int i=2; i < 5; i++) {
405       kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[i], 1, VALUE));
406     }
407     for (int i=2; i < 5; i++) {
408       kvListExp.add(new KeyValue(ROW, FAMILIES[1], QUALIFIERS[i], 1, VALUE));
409     }
410     for (int i=2; i < 5; i++) {
411       kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[i], 1, VALUE));
412     }
413     verifyResult(result, kvListExp, toLog, "Testing multiple CFs + CRF");
414 
415     get = new Get(ROW);
416     get.setMaxResultsPerColumnFamily(7);
417     get.setFilter(new ColumnPrefixFilter(QUALIFIERS[1]));
418     result = ht.get(get);
419     kvListExp = new ArrayList<Cell>();
420     kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[1], 1, VALUE));
421     kvListExp.add(new KeyValue(ROW, FAMILIES[1], QUALIFIERS[1], 1, VALUE));
422     kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[1], 1, VALUE));
423     for (int i=10; i < 16; i++) {
424       kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[i], 1, VALUE));
425     }
426     verifyResult(result, kvListExp, toLog, "Testing multiple CFs + PFF");
427 
428   }
429 
430   /**
431    * Test from client side for scan with maxResultPerCF set
432    *
433    * @throws Exception
434    */
435   @Test
436   public void testScanMaxResults() throws Exception {
437     byte [] TABLE = Bytes.toBytes("testScanLimit");
438     byte [][] ROWS = HTestConst.makeNAscii(ROW, 2);
439     byte [][] FAMILIES = HTestConst.makeNAscii(FAMILY, 3);
440     byte [][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, 10);
441 
442     Table ht = TEST_UTIL.createTable(TABLE, FAMILIES);
443 
444     Put put;
445     Scan scan;
446     Result result;
447     boolean toLog = true;
448     List<Cell> kvListExp, kvListScan;
449 
450     kvListExp = new ArrayList<Cell>();
451 
452     for (int r=0; r < ROWS.length; r++) {
453       put = new Put(ROWS[r]);
454       for (int c=0; c < FAMILIES.length; c++) {
455         for (int q=0; q < QUALIFIERS.length; q++) {
456           KeyValue kv = new KeyValue(ROWS[r], FAMILIES[c], QUALIFIERS[q], 1, VALUE);
457           put.add(kv);
458           if (q < 4) {
459             kvListExp.add(kv);
460           }
461         }
462       }
463       ht.put(put);
464     }
465 
466     scan = new Scan();
467     scan.setMaxResultsPerColumnFamily(4);
468     ResultScanner scanner = ht.getScanner(scan);
469     kvListScan = new ArrayList<Cell>();
470     while ((result = scanner.next()) != null) {
471       for (Cell kv : result.listCells()) {
472         kvListScan.add(kv);
473       }
474     }
475     result = Result.create(kvListScan);
476     verifyResult(result, kvListExp, toLog, "Testing scan with maxResults");
477 
478   }
479 
480   /**
481    * Test from client side for get with rowOffset
482    *
483    * @throws Exception
484    */
485   @Test
486   public void testGetRowOffset() throws Exception {
487     byte [] TABLE = Bytes.toBytes("testGetRowOffset");
488     byte [][] FAMILIES = HTestConst.makeNAscii(FAMILY, 3);
489     byte [][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, 20);
490 
491     Table ht = TEST_UTIL.createTable(TABLE, FAMILIES);
492 
493     Get get;
494     Put put;
495     Result result;
496     boolean toLog = true;
497     List<Cell> kvListExp;
498 
499     // Insert one CF for row
500     kvListExp = new ArrayList<Cell>();
501     put = new Put(ROW);
502     for (int i=0; i < 10; i++) {
503       KeyValue kv = new KeyValue(ROW, FAMILIES[0], QUALIFIERS[i], 1, VALUE);
504       put.add(kv);
505       // skipping first two kvs
506       if (i < 2) continue;
507       kvListExp.add(kv);
508     }
509     ht.put(put);
510 
511     //setting offset to 2
512     get = new Get(ROW);
513     get.setRowOffsetPerColumnFamily(2);
514     result = ht.get(get);
515     verifyResult(result, kvListExp, toLog, "Testing basic setRowOffset");
516 
517     //setting offset to 20
518     get = new Get(ROW);
519     get.setRowOffsetPerColumnFamily(20);
520     result = ht.get(get);
521     kvListExp = new ArrayList<Cell>();
522     verifyResult(result, kvListExp, toLog, "Testing offset > #kvs");
523 
524     //offset + maxResultPerCF
525     get = new Get(ROW);
526     get.setRowOffsetPerColumnFamily(4);
527     get.setMaxResultsPerColumnFamily(5);
528     result = ht.get(get);
529     kvListExp = new ArrayList<Cell>();
530     for (int i=4; i < 9; i++) {
531       kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[i], 1, VALUE));
532     }
533     verifyResult(result, kvListExp, toLog,
534       "Testing offset + setMaxResultsPerCF");
535 
536     // Filters: ColumnRangeFilter
537     get = new Get(ROW);
538     get.setRowOffsetPerColumnFamily(1);
539     get.setFilter(new ColumnRangeFilter(QUALIFIERS[2], true, QUALIFIERS[5],
540                                         true));
541     result = ht.get(get);
542     kvListExp = new ArrayList<Cell>();
543     kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[3], 1, VALUE));
544     kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[4], 1, VALUE));
545     kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[5], 1, VALUE));
546     verifyResult(result, kvListExp, toLog, "Testing offset with CRF");
547 
548     // Insert into two more CFs for row
549     // 10 columns for CF2, 10 columns for CF1
550     for(int j=2; j > 0; j--) {
551       put = new Put(ROW);
552       for (int i=0; i < 10; i++) {
553         KeyValue kv = new KeyValue(ROW, FAMILIES[j], QUALIFIERS[i], 1, VALUE);
554         put.add(kv);
555       }
556       ht.put(put);
557     }
558 
559     get = new Get(ROW);
560     get.setRowOffsetPerColumnFamily(4);
561     get.setMaxResultsPerColumnFamily(2);
562     get.addFamily(FAMILIES[1]);
563     get.addFamily(FAMILIES[2]);
564     result = ht.get(get);
565     kvListExp = new ArrayList<Cell>();
566     //Exp: CF1:q4, q5, CF2: q4, q5
567     kvListExp.add(new KeyValue(ROW, FAMILIES[1], QUALIFIERS[4], 1, VALUE));
568     kvListExp.add(new KeyValue(ROW, FAMILIES[1], QUALIFIERS[5], 1, VALUE));
569     kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[4], 1, VALUE));
570     kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[5], 1, VALUE));
571     verifyResult(result, kvListExp, toLog,
572        "Testing offset + multiple CFs + maxResults");
573   }
574 
575   /**
576    * Test from client side for scan while the region is reopened
577    * on the same region server.
578    *
579    * @throws Exception
580    */
581   @Test
582   public void testScanOnReopenedRegion() throws Exception {
583     TableName TABLE = TableName.valueOf("testScanOnReopenedRegion");
584     byte [][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, 2);
585 
586     HTable ht = TEST_UTIL.createTable(TABLE, FAMILY);
587 
588     Put put;
589     Scan scan;
590     Result result;
591     ResultScanner scanner;
592     boolean toLog = false;
593     List<Cell> kvListExp;
594 
595     // table: row, family, c0:0, c1:1
596     put = new Put(ROW);
597     for (int i=0; i < QUALIFIERS.length; i++) {
598       KeyValue kv = new KeyValue(ROW, FAMILY, QUALIFIERS[i], i, VALUE);
599       put.add(kv);
600     }
601     ht.put(put);
602 
603     scan = new Scan().withStartRow(ROW);
604     scanner = ht.getScanner(scan);
605 
606     HRegionLocation loc = ht.getRegionLocation(ROW);
607     HRegionInfo hri = loc.getRegionInfo();
608     MiniHBaseCluster cluster = TEST_UTIL.getMiniHBaseCluster();
609     byte[] regionName = hri.getRegionName();
610     int i = cluster.getServerWith(regionName);
611     HRegionServer rs = cluster.getRegionServer(i);
612     ProtobufUtil.closeRegion(null,
613       rs.getRSRpcServices(), rs.getServerName(), regionName, false);
614     long startTime = EnvironmentEdgeManager.currentTime();
615     long timeOut = 300000;
616     while (true) {
617       if (rs.getOnlineRegion(regionName) == null) {
618         break;
619       }
620       assertTrue("Timed out in closing the testing region",
621         EnvironmentEdgeManager.currentTime() < startTime + timeOut);
622       Thread.sleep(500);
623     }
624 
625     // Now open the region again.
626     ZooKeeperWatcher zkw = TEST_UTIL.getZooKeeperWatcher();
627     try {
628       HMaster master = cluster.getMaster();
629       RegionStates states = master.getAssignmentManager().getRegionStates();
630       states.regionOffline(hri);
631       states.updateRegionState(hri, State.OPENING);
632       if (ConfigUtil.useZKForAssignment(TEST_UTIL.getConfiguration())) {
633         ZKAssign.createNodeOffline(zkw, hri, loc.getServerName());
634       }
635       ProtobufUtil.openRegion(null, rs.getRSRpcServices(), rs.getServerName(), hri);
636       startTime = EnvironmentEdgeManager.currentTime();
637       while (true) {
638         if (rs.getOnlineRegion(regionName) != null) {
639           break;
640         }
641         assertTrue("Timed out in open the testing region",
642           EnvironmentEdgeManager.currentTime() < startTime + timeOut);
643         Thread.sleep(500);
644       }
645     } finally {
646       ZKAssign.deleteNodeFailSilent(zkw, hri);
647     }
648 
649     // c0:0, c1:1
650     kvListExp = new ArrayList<Cell>();
651     kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[0], 0, VALUE));
652     kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[1], 1, VALUE));
653     result = scanner.next();
654     verifyResult(result, kvListExp, toLog, "Testing scan on re-opened region");
655   }
656 
657   static void verifyResult(Result result, List<Cell> expKvList, boolean toLog,
658       String msg) {
659 
660     LOG.info(msg);
661     LOG.info("Expected count: " + expKvList.size());
662     LOG.info("Actual count: " + result.size());
663     if (expKvList.size() == 0)
664       return;
665 
666     int i = 0;
667     for (Cell kv : result.rawCells()) {
668       if (i >= expKvList.size()) {
669         break;  // we will check the size later
670       }
671 
672       Cell kvExp = expKvList.get(i++);
673       if (toLog) {
674         LOG.info("get kv is: " + kv.toString());
675         LOG.info("exp kv is: " + kvExp.toString());
676       }
677       assertTrue("Not equal", kvExp.equals(kv));
678     }
679 
680     assertEquals(expKvList.size(), result.size());
681   }
682 
683   @Test
684   public void testReadExpiredDataForRawScan() throws IOException {
685     TableName tableName = TableName.valueOf("testReadExpiredDataForRawScan");
686     long ts = System.currentTimeMillis() - 10000;
687     byte[] value = Bytes.toBytes("expired");
688     try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) {
689       table.put(new Put(ROW).addColumn(FAMILY, QUALIFIER, ts, value));
690       assertArrayEquals(value, table.get(new Get(ROW)).getValue(FAMILY, QUALIFIER));
691       TEST_UTIL.getHBaseAdmin().modifyColumn(tableName,
692         new HColumnDescriptor(FAMILY).setTimeToLive(5));
693       try (ResultScanner scanner = table.getScanner(FAMILY)) {
694         assertNull(scanner.next());
695       }
696       try (ResultScanner scanner = table.getScanner(new Scan().setRaw(true))) {
697         assertArrayEquals(value, scanner.next().getValue(FAMILY, QUALIFIER));
698         assertNull(scanner.next());
699       }
700     }
701   }
702 }