001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import static org.apache.hadoop.hbase.HConstants.RPC_CODEC_CONF_KEY;
021import static org.apache.hadoop.hbase.ipc.RpcClient.DEFAULT_CODEC_CLASS;
022import static org.junit.Assert.assertEquals;
023import static org.junit.Assert.assertNotEquals;
024import static org.junit.Assert.assertNotNull;
025import static org.junit.Assert.assertTrue;
026import static org.junit.Assert.fail;
027
028import java.io.IOException;
029import java.util.ArrayList;
030import java.util.Collections;
031import java.util.HashMap;
032import java.util.List;
033import java.util.Map;
034import org.apache.hadoop.conf.Configuration;
035import org.apache.hadoop.hbase.Cell;
036import org.apache.hadoop.hbase.CellUtil;
037import org.apache.hadoop.hbase.DoNotRetryIOException;
038import org.apache.hadoop.hbase.HBaseClassTestRule;
039import org.apache.hadoop.hbase.HBaseTestingUtility;
040import org.apache.hadoop.hbase.HConstants;
041import org.apache.hadoop.hbase.HTableDescriptor;
042import org.apache.hadoop.hbase.KeyValue;
043import org.apache.hadoop.hbase.PrivateCellUtil;
044import org.apache.hadoop.hbase.TableName;
045import org.apache.hadoop.hbase.Tag;
046import org.apache.hadoop.hbase.TagType;
047import org.apache.hadoop.hbase.codec.KeyValueCodecWithTags;
048import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
049import org.apache.hadoop.hbase.coprocessor.MultiRowMutationEndpoint;
050import org.apache.hadoop.hbase.testclassification.LargeTests;
051import org.apache.hadoop.hbase.util.Bytes;
052import org.junit.AfterClass;
053import org.junit.BeforeClass;
054import org.junit.ClassRule;
055import org.junit.Rule;
056import org.junit.Test;
057import org.junit.experimental.categories.Category;
058import org.junit.rules.TestName;
059import org.slf4j.Logger;
060import org.slf4j.LoggerFactory;
061
062/**
063 * Run Increment tests that use the HBase clients; {@link TableBuilder}. Test is parameterized to
064 * run the slow and fast increment code paths. If fast, in the @before, we do a rolling restart of
065 * the single regionserver so that it can pick up the go fast configuration. Doing it this way
066 * should be faster than starting/stopping a cluster per test. Test takes a long time because spin
067 * up a cluster between each run -- ugh.
068 */
069@Category(LargeTests.class)
070public class TestIncrementsFromClientSide {
071
072  @ClassRule
073  public static final HBaseClassTestRule CLASS_RULE =
074    HBaseClassTestRule.forClass(TestIncrementsFromClientSide.class);
075
076  final Logger LOG = LoggerFactory.getLogger(getClass());
077  protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
078  private static byte[] ROW = Bytes.toBytes("testRow");
079  private static byte[] FAMILY = Bytes.toBytes("testFamily");
080  private static byte[] QUALIFIER = Bytes.toBytes("testQualifier");
081  // This test depends on there being only one slave running at at a time. See the @Before
082  // method where we do rolling restart.
083  protected static int SLAVES = 1;
084  @Rule
085  public TestName name = new TestName();
086
087  @BeforeClass
088  public static void beforeClass() throws Exception {
089    Configuration conf = TEST_UTIL.getConfiguration();
090    conf.setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
091      MultiRowMutationEndpoint.class.getName());
092    // We need more than one region server in this test
093    TEST_UTIL.startMiniCluster(SLAVES);
094  }
095
096  /**
097   * @throws java.lang.Exception
098   */
099  @AfterClass
100  public static void afterClass() throws Exception {
101    TEST_UTIL.shutdownMiniCluster();
102  }
103
104  /**
105   * Test increment result when there are duplicate rpc request.
106   */
107  @Test
108  public void testDuplicateIncrement() throws Exception {
109    HTableDescriptor hdt = TEST_UTIL.createTableDescriptor(TableName.valueOf(name.getMethodName()));
110    Map<String, String> kvs = new HashMap<>();
111    kvs.put(HConnectionTestingUtility.SleepAtFirstRpcCall.SLEEP_TIME_CONF_KEY, "2000");
112    hdt.addCoprocessor(HConnectionTestingUtility.SleepAtFirstRpcCall.class.getName(), null, 1, kvs);
113    TEST_UTIL.createTable(hdt, new byte[][] { ROW }).close();
114
115    Configuration c = new Configuration(TEST_UTIL.getConfiguration());
116    c.setInt(HConstants.HBASE_CLIENT_PAUSE, 50);
117    // Client will retry beacuse rpc timeout is small than the sleep time of first rpc call
118    c.setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 1500);
119
120    Connection connection = ConnectionFactory.createConnection(c);
121    Table t = connection.getTable(TableName.valueOf(name.getMethodName()));
122    if (t instanceof HTable) {
123      HTable table = (HTable) t;
124      table.setOperationTimeout(3 * 1000);
125
126      try {
127        Increment inc = new Increment(ROW);
128        inc.addColumn(TEST_UTIL.fam1, QUALIFIER, 1);
129        Result result = table.increment(inc);
130
131        Cell[] cells = result.rawCells();
132        assertEquals(1, cells.length);
133        assertIncrementKey(cells[0], ROW, TEST_UTIL.fam1, QUALIFIER, 1);
134
135        // Verify expected result
136        Result readResult = table.get(new Get(ROW));
137        cells = readResult.rawCells();
138        assertEquals(1, cells.length);
139        assertIncrementKey(cells[0], ROW, TEST_UTIL.fam1, QUALIFIER, 1);
140      } finally {
141        table.close();
142        connection.close();
143      }
144    }
145  }
146
147  /**
148   * Test batch increment result when there are duplicate rpc request.
149   */
150  @Test
151  public void testDuplicateBatchIncrement() throws Exception {
152    HTableDescriptor hdt = TEST_UTIL.createTableDescriptor(TableName.valueOf(name.getMethodName()));
153    Map<String, String> kvs = new HashMap<>();
154    kvs.put(HConnectionTestingUtility.SleepAtFirstRpcCall.SLEEP_TIME_CONF_KEY, "2000");
155    hdt.addCoprocessor(HConnectionTestingUtility.SleepAtFirstRpcCall.class.getName(), null, 1, kvs);
156    TEST_UTIL.createTable(hdt, new byte[][] { ROW }).close();
157
158    Configuration c = new Configuration(TEST_UTIL.getConfiguration());
159    c.setInt(HConstants.HBASE_CLIENT_PAUSE, 50);
160    // Client will retry beacuse rpc timeout is small than the sleep time of first rpc call
161    c.setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 1500);
162
163    try (Connection connection = ConnectionFactory.createConnection(c);
164      Table table = connection.getTableBuilder(TableName.valueOf(name.getMethodName()), null)
165        .setOperationTimeout(3 * 1000).build()) {
166      Increment inc = new Increment(ROW);
167      inc.addColumn(HBaseTestingUtility.fam1, QUALIFIER, 1);
168
169      // Batch increment
170      Object[] results = new Object[1];
171      table.batch(Collections.singletonList(inc), results);
172
173      Cell[] cells = ((Result) results[0]).rawCells();
174      assertEquals(1, cells.length);
175      assertIncrementKey(cells[0], ROW, HBaseTestingUtility.fam1, QUALIFIER, 1);
176
177      // Verify expected result
178      Result readResult = table.get(new Get(ROW));
179      cells = readResult.rawCells();
180      assertEquals(1, cells.length);
181      assertIncrementKey(cells[0], ROW, HBaseTestingUtility.fam1, QUALIFIER, 1);
182    }
183  }
184
185  @Test
186  public void testIncrementWithDeletes() throws Exception {
187    LOG.info("Starting " + this.name.getMethodName());
188    final TableName TABLENAME =
189      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
190    Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
191    final byte[] COLUMN = Bytes.toBytes("column");
192
193    ht.incrementColumnValue(ROW, FAMILY, COLUMN, 5);
194    TEST_UTIL.flush(TABLENAME);
195
196    Delete del = new Delete(ROW);
197    ht.delete(del);
198
199    ht.incrementColumnValue(ROW, FAMILY, COLUMN, 5);
200
201    Get get = new Get(ROW);
202    Result r = ht.get(get);
203    assertEquals(1, r.size());
204    assertEquals(5, Bytes.toLong(r.getValue(FAMILY, COLUMN)));
205  }
206
207  @Test
208  public void testIncrementingInvalidValue() throws Exception {
209    LOG.info("Starting " + this.name.getMethodName());
210    final TableName TABLENAME =
211      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
212    Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
213    final byte[] COLUMN = Bytes.toBytes("column");
214    Put p = new Put(ROW);
215    // write an integer here (not a Long)
216    p.addColumn(FAMILY, COLUMN, Bytes.toBytes(5));
217    ht.put(p);
218    try {
219      ht.incrementColumnValue(ROW, FAMILY, COLUMN, 5);
220      fail("Should have thrown DoNotRetryIOException");
221    } catch (DoNotRetryIOException iox) {
222      // success
223    }
224    Increment inc = new Increment(ROW);
225    inc.addColumn(FAMILY, COLUMN, 5);
226    try {
227      ht.increment(inc);
228      fail("Should have thrown DoNotRetryIOException");
229    } catch (DoNotRetryIOException iox) {
230      // success
231    }
232  }
233
234  @Test
235  public void testBatchIncrementsWithReturnResultFalse() throws Exception {
236    LOG.info("Starting testBatchIncrementsWithReturnResultFalse");
237    final TableName tableName = TableName.valueOf(name.getMethodName());
238    Table table = TEST_UTIL.createTable(tableName, FAMILY);
239    Increment inc1 = new Increment(Bytes.toBytes("row2"));
240    inc1.setReturnResults(false);
241    inc1.addColumn(FAMILY, Bytes.toBytes("f1"), 1);
242    Increment inc2 = new Increment(Bytes.toBytes("row2"));
243    inc2.setReturnResults(false);
244    inc2.addColumn(FAMILY, Bytes.toBytes("f1"), 1);
245    List<Increment> incs = new ArrayList<>();
246    incs.add(inc1);
247    incs.add(inc2);
248    Object[] results = new Object[2];
249    table.batch(incs, results);
250    assertTrue(results.length == 2);
251    for (Object r : results) {
252      Result result = (Result) r;
253      assertTrue(result.isEmpty());
254    }
255    table.close();
256  }
257
258  @Test
259  public void testIncrementInvalidArguments() throws Exception {
260    LOG.info("Starting " + this.name.getMethodName());
261    final TableName TABLENAME =
262      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
263    Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
264    final byte[] COLUMN = Bytes.toBytes("column");
265    try {
266      // try null row
267      ht.incrementColumnValue(null, FAMILY, COLUMN, 5);
268      fail("Should have thrown IOException");
269    } catch (IOException iox) {
270      // success
271    }
272    try {
273      // try null family
274      ht.incrementColumnValue(ROW, null, COLUMN, 5);
275      fail("Should have thrown IOException");
276    } catch (IOException iox) {
277      // success
278    }
279    // try null row
280    try {
281      Increment incNoRow = new Increment((byte[]) null);
282      incNoRow.addColumn(FAMILY, COLUMN, 5);
283      fail("Should have thrown IllegalArgumentException");
284    } catch (IllegalArgumentException iax) {
285      // success
286    } catch (NullPointerException npe) {
287      // success
288    }
289    // try null family
290    try {
291      Increment incNoFamily = new Increment(ROW);
292      incNoFamily.addColumn(null, COLUMN, 5);
293      fail("Should have thrown IllegalArgumentException");
294    } catch (IllegalArgumentException iax) {
295      // success
296    }
297  }
298
299  @Test
300  public void testIncrementOutOfOrder() throws Exception {
301    LOG.info("Starting " + this.name.getMethodName());
302    final TableName TABLENAME =
303      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
304    Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
305
306    byte[][] QUALIFIERS =
307      new byte[][] { Bytes.toBytes("B"), Bytes.toBytes("A"), Bytes.toBytes("C") };
308
309    Increment inc = new Increment(ROW);
310    for (int i = 0; i < QUALIFIERS.length; i++) {
311      inc.addColumn(FAMILY, QUALIFIERS[i], 1);
312    }
313    ht.increment(inc);
314
315    // Verify expected results
316    Get get = new Get(ROW);
317    Result r = ht.get(get);
318    Cell[] kvs = r.rawCells();
319    assertEquals(3, kvs.length);
320    assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[1], 1);
321    assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[0], 1);
322    assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 1);
323
324    // Now try multiple columns again
325    inc = new Increment(ROW);
326    for (int i = 0; i < QUALIFIERS.length; i++) {
327      inc.addColumn(FAMILY, QUALIFIERS[i], 1);
328    }
329    ht.increment(inc);
330
331    // Verify
332    r = ht.get(get);
333    kvs = r.rawCells();
334    assertEquals(3, kvs.length);
335    assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[1], 2);
336    assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[0], 2);
337    assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 2);
338  }
339
340  @Test
341  public void testIncrementOnSameColumn() throws Exception {
342    LOG.info("Starting " + this.name.getMethodName());
343    final byte[] TABLENAME = Bytes.toBytes(filterStringSoTableNameSafe(this.name.getMethodName()));
344    Table ht = TEST_UTIL.createTable(TableName.valueOf(TABLENAME), FAMILY);
345
346    byte[][] QUALIFIERS =
347      new byte[][] { Bytes.toBytes("A"), Bytes.toBytes("B"), Bytes.toBytes("C") };
348
349    Increment inc = new Increment(ROW);
350    for (int i = 0; i < QUALIFIERS.length; i++) {
351      inc.addColumn(FAMILY, QUALIFIERS[i], 1);
352      inc.addColumn(FAMILY, QUALIFIERS[i], 1);
353    }
354    ht.increment(inc);
355
356    // Verify expected results
357    Get get = new Get(ROW);
358    Result r = ht.get(get);
359    Cell[] kvs = r.rawCells();
360    assertEquals(3, kvs.length);
361    assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[0], 1);
362    assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[1], 1);
363    assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 1);
364
365    // Now try multiple columns again
366    inc = new Increment(ROW);
367    for (int i = 0; i < QUALIFIERS.length; i++) {
368      inc.addColumn(FAMILY, QUALIFIERS[i], 1);
369      inc.addColumn(FAMILY, QUALIFIERS[i], 1);
370    }
371    ht.increment(inc);
372
373    // Verify
374    r = ht.get(get);
375    kvs = r.rawCells();
376    assertEquals(3, kvs.length);
377    assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[0], 2);
378    assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[1], 2);
379    assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 2);
380
381    ht.close();
382  }
383
384  @Test
385  public void testIncrementIncrZeroAtFirst() throws Exception {
386    LOG.info("Starting " + this.name.getMethodName());
387    final TableName TABLENAME =
388      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
389    Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
390
391    byte[] col1 = Bytes.toBytes("col1");
392    byte[] col2 = Bytes.toBytes("col2");
393    byte[] col3 = Bytes.toBytes("col3");
394
395    // Now increment zero at first time incr
396    Increment inc = new Increment(ROW);
397    inc.addColumn(FAMILY, col1, 0);
398    ht.increment(inc);
399
400    // Verify expected results
401    Get get = new Get(ROW);
402    Result r = ht.get(get);
403    Cell[] kvs = r.rawCells();
404    assertEquals(1, kvs.length);
405    assertNotNull(kvs[0]);
406    assertIncrementKey(kvs[0], ROW, FAMILY, col1, 0);
407
408    // Now try multiple columns by different amounts
409    inc = new Increment(ROW);
410    inc.addColumn(FAMILY, col1, 1);
411    inc.addColumn(FAMILY, col2, 0);
412    inc.addColumn(FAMILY, col3, 2);
413    ht.increment(inc);
414    // Verify
415    get = new Get(ROW);
416    r = ht.get(get);
417    kvs = r.rawCells();
418    assertEquals(3, kvs.length);
419    assertNotNull(kvs[0]);
420    assertNotNull(kvs[1]);
421    assertNotNull(kvs[2]);
422    assertIncrementKey(kvs[0], ROW, FAMILY, col1, 1);
423    assertIncrementKey(kvs[1], ROW, FAMILY, col2, 0);
424    assertIncrementKey(kvs[2], ROW, FAMILY, col3, 2);
425  }
426
427  @Test
428  public void testIncrement() throws Exception {
429    LOG.info("Starting " + this.name.getMethodName());
430    final TableName TABLENAME =
431      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
432    Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
433
434    byte[][] ROWS = new byte[][] { Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c"),
435      Bytes.toBytes("d"), Bytes.toBytes("e"), Bytes.toBytes("f"), Bytes.toBytes("g"),
436      Bytes.toBytes("h"), Bytes.toBytes("i") };
437    byte[][] QUALIFIERS = new byte[][] { Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c"),
438      Bytes.toBytes("d"), Bytes.toBytes("e"), Bytes.toBytes("f"), Bytes.toBytes("g"),
439      Bytes.toBytes("h"), Bytes.toBytes("i") };
440
441    // Do some simple single-column increments
442
443    // First with old API
444    ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[0], 1);
445    ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[1], 2);
446    ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[2], 3);
447    ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[3], 4);
448
449    // Now increment things incremented with old and do some new
450    Increment inc = new Increment(ROW);
451    inc.addColumn(FAMILY, QUALIFIERS[1], 1);
452    inc.addColumn(FAMILY, QUALIFIERS[3], 1);
453    inc.addColumn(FAMILY, QUALIFIERS[4], 1);
454    ht.increment(inc);
455
456    // Verify expected results
457    Get get = new Get(ROW);
458    Result r = ht.get(get);
459    Cell[] kvs = r.rawCells();
460    assertEquals(5, kvs.length);
461    assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[0], 1);
462    assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[1], 3);
463    assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 3);
464    assertIncrementKey(kvs[3], ROW, FAMILY, QUALIFIERS[3], 5);
465    assertIncrementKey(kvs[4], ROW, FAMILY, QUALIFIERS[4], 1);
466
467    // Now try multiple columns by different amounts
468    inc = new Increment(ROWS[0]);
469    for (int i = 0; i < QUALIFIERS.length; i++) {
470      inc.addColumn(FAMILY, QUALIFIERS[i], i + 1);
471    }
472    ht.increment(inc);
473    // Verify
474    get = new Get(ROWS[0]);
475    r = ht.get(get);
476    kvs = r.rawCells();
477    assertEquals(QUALIFIERS.length, kvs.length);
478    for (int i = 0; i < QUALIFIERS.length; i++) {
479      assertIncrementKey(kvs[i], ROWS[0], FAMILY, QUALIFIERS[i], i + 1);
480    }
481
482    // Re-increment them
483    inc = new Increment(ROWS[0]);
484    for (int i = 0; i < QUALIFIERS.length; i++) {
485      inc.addColumn(FAMILY, QUALIFIERS[i], i + 1);
486    }
487    ht.increment(inc);
488    // Verify
489    r = ht.get(get);
490    kvs = r.rawCells();
491    assertEquals(QUALIFIERS.length, kvs.length);
492    for (int i = 0; i < QUALIFIERS.length; i++) {
493      assertIncrementKey(kvs[i], ROWS[0], FAMILY, QUALIFIERS[i], 2 * (i + 1));
494    }
495
496    // Verify that an Increment of an amount of zero, returns current count; i.e. same as for above
497    // test, that is: 2 * (i + 1).
498    inc = new Increment(ROWS[0]);
499    for (int i = 0; i < QUALIFIERS.length; i++) {
500      inc.addColumn(FAMILY, QUALIFIERS[i], 0);
501    }
502    ht.increment(inc);
503    r = ht.get(get);
504    kvs = r.rawCells();
505    assertEquals(QUALIFIERS.length, kvs.length);
506    for (int i = 0; i < QUALIFIERS.length; i++) {
507      assertIncrementKey(kvs[i], ROWS[0], FAMILY, QUALIFIERS[i], 2 * (i + 1));
508    }
509  }
510
511  @Test
512  public void testIncrementWithCustomTimestamp() throws IOException {
513    TableName TABLENAME = TableName.valueOf(name.getMethodName());
514    Table table = TEST_UTIL.createTable(TABLENAME, FAMILY);
515    long timestamp = 999;
516    Increment increment = new Increment(ROW);
517    increment.add(CellUtil.createCell(ROW, FAMILY, QUALIFIER, timestamp,
518      KeyValue.Type.Put.getCode(), Bytes.toBytes(100L)));
519    Result r = table.increment(increment);
520    assertEquals(1, r.size());
521    assertEquals(timestamp, r.rawCells()[0].getTimestamp());
522    r = table.get(new Get(ROW));
523    assertEquals(1, r.size());
524    assertEquals(timestamp, r.rawCells()[0].getTimestamp());
525    r = table.increment(increment);
526    assertEquals(1, r.size());
527    assertNotEquals(timestamp, r.rawCells()[0].getTimestamp());
528    r = table.get(new Get(ROW));
529    assertEquals(1, r.size());
530    assertNotEquals(timestamp, r.rawCells()[0].getTimestamp());
531  }
532
533  /**
534   * Call over to the adjacent class's method of same name.
535   */
536  static void assertIncrementKey(Cell key, byte[] row, byte[] family, byte[] qualifier, long value)
537    throws Exception {
538    TestFromClientSide.assertIncrementKey(key, row, family, qualifier, value);
539  }
540
541  public static String filterStringSoTableNameSafe(final String str) {
542    return str.replaceAll("\\[fast\\=(.*)\\]", ".FAST.is.$1");
543  }
544
545  /*
546   * Test that we have only 1 ttl tag with increment mutation.
547   */
548  @Test
549  public void testIncrementWithTtlTags() throws Exception {
550    LOG.info("Starting " + this.name.getMethodName());
551    final TableName tableName =
552      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
553    Table ht = TEST_UTIL.createTable(tableName, FAMILY);
554    final byte[] COLUMN = Bytes.toBytes("column");
555
556    Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
557    // Set RPC_CODEC_CONF_KEY to KeyValueCodecWithTags so that scan will return tags.
558    conf.set(RPC_CODEC_CONF_KEY, KeyValueCodecWithTags.class.getName());
559    conf.set(DEFAULT_CODEC_CLASS, "");
560    try (Connection connection = ConnectionFactory.createConnection(conf);
561      Table table = connection.getTable(tableName)) {
562      for (int i = 0; i < 10; i++) {
563        Increment inc = new Increment(ROW);
564        inc.addColumn(FAMILY, COLUMN, 1);
565        long ttl = i + 3600000;
566        inc.setTTL(ttl);
567        ht.increment(inc);
568
569        Scan scan = new Scan().withStartRow(ROW);
570        ResultScanner scanner = table.getScanner(scan);
571        int count = 0;
572        Result result;
573        while ((result = scanner.next()) != null) {
574          Cell[] cells = result.rawCells();
575          for (Cell cell : cells) {
576            List<Tag> tags = PrivateCellUtil.getTags(cell);
577            // Make sure there is only 1 tag.
578            assertEquals(1, tags.size());
579            Tag tag = tags.get(0);
580            assertEquals(TagType.TTL_TAG_TYPE, tag.getType());
581            long ttlTagValue = Bytes.toLong(tag.getValueArray(), tag.getValueOffset());
582            assertEquals(ttl, ttlTagValue);
583          }
584          count++;
585        }
586        // Make sure there is only 1 result.
587        assertEquals(1, count);
588      }
589    }
590  }
591}