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