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  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.hbase.Cell;
31  import org.apache.hadoop.hbase.DoNotRetryIOException;
32  import org.apache.hadoop.hbase.HBaseTestingUtility;
33  import org.apache.hadoop.hbase.TableName;
34  import org.apache.hadoop.hbase.testclassification.LargeTests;
35  import org.apache.hadoop.hbase.util.Bytes;
36  import org.junit.AfterClass;
37  import org.junit.BeforeClass;
38  import org.junit.Rule;
39  import org.junit.Test;
40  import org.junit.experimental.categories.Category;
41  import org.junit.rules.TestName;
42  
43  /**
44   * Run Increment tests that use the HBase clients; {@link HTable}.
45   *
46   * Test is parameterized to run the slow and fast increment code paths. If fast, in the @before, we
47   * do a rolling restart of the single regionserver so that it can pick up the go fast configuration.
48   * Doing it this way should be faster than starting/stopping a cluster per test.
49   *
50   * Test takes a long time because spin up a cluster between each run -- ugh.
51   */
52  @Category(LargeTests.class)
53  @SuppressWarnings ("deprecation")
54  public class TestIncrementsFromClientSide {
55    final Log LOG = LogFactory.getLog(getClass());
56    protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
57    private static byte [] ROW = Bytes.toBytes("testRow");
58    private static byte [] FAMILY = Bytes.toBytes("testFamily");
59    // This test depends on there being only one slave running at at a time. See the @Before
60    // method where we do rolling restart.
61    protected static int SLAVES = 1;
62    @Rule public TestName name = new TestName();
63    public static Collection<Object []> data() {
64      return Arrays.asList(new Object[] {Boolean.FALSE}, new Object [] {Boolean.TRUE});
65    }
66  
67    @BeforeClass
68    public static void beforeClass() throws Exception {
69      TEST_UTIL.startMiniCluster();
70    }
71  
72    @AfterClass
73    public static void afterClass() throws Exception {
74      TEST_UTIL.shutdownMiniCluster();
75    }
76  
77    @Test
78    public void testIncrementWithDeletes() throws Exception {
79      LOG.info("Starting " + this.name.getMethodName());
80      final TableName TABLENAME =
81          TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
82      Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
83      final byte[] COLUMN = Bytes.toBytes("column");
84  
85      ht.incrementColumnValue(ROW, FAMILY, COLUMN, 5);
86      TEST_UTIL.flush(TABLENAME);
87  
88      Delete del = new Delete(ROW);
89      ht.delete(del);
90  
91      ht.incrementColumnValue(ROW, FAMILY, COLUMN, 5);
92  
93      Get get = new Get(ROW);
94      Result r = ht.get(get);
95      assertEquals(1, r.size());
96      assertEquals(5, Bytes.toLong(r.getValue(FAMILY, COLUMN)));
97    }
98  
99    @Test
100   public void testIncrementingInvalidValue() throws Exception {
101     LOG.info("Starting " + this.name.getMethodName());
102     final TableName TABLENAME =
103         TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
104     Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
105     final byte[] COLUMN = Bytes.toBytes("column");
106     Put p = new Put(ROW);
107     // write an integer here (not a Long)
108     p.add(FAMILY, COLUMN, Bytes.toBytes(5));
109     ht.put(p);
110     try {
111       ht.incrementColumnValue(ROW, FAMILY, COLUMN, 5);
112       fail("Should have thrown DoNotRetryIOException");
113     } catch (DoNotRetryIOException iox) {
114       // success
115     }
116     Increment inc = new Increment(ROW);
117     inc.addColumn(FAMILY, COLUMN, 5);
118     try {
119       ht.increment(inc);
120       fail("Should have thrown DoNotRetryIOException");
121     } catch (DoNotRetryIOException iox) {
122       // success
123     }
124   }
125 
126   @Test
127   public void testIncrementInvalidArguments() throws Exception {
128     LOG.info("Starting " + this.name.getMethodName());
129     final TableName TABLENAME =
130         TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
131     Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
132     final byte[] COLUMN = Bytes.toBytes("column");
133     try {
134       // try null row
135       ht.incrementColumnValue(null, FAMILY, COLUMN, 5);
136       fail("Should have thrown IOException");
137     } catch (IOException iox) {
138       // success
139     }
140     try {
141       // try null family
142       ht.incrementColumnValue(ROW, null, COLUMN, 5);
143       fail("Should have thrown IOException");
144     } catch (IOException iox) {
145       // success
146     }
147     try {
148       // try null qualifier
149       ht.incrementColumnValue(ROW, FAMILY, null, 5);
150       fail("Should have thrown IOException");
151     } catch (IOException iox) {
152       // success
153     }
154     // try null row
155     try {
156       Increment incNoRow = new Increment((byte [])null);
157       incNoRow.addColumn(FAMILY, COLUMN, 5);
158       fail("Should have thrown IllegalArgumentException");
159     } catch (IllegalArgumentException iax) {
160       // success
161     } catch (NullPointerException npe) {
162       // success
163     }
164     // try null family
165     try {
166       Increment incNoFamily = new Increment(ROW);
167       incNoFamily.addColumn(null, COLUMN, 5);
168       fail("Should have thrown IllegalArgumentException");
169     } catch (IllegalArgumentException iax) {
170       // success
171     }
172     // try null qualifier
173     try {
174       Increment incNoQualifier = new Increment(ROW);
175       incNoQualifier.addColumn(FAMILY, null, 5);
176       fail("Should have thrown IllegalArgumentException");
177     } catch (IllegalArgumentException iax) {
178       // success
179     }
180   }
181 
182   @Test
183   public void testIncrementOutOfOrder() throws Exception {
184     LOG.info("Starting " + this.name.getMethodName());
185     final TableName TABLENAME =
186         TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
187     Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
188 
189     byte [][] QUALIFIERS = new byte [][] {
190       Bytes.toBytes("B"), Bytes.toBytes("A"), Bytes.toBytes("C")
191     };
192 
193     Increment inc = new Increment(ROW);
194     for (int i=0; i<QUALIFIERS.length; i++) {
195       inc.addColumn(FAMILY, QUALIFIERS[i], 1);
196     }
197     ht.increment(inc);
198 
199     // Verify expected results
200     Get get = new Get(ROW);
201     Result r = ht.get(get);
202     Cell [] kvs = r.rawCells();
203     assertEquals(3, kvs.length);
204     assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[1], 1);
205     assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[0], 1);
206     assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 1);
207 
208     // Now try multiple columns again
209     inc = new Increment(ROW);
210     for (int i=0; i<QUALIFIERS.length; i++) {
211       inc.addColumn(FAMILY, QUALIFIERS[i], 1);
212     }
213     ht.increment(inc);
214 
215     // Verify
216     r = ht.get(get);
217     kvs = r.rawCells();
218     assertEquals(3, kvs.length);
219     assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[1], 2);
220     assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[0], 2);
221     assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 2);
222   }
223 
224   @Test
225   public void testIncrementOnSameColumn() throws Exception {
226     LOG.info("Starting " + this.name.getMethodName());
227     final byte[] TABLENAME = Bytes.toBytes(filterStringSoTableNameSafe(this.name.getMethodName()));
228     HTable ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
229 
230     byte[][] QUALIFIERS =
231         new byte[][] { Bytes.toBytes("A"), Bytes.toBytes("B"), Bytes.toBytes("C") };
232 
233     Increment inc = new Increment(ROW);
234     for (int i = 0; i < QUALIFIERS.length; i++) {
235       inc.addColumn(FAMILY, QUALIFIERS[i], 1);
236       inc.addColumn(FAMILY, QUALIFIERS[i], 1);
237     }
238     ht.increment(inc);
239 
240     // Verify expected results
241     Get get = new Get(ROW);
242     Result r = ht.get(get);
243     Cell[] kvs = r.rawCells();
244     assertEquals(3, kvs.length);
245     assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[0], 1);
246     assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[1], 1);
247     assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 1);
248 
249     // Now try multiple columns again
250     inc = new Increment(ROW);
251     for (int i = 0; i < QUALIFIERS.length; i++) {
252       inc.addColumn(FAMILY, QUALIFIERS[i], 1);
253       inc.addColumn(FAMILY, QUALIFIERS[i], 1);
254     }
255     ht.increment(inc);
256 
257     // Verify
258     r = ht.get(get);
259     kvs = r.rawCells();
260     assertEquals(3, kvs.length);
261     assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[0], 2);
262     assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[1], 2);
263     assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 2);
264 
265     ht.close();
266   }
267 
268   @Test
269   public void testIncrement() throws Exception {
270     LOG.info("Starting " + this.name.getMethodName());
271     final TableName TABLENAME =
272         TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
273     Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
274 
275     byte [][] ROWS = new byte [][] {
276         Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c"),
277         Bytes.toBytes("d"), Bytes.toBytes("e"), Bytes.toBytes("f"),
278         Bytes.toBytes("g"), Bytes.toBytes("h"), Bytes.toBytes("i")
279     };
280     byte [][] QUALIFIERS = new byte [][] {
281         Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c"),
282         Bytes.toBytes("d"), Bytes.toBytes("e"), Bytes.toBytes("f"),
283         Bytes.toBytes("g"), Bytes.toBytes("h"), Bytes.toBytes("i")
284     };
285 
286     // Do some simple single-column increments
287 
288     // First with old API
289     ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[0], 1);
290     ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[1], 2);
291     ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[2], 3);
292     ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[3], 4);
293 
294     // Now increment things incremented with old and do some new
295     Increment inc = new Increment(ROW);
296     inc.addColumn(FAMILY, QUALIFIERS[1], 1);
297     inc.addColumn(FAMILY, QUALIFIERS[3], 1);
298     inc.addColumn(FAMILY, QUALIFIERS[4], 1);
299     ht.increment(inc);
300 
301     // Verify expected results
302     Get get = new Get(ROW);
303     Result r = ht.get(get);
304     Cell [] kvs = r.rawCells();
305     assertEquals(5, kvs.length);
306     assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[0], 1);
307     assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[1], 3);
308     assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 3);
309     assertIncrementKey(kvs[3], ROW, FAMILY, QUALIFIERS[3], 5);
310     assertIncrementKey(kvs[4], ROW, FAMILY, QUALIFIERS[4], 1);
311 
312     // Now try multiple columns by different amounts
313     inc = new Increment(ROWS[0]);
314     for (int i=0;i<QUALIFIERS.length;i++) {
315       inc.addColumn(FAMILY, QUALIFIERS[i], i+1);
316     }
317     ht.increment(inc);
318     // Verify
319     get = new Get(ROWS[0]);
320     r = ht.get(get);
321     kvs = r.rawCells();
322     assertEquals(QUALIFIERS.length, kvs.length);
323     for (int i=0;i<QUALIFIERS.length;i++) {
324       assertIncrementKey(kvs[i], ROWS[0], FAMILY, QUALIFIERS[i], i+1);
325     }
326 
327     // Re-increment them
328     inc = new Increment(ROWS[0]);
329     for (int i=0;i<QUALIFIERS.length;i++) {
330       inc.addColumn(FAMILY, QUALIFIERS[i], i+1);
331     }
332     ht.increment(inc);
333     // Verify
334     r = ht.get(get);
335     kvs = r.rawCells();
336     assertEquals(QUALIFIERS.length, kvs.length);
337     for (int i=0;i<QUALIFIERS.length;i++) {
338       assertIncrementKey(kvs[i], ROWS[0], FAMILY, QUALIFIERS[i], 2*(i+1));
339     }
340 
341     // Verify that an Increment of an amount of zero, returns current count; i.e. same as for above
342     // test, that is: 2 * (i + 1).
343     inc = new Increment(ROWS[0]);
344     for (int i = 0; i < QUALIFIERS.length; i++) {
345       inc.addColumn(FAMILY, QUALIFIERS[i], 0);
346     }
347     ht.increment(inc);
348     r = ht.get(get);
349     kvs = r.rawCells();
350     assertEquals(QUALIFIERS.length, kvs.length);
351     for (int i = 0; i < QUALIFIERS.length; i++) {
352       assertIncrementKey(kvs[i], ROWS[0], FAMILY, QUALIFIERS[i], 2*(i+1));
353     }
354   }
355 
356 
357   /**
358    * Call over to the adjacent class's method of same name.
359    */
360   static void assertIncrementKey(Cell key, byte [] row, byte [] family,
361       byte [] qualifier, long value) throws Exception {
362     TestFromClientSide.assertIncrementKey(key, row, family, qualifier, value);
363   }
364 
365   public static String filterStringSoTableNameSafe(final String str) {
366     return str.replaceAll("\\[fast\\=(.*)\\]", ".FAST.is.$1");
367   }
368 }