View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.client;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.fail;
23  
24  import java.io.IOException;
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.HashMap;
28  import java.util.Map;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.hadoop.conf.Configuration;
33  import org.apache.hadoop.hbase.CategoryBasedTimeout;
34  import org.apache.hadoop.hbase.Cell;
35  import org.apache.hadoop.hbase.DoNotRetryIOException;
36  import org.apache.hadoop.hbase.HBaseTestingUtility;
37  import org.apache.hadoop.hbase.HConstants;
38  import org.apache.hadoop.hbase.HTableDescriptor;
39  import org.apache.hadoop.hbase.TableName;
40  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
41  import org.apache.hadoop.hbase.coprocessor.MultiRowMutationEndpoint;
42  import org.apache.hadoop.hbase.testclassification.LargeTests;
43  import org.apache.hadoop.hbase.util.Bytes;
44  import org.junit.AfterClass;
45  import org.junit.BeforeClass;
46  import org.junit.Rule;
47  import org.junit.Test;
48  import org.junit.experimental.categories.Category;
49  import org.junit.rules.TestName;
50  import org.junit.rules.TestRule;
51  
52  /**
53   * Run Increment tests that use the HBase clients; {@link HTable}.
54   *
55   * Test is parameterized to run the slow and fast increment code paths. If fast, in the @before, we
56   * do a rolling restart of the single regionserver so that it can pick up the go fast configuration.
57   * Doing it this way should be faster than starting/stopping a cluster per test.
58   *
59   * Test takes a long time because spin up a cluster between each run -- ugh.
60   */
61  @Category(LargeTests.class)
62  @SuppressWarnings ("deprecation")
63  public class TestIncrementsFromClientSide {
64    final Log LOG = LogFactory.getLog(getClass());
65    protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
66    private static byte [] ROW = Bytes.toBytes("testRow");
67    private static byte [] FAMILY = Bytes.toBytes("testFamily");
68    private static byte [] QUALIFIER = Bytes.toBytes("testQualifier");
69    // This test depends on there being only one slave running at at a time. See the @Before
70    // method where we do rolling restart.
71    protected static int SLAVES = 1;
72    @Rule public TestName name = new TestName();
73    @Rule public final TestRule timeout = CategoryBasedTimeout.builder().withTimeout(this.getClass()).
74      withLookingForStuckThread(true).build();
75    public static Collection<Object []> data() {
76      return Arrays.asList(new Object[] {Boolean.FALSE}, new Object [] {Boolean.TRUE});
77    }
78  
79    @BeforeClass
80    public static void beforeClass() throws Exception {
81      Configuration conf = TEST_UTIL.getConfiguration();
82      conf.setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
83        MultiRowMutationEndpoint.class.getName());
84      conf.setBoolean("hbase.table.sanity.checks", true); // enable for below tests
85      // We need more than one region server in this test
86      TEST_UTIL.startMiniCluster(SLAVES);
87    }
88  
89    /**
90     * @throws java.lang.Exception
91     */
92    @AfterClass
93    public static void afterClass() throws Exception {
94      TEST_UTIL.shutdownMiniCluster();
95    }
96  
97    /**
98     * Test increment result when there are duplicate rpc request.
99     */
100   @Test
101   public void testDuplicateIncrement() throws Exception {
102     HTableDescriptor hdt = TEST_UTIL.createTableDescriptor("HCM-testDuplicateIncrement");
103     Map<String, String> kvs = new HashMap<String, String>();
104     kvs.put(HConnectionTestingUtility.SleepAtFirstRpcCall.SLEEP_TIME_CONF_KEY, "2000");
105     hdt.addCoprocessor(HConnectionTestingUtility.SleepAtFirstRpcCall.class.getName(), null, 1, kvs);
106     TEST_UTIL.createTable(hdt, new byte[][] { ROW }).close();
107 
108     Configuration c = new Configuration(TEST_UTIL.getConfiguration());
109     c.setInt(HConstants.HBASE_CLIENT_PAUSE, 50);
110     // Client will retry beacuse rpc timeout is small than the sleep time of first rpc call
111     c.setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 1500);
112 
113     Connection connection = ConnectionFactory.createConnection(c);
114     Table t = connection.getTable(TableName.valueOf("HCM-testDuplicateIncrement"));
115     if (t instanceof HTable) {
116       HTable table = (HTable) t;
117       table.setOperationTimeout(3 * 1000);
118 
119       try {
120         Increment inc = new Increment(ROW);
121         inc.addColumn(TEST_UTIL.fam1, QUALIFIER, 1);
122         Result result = table.increment(inc);
123 
124         Cell [] cells = result.rawCells();
125         assertEquals(1, cells.length);
126         assertIncrementKey(cells[0], ROW, TEST_UTIL.fam1, QUALIFIER, 1);
127 
128         // Verify expected result
129         Result readResult = table.get(new Get(ROW));
130         cells = readResult.rawCells();
131         assertEquals(1, cells.length);
132         assertIncrementKey(cells[0], ROW, TEST_UTIL.fam1, QUALIFIER, 1);
133       } finally {
134         table.close();
135         connection.close();
136       }
137     }
138   }
139 
140   @Test
141   public void testIncrementWithDeletes() throws Exception {
142     LOG.info("Starting " + this.name.getMethodName());
143     final TableName TABLENAME =
144         TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
145     Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
146     final byte[] COLUMN = Bytes.toBytes("column");
147 
148     ht.incrementColumnValue(ROW, FAMILY, COLUMN, 5);
149     TEST_UTIL.flush(TABLENAME);
150 
151     Delete del = new Delete(ROW);
152     ht.delete(del);
153 
154     ht.incrementColumnValue(ROW, FAMILY, COLUMN, 5);
155 
156     Get get = new Get(ROW);
157     Result r = ht.get(get);
158     assertEquals(1, r.size());
159     assertEquals(5, Bytes.toLong(r.getValue(FAMILY, COLUMN)));
160   }
161 
162   @Test
163   public void testIncrementingInvalidValue() throws Exception {
164     LOG.info("Starting " + this.name.getMethodName());
165     final TableName TABLENAME =
166         TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
167     Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
168     final byte[] COLUMN = Bytes.toBytes("column");
169     Put p = new Put(ROW);
170     // write an integer here (not a Long)
171     p.add(FAMILY, COLUMN, Bytes.toBytes(5));
172     ht.put(p);
173     try {
174       ht.incrementColumnValue(ROW, FAMILY, COLUMN, 5);
175       fail("Should have thrown DoNotRetryIOException");
176     } catch (DoNotRetryIOException iox) {
177       // success
178     }
179     Increment inc = new Increment(ROW);
180     inc.addColumn(FAMILY, COLUMN, 5);
181     try {
182       ht.increment(inc);
183       fail("Should have thrown DoNotRetryIOException");
184     } catch (DoNotRetryIOException iox) {
185       // success
186     }
187   }
188 
189   @Test
190   public void testIncrementInvalidArguments() throws Exception {
191     LOG.info("Starting " + this.name.getMethodName());
192     final TableName TABLENAME =
193         TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
194     Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
195     final byte[] COLUMN = Bytes.toBytes("column");
196     try {
197       // try null row
198       ht.incrementColumnValue(null, FAMILY, COLUMN, 5);
199       fail("Should have thrown IOException");
200     } catch (IOException iox) {
201       // success
202     }
203     try {
204       // try null family
205       ht.incrementColumnValue(ROW, null, COLUMN, 5);
206       fail("Should have thrown IOException");
207     } catch (IOException iox) {
208       // success
209     }
210     try {
211       // try null qualifier
212       ht.incrementColumnValue(ROW, FAMILY, null, 5);
213       fail("Should have thrown IOException");
214     } catch (IOException iox) {
215       // success
216     }
217     // try null row
218     try {
219       Increment incNoRow = new Increment((byte [])null);
220       incNoRow.addColumn(FAMILY, COLUMN, 5);
221       fail("Should have thrown IllegalArgumentException");
222     } catch (IllegalArgumentException iax) {
223       // success
224     } catch (NullPointerException npe) {
225       // success
226     }
227     // try null family
228     try {
229       Increment incNoFamily = new Increment(ROW);
230       incNoFamily.addColumn(null, COLUMN, 5);
231       fail("Should have thrown IllegalArgumentException");
232     } catch (IllegalArgumentException iax) {
233       // success
234     }
235     // try null qualifier
236     try {
237       Increment incNoQualifier = new Increment(ROW);
238       incNoQualifier.addColumn(FAMILY, null, 5);
239       fail("Should have thrown IllegalArgumentException");
240     } catch (IllegalArgumentException iax) {
241       // success
242     }
243   }
244 
245   @Test
246   public void testIncrementOutOfOrder() throws Exception {
247     LOG.info("Starting " + this.name.getMethodName());
248     final TableName TABLENAME =
249         TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
250     Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
251 
252     byte [][] QUALIFIERS = new byte [][] {
253       Bytes.toBytes("B"), Bytes.toBytes("A"), Bytes.toBytes("C")
254     };
255 
256     Increment inc = new Increment(ROW);
257     for (int i=0; i<QUALIFIERS.length; i++) {
258       inc.addColumn(FAMILY, QUALIFIERS[i], 1);
259     }
260     ht.increment(inc);
261 
262     // Verify expected results
263     Get get = new Get(ROW);
264     Result r = ht.get(get);
265     Cell [] kvs = r.rawCells();
266     assertEquals(3, kvs.length);
267     assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[1], 1);
268     assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[0], 1);
269     assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 1);
270 
271     // Now try multiple columns again
272     inc = new Increment(ROW);
273     for (int i=0; i<QUALIFIERS.length; i++) {
274       inc.addColumn(FAMILY, QUALIFIERS[i], 1);
275     }
276     ht.increment(inc);
277 
278     // Verify
279     r = ht.get(get);
280     kvs = r.rawCells();
281     assertEquals(3, kvs.length);
282     assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[1], 2);
283     assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[0], 2);
284     assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 2);
285   }
286 
287   @Test
288   public void testIncrementOnSameColumn() throws Exception {
289     LOG.info("Starting " + this.name.getMethodName());
290     final byte[] TABLENAME = Bytes.toBytes(filterStringSoTableNameSafe(this.name.getMethodName()));
291     HTable ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
292 
293     byte[][] QUALIFIERS =
294         new byte[][] { Bytes.toBytes("A"), Bytes.toBytes("B"), Bytes.toBytes("C") };
295 
296     Increment inc = new Increment(ROW);
297     for (int i = 0; i < QUALIFIERS.length; i++) {
298       inc.addColumn(FAMILY, QUALIFIERS[i], 1);
299       inc.addColumn(FAMILY, QUALIFIERS[i], 1);
300     }
301     ht.increment(inc);
302 
303     // Verify expected results
304     Get get = new Get(ROW);
305     Result r = ht.get(get);
306     Cell[] kvs = r.rawCells();
307     assertEquals(3, kvs.length);
308     assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[0], 1);
309     assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[1], 1);
310     assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 1);
311 
312     // Now try multiple columns again
313     inc = new Increment(ROW);
314     for (int i = 0; i < QUALIFIERS.length; i++) {
315       inc.addColumn(FAMILY, QUALIFIERS[i], 1);
316       inc.addColumn(FAMILY, QUALIFIERS[i], 1);
317     }
318     ht.increment(inc);
319 
320     // Verify
321     r = ht.get(get);
322     kvs = r.rawCells();
323     assertEquals(3, kvs.length);
324     assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[0], 2);
325     assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[1], 2);
326     assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 2);
327 
328     ht.close();
329   }
330 
331   @Test
332   public void testIncrement() throws Exception {
333     LOG.info("Starting " + this.name.getMethodName());
334     final TableName TABLENAME =
335         TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
336     Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
337 
338     byte [][] ROWS = new byte [][] {
339         Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c"),
340         Bytes.toBytes("d"), Bytes.toBytes("e"), Bytes.toBytes("f"),
341         Bytes.toBytes("g"), Bytes.toBytes("h"), Bytes.toBytes("i")
342     };
343     byte [][] QUALIFIERS = new byte [][] {
344         Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c"),
345         Bytes.toBytes("d"), Bytes.toBytes("e"), Bytes.toBytes("f"),
346         Bytes.toBytes("g"), Bytes.toBytes("h"), Bytes.toBytes("i")
347     };
348 
349     // Do some simple single-column increments
350 
351     // First with old API
352     ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[0], 1);
353     ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[1], 2);
354     ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[2], 3);
355     ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[3], 4);
356 
357     // Now increment things incremented with old and do some new
358     Increment inc = new Increment(ROW);
359     inc.addColumn(FAMILY, QUALIFIERS[1], 1);
360     inc.addColumn(FAMILY, QUALIFIERS[3], 1);
361     inc.addColumn(FAMILY, QUALIFIERS[4], 1);
362     ht.increment(inc);
363 
364     // Verify expected results
365     Get get = new Get(ROW);
366     Result r = ht.get(get);
367     Cell [] kvs = r.rawCells();
368     assertEquals(5, kvs.length);
369     assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[0], 1);
370     assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[1], 3);
371     assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 3);
372     assertIncrementKey(kvs[3], ROW, FAMILY, QUALIFIERS[3], 5);
373     assertIncrementKey(kvs[4], ROW, FAMILY, QUALIFIERS[4], 1);
374 
375     // Now try multiple columns by different amounts
376     inc = new Increment(ROWS[0]);
377     for (int i=0;i<QUALIFIERS.length;i++) {
378       inc.addColumn(FAMILY, QUALIFIERS[i], i+1);
379     }
380     ht.increment(inc);
381     // Verify
382     get = new Get(ROWS[0]);
383     r = ht.get(get);
384     kvs = r.rawCells();
385     assertEquals(QUALIFIERS.length, kvs.length);
386     for (int i=0;i<QUALIFIERS.length;i++) {
387       assertIncrementKey(kvs[i], ROWS[0], FAMILY, QUALIFIERS[i], i+1);
388     }
389 
390     // Re-increment them
391     inc = new Increment(ROWS[0]);
392     for (int i=0;i<QUALIFIERS.length;i++) {
393       inc.addColumn(FAMILY, QUALIFIERS[i], i+1);
394     }
395     ht.increment(inc);
396     // Verify
397     r = ht.get(get);
398     kvs = r.rawCells();
399     assertEquals(QUALIFIERS.length, kvs.length);
400     for (int i=0;i<QUALIFIERS.length;i++) {
401       assertIncrementKey(kvs[i], ROWS[0], FAMILY, QUALIFIERS[i], 2*(i+1));
402     }
403 
404     // Verify that an Increment of an amount of zero, returns current count; i.e. same as for above
405     // test, that is: 2 * (i + 1).
406     inc = new Increment(ROWS[0]);
407     for (int i = 0; i < QUALIFIERS.length; i++) {
408       inc.addColumn(FAMILY, QUALIFIERS[i], 0);
409     }
410     ht.increment(inc);
411     r = ht.get(get);
412     kvs = r.rawCells();
413     assertEquals(QUALIFIERS.length, kvs.length);
414     for (int i = 0; i < QUALIFIERS.length; i++) {
415       assertIncrementKey(kvs[i], ROWS[0], FAMILY, QUALIFIERS[i], 2*(i+1));
416     }
417   }
418 
419 
420   /**
421    * Call over to the adjacent class's method of same name.
422    */
423   static void assertIncrementKey(Cell key, byte [] row, byte [] family,
424       byte [] qualifier, long value) throws Exception {
425     TestFromClientSide.assertIncrementKey(key, row, family, qualifier, value);
426   }
427 
428   public static String filterStringSoTableNameSafe(final String str) {
429     return str.replaceAll("\\[fast\\=(.*)\\]", ".FAST.is.$1");
430   }
431 }