View Javadoc

1   /**
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
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
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.
17   */
18  package org.apache.hadoop.hbase.client;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertFalse;
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.hadoop.hbase.HBaseTestingUtility;
29  import org.apache.hadoop.hbase.TableName;
30  import org.apache.hadoop.hbase.testclassification.ClientTests;
31  import org.apache.hadoop.hbase.testclassification.MediumTests;
32  import org.apache.hadoop.hbase.util.Bytes;
33  import org.junit.AfterClass;
34  import org.junit.BeforeClass;
35  import org.junit.Test;
36  import org.junit.experimental.categories.Category;
37  import org.junit.runner.RunWith;
38  import org.junit.runners.Parameterized;
39  import org.junit.runners.Parameterized.Parameter;
40  import org.junit.runners.Parameterized.Parameters;
41  
42  /**
43   * Testcase for newly added feature in HBASE-17143, such as startRow and stopRow
44   * inclusive/exclusive, limit for rows, etc.
45   */
46  @RunWith(Parameterized.class)
47  @Category({ MediumTests.class, ClientTests.class })
48  public class TestScannersFromClientSide2 {
49  
50    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
51  
52    private static TableName TABLE_NAME = TableName.valueOf("scan");
53  
54    private static byte[] FAMILY = Bytes.toBytes("cf");
55  
56    private static byte[] CQ1 = Bytes.toBytes("cq1");
57  
58    private static byte[] CQ2 = Bytes.toBytes("cq2");
59  
60    @Parameter(0)
61    public boolean batch;
62  
63    @Parameter(1)
64    public boolean smallResultSize;
65  
66    @Parameter(2)
67    public boolean allowPartial;
68  
69    @Parameters(name = "{index}: batch={0}, smallResultSize={1}, allowPartial={2}")
70    public static List<Object[]> params() {
71      List<Object[]> params = new ArrayList<>();
72      boolean[] values = new boolean[] { false, true };
73      for (int i = 0; i < 2; i++) {
74        for (int j = 0; j < 2; j++) {
75          for (int k = 0; k < 2; k++) {
76            params.add(new Object[] { values[i], values[j], values[k] });
77          }
78        }
79      }
80      return params;
81    }
82  
83    @BeforeClass
84    public static void setUp() throws Exception {
85      TEST_UTIL.startMiniCluster(3);
86      byte[][] splitKeys = new byte[8][];
87      for (int i = 111; i < 999; i += 111) {
88        splitKeys[i / 111 - 1] = Bytes.toBytes(String.format("%03d", i));
89      }
90      Table table = TEST_UTIL.createTable(TABLE_NAME, FAMILY, splitKeys);
91      List<Put> puts = new ArrayList<>();
92      for (int i = 0; i < 1000; i++) {
93        puts.add(new Put(Bytes.toBytes(String.format("%03d", i)))
94            .addColumn(FAMILY, CQ1, Bytes.toBytes(i)).addColumn(FAMILY, CQ2, Bytes.toBytes(i * i)));
95      }
96      TEST_UTIL.waitTableAvailable(TABLE_NAME);
97      table.put(puts);
98    }
99  
100   @AfterClass
101   public static void tearDown() throws Exception {
102     TEST_UTIL.shutdownMiniCluster();
103   }
104 
105   private Scan createScan() {
106     Scan scan = new Scan();
107     if (batch) {
108       scan.setBatch(1);
109     }
110     if (smallResultSize) {
111       scan.setMaxResultSize(1);
112     }
113     if (allowPartial) {
114       scan.setAllowPartialResults(true);
115     }
116     return scan;
117   }
118 
119   private void assertResultEquals(Result result, int i) {
120     assertEquals(String.format("%03d", i), Bytes.toString(result.getRow()));
121     assertEquals(i, Bytes.toInt(result.getValue(FAMILY, CQ1)));
122     assertEquals(i * i, Bytes.toInt(result.getValue(FAMILY, CQ2)));
123   }
124 
125   private List<Result> doScan(Scan scan) throws IOException {
126     List<Result> results = new ArrayList<>();
127     try (Table table = TEST_UTIL.getConnection().getTable(TABLE_NAME);
128         ResultScanner scanner = table.getScanner(scan)) {
129       for (Result r; (r = scanner.next()) != null;) {
130         results.add(r);
131       }
132     }
133     return assertAndCreateCompleteResults(results);
134   }
135 
136   private List<Result> assertAndCreateCompleteResults(List<Result> results) throws IOException {
137     if ((!batch && !allowPartial) || (allowPartial && !batch && !smallResultSize)) {
138       for (Result result : results) {
139         assertFalse("Should not have partial result", result.mayHaveMoreCellsInRow());
140       }
141       return results;
142     }
143     List<Result> completeResults = new ArrayList<>();
144     List<Result> partialResults = new ArrayList<>();
145     for (Result result : results) {
146       if (!result.mayHaveMoreCellsInRow()) {
147         assertFalse("Should have partial result", partialResults.isEmpty());
148         partialResults.add(result);
149         completeResults.add(Result.createCompleteResult(partialResults));
150         partialResults.clear();
151       } else {
152         partialResults.add(result);
153       }
154     }
155     assertTrue("Should not have orphan partial result", partialResults.isEmpty());
156     return completeResults;
157   }
158 
159   private void testScan(int start, boolean startInclusive, int stop, boolean stopInclusive,
160       int limit) throws Exception {
161     Scan scan =
162         createScan().withStartRow(Bytes.toBytes(String.format("%03d", start)), startInclusive)
163             .withStopRow(Bytes.toBytes(String.format("%03d", stop)), stopInclusive);
164     if (limit > 0) {
165       scan.setLimit(limit);
166     }
167     List<Result> results = doScan(scan);
168     int actualStart = startInclusive ? start : start + 1;
169     int actualStop = stopInclusive ? stop + 1 : stop;
170     int count = actualStop - actualStart;
171     if (limit > 0) {
172       count = Math.min(count, limit);
173     }
174     assertEquals(count, results.size());
175     for (int i = 0; i < count; i++) {
176       assertResultEquals(results.get(i), actualStart + i);
177     }
178   }
179 
180   private void testReversedScan(int start, boolean startInclusive, int stop, boolean stopInclusive,
181       int limit) throws Exception {
182     Scan scan = createScan()
183         .withStartRow(Bytes.toBytes(String.format("%03d", start)), startInclusive)
184         .withStopRow(Bytes.toBytes(String.format("%03d", stop)), stopInclusive).setReversed(true);
185     if (limit > 0) {
186       scan.setLimit(limit);
187     }
188     List<Result> results = doScan(scan);
189     int actualStart = startInclusive ? start : start - 1;
190     int actualStop = stopInclusive ? stop - 1 : stop;
191     int count = actualStart - actualStop;
192     if (limit > 0) {
193       count = Math.min(count, limit);
194     }
195     assertEquals(count, results.size());
196     for (int i = 0; i < count; i++) {
197       assertResultEquals(results.get(i), actualStart - i);
198     }
199   }
200 
201   @Test
202   public void testScanWithLimit() throws Exception {
203     testScan(1, true, 998, false, 900); // from first region to last region
204     testScan(123, true, 345, true, 100);
205     testScan(234, true, 456, false, 100);
206     testScan(345, false, 567, true, 100);
207     testScan(456, false, 678, false, 100);
208 
209   }
210 
211   @Test
212   public void testScanWithLimitGreaterThanActualCount() throws Exception {
213     testScan(1, true, 998, false, 1000); // from first region to last region
214     testScan(123, true, 345, true, 200);
215     testScan(234, true, 456, false, 200);
216     testScan(345, false, 567, true, 200);
217     testScan(456, false, 678, false, 200);
218   }
219 
220   @Test
221   public void testReversedScanWithLimit() throws Exception {
222     testReversedScan(998, true, 1, false, 900); // from last region to first region
223     testReversedScan(543, true, 321, true, 100);
224     testReversedScan(654, true, 432, false, 100);
225     testReversedScan(765, false, 543, true, 100);
226     testReversedScan(876, false, 654, false, 100);
227   }
228 
229   @Test
230   public void testReversedScanWithLimitGreaterThanActualCount() throws Exception {
231     testReversedScan(998, true, 1, false, 1000); // from last region to first region
232     testReversedScan(543, true, 321, true, 200);
233     testReversedScan(654, true, 432, false, 200);
234     testReversedScan(765, false, 543, true, 200);
235     testReversedScan(876, false, 654, false, 200);
236   }
237 
238   @Test
239   public void testStartRowStopRowInclusive() throws Exception {
240     testScan(1, true, 998, false, -1); // from first region to last region
241     testScan(123, true, 345, true, -1);
242     testScan(234, true, 456, false, -1);
243     testScan(345, false, 567, true, -1);
244     testScan(456, false, 678, false, -1);
245   }
246 
247   @Test
248   public void testReversedStartRowStopRowInclusive() throws Exception {
249     testReversedScan(998, true, 1, false, -1); // from last region to first region
250     testReversedScan(543, true, 321, true, -1);
251     testReversedScan(654, true, 432, false, -1);
252     testReversedScan(765, false, 543, true, -1);
253     testReversedScan(876, false, 654, false, -1);
254   }
255 }