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.CellBuilderType;
037import org.apache.hadoop.hbase.DoNotRetryIOException;
038import org.apache.hadoop.hbase.ExtendedCellBuilderFactory;
039import org.apache.hadoop.hbase.HBaseClassTestRule;
040import org.apache.hadoop.hbase.HBaseTestingUtil;
041import org.apache.hadoop.hbase.HConstants;
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 HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
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    TableDescriptorBuilder builder =
110      TEST_UTIL.createModifyableTableDescriptor(name.getMethodName());
111    Map<String, String> kvs = new HashMap<>();
112    kvs.put(SleepAtFirstRpcCall.SLEEP_TIME_CONF_KEY, "2000");
113    builder.setCoprocessor(CoprocessorDescriptorBuilder
114      .newBuilder(SleepAtFirstRpcCall.class.getName()).setPriority(1).setProperties(kvs).build());
115    TEST_UTIL.createTable(builder.build(), new byte[][] { ROW }).close();
116
117    Configuration c = new Configuration(TEST_UTIL.getConfiguration());
118    c.setInt(HConstants.HBASE_CLIENT_PAUSE, 50);
119    // Client will retry beacuse rpc timeout is small than the sleep time of first rpc call
120    c.setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 1500);
121
122    try (Connection connection = ConnectionFactory.createConnection(c);
123      Table table = connection.getTableBuilder(TableName.valueOf(name.getMethodName()), null)
124        .setOperationTimeout(3 * 1000).build()) {
125      Increment inc = new Increment(ROW);
126      inc.addColumn(HBaseTestingUtil.fam1, QUALIFIER, 1);
127      Result result = table.increment(inc);
128
129      Cell[] cells = result.rawCells();
130      assertEquals(1, cells.length);
131      assertIncrementKey(cells[0], ROW, HBaseTestingUtil.fam1, QUALIFIER, 1);
132
133      // Verify expected result
134      Result readResult = table.get(new Get(ROW));
135      cells = readResult.rawCells();
136      assertEquals(1, cells.length);
137      assertIncrementKey(cells[0], ROW, HBaseTestingUtil.fam1, QUALIFIER, 1);
138    }
139  }
140
141  /**
142   * Test batch increment result when there are duplicate rpc request.
143   */
144  @Test
145  public void testDuplicateBatchIncrement() throws Exception {
146    TableDescriptorBuilder builder =
147      TEST_UTIL.createModifyableTableDescriptor(name.getMethodName());
148    Map<String, String> kvs = new HashMap<>();
149    kvs.put(SleepAtFirstRpcCall.SLEEP_TIME_CONF_KEY, "2000");
150    builder.setCoprocessor(CoprocessorDescriptorBuilder
151      .newBuilder(SleepAtFirstRpcCall.class.getName()).setPriority(1).setProperties(kvs).build());
152    TEST_UTIL.createTable(builder.build(), new byte[][] { ROW }).close();
153
154    Configuration c = new Configuration(TEST_UTIL.getConfiguration());
155    c.setInt(HConstants.HBASE_CLIENT_PAUSE, 50);
156    // Client will retry beacuse rpc timeout is small than the sleep time of first rpc call
157    c.setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 1500);
158
159    try (Connection connection = ConnectionFactory.createConnection(c);
160      Table table = connection.getTableBuilder(TableName.valueOf(name.getMethodName()), null)
161        .setOperationTimeout(3 * 1000).build()) {
162      Increment inc = new Increment(ROW);
163      inc.addColumn(HBaseTestingUtil.fam1, QUALIFIER, 1);
164
165      // Batch increment
166      Object[] results = new Object[1];
167      table.batch(Collections.singletonList(inc), results);
168
169      Cell[] cells = ((Result) results[0]).rawCells();
170      assertEquals(1, cells.length);
171      assertIncrementKey(cells[0], ROW, HBaseTestingUtil.fam1, QUALIFIER, 1);
172
173      // Verify expected result
174      Result readResult = table.get(new Get(ROW));
175      cells = readResult.rawCells();
176      assertEquals(1, cells.length);
177      assertIncrementKey(cells[0], ROW, HBaseTestingUtil.fam1, QUALIFIER, 1);
178    }
179  }
180
181  @Test
182  public void testIncrementWithDeletes() throws Exception {
183    LOG.info("Starting " + this.name.getMethodName());
184    final TableName TABLENAME =
185      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
186    Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
187    final byte[] COLUMN = Bytes.toBytes("column");
188
189    ht.incrementColumnValue(ROW, FAMILY, COLUMN, 5);
190    TEST_UTIL.flush(TABLENAME);
191
192    Delete del = new Delete(ROW);
193    ht.delete(del);
194
195    ht.incrementColumnValue(ROW, FAMILY, COLUMN, 5);
196
197    Get get = new Get(ROW);
198    Result r = ht.get(get);
199    assertEquals(1, r.size());
200    assertEquals(5, Bytes.toLong(r.getValue(FAMILY, COLUMN)));
201  }
202
203  @Test
204  public void testIncrementingInvalidValue() throws Exception {
205    LOG.info("Starting " + this.name.getMethodName());
206    final TableName TABLENAME =
207      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
208    Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
209    final byte[] COLUMN = Bytes.toBytes("column");
210    Put p = new Put(ROW);
211    // write an integer here (not a Long)
212    p.addColumn(FAMILY, COLUMN, Bytes.toBytes(5));
213    ht.put(p);
214    try {
215      ht.incrementColumnValue(ROW, FAMILY, COLUMN, 5);
216      fail("Should have thrown DoNotRetryIOException");
217    } catch (DoNotRetryIOException iox) {
218      // success
219    }
220    Increment inc = new Increment(ROW);
221    inc.addColumn(FAMILY, COLUMN, 5);
222    try {
223      ht.increment(inc);
224      fail("Should have thrown DoNotRetryIOException");
225    } catch (DoNotRetryIOException iox) {
226      // success
227    }
228  }
229
230  @Test
231  public void testBatchIncrementsWithReturnResultFalse() throws Exception {
232    LOG.info("Starting testBatchIncrementsWithReturnResultFalse");
233    final TableName tableName = TableName.valueOf(name.getMethodName());
234    Table table = TEST_UTIL.createTable(tableName, FAMILY);
235    Increment inc1 = new Increment(Bytes.toBytes("row2"));
236    inc1.setReturnResults(false);
237    inc1.addColumn(FAMILY, Bytes.toBytes("f1"), 1);
238    Increment inc2 = new Increment(Bytes.toBytes("row2"));
239    inc2.setReturnResults(false);
240    inc2.addColumn(FAMILY, Bytes.toBytes("f1"), 1);
241    List<Increment> incs = new ArrayList<>();
242    incs.add(inc1);
243    incs.add(inc2);
244    Object[] results = new Object[2];
245    table.batch(incs, results);
246    assertTrue(results.length == 2);
247    for (Object r : results) {
248      Result result = (Result) r;
249      assertTrue(result.isEmpty());
250    }
251    table.close();
252  }
253
254  @Test
255  public void testIncrementInvalidArguments() throws Exception {
256    LOG.info("Starting " + this.name.getMethodName());
257    final TableName TABLENAME =
258      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
259    Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
260    final byte[] COLUMN = Bytes.toBytes("column");
261    try {
262      // try null row
263      ht.incrementColumnValue(null, FAMILY, COLUMN, 5);
264      fail("Should have thrown NPE/IOE");
265    } catch (NullPointerException | IOException error) {
266      // success
267    }
268    try {
269      // try null family
270      ht.incrementColumnValue(ROW, null, COLUMN, 5);
271      fail("Should have thrown NPE/IOE");
272    } catch (NullPointerException | IOException error) {
273      // success
274    }
275    // try null row
276    try {
277      Increment incNoRow = new Increment((byte[]) null);
278      incNoRow.addColumn(FAMILY, COLUMN, 5);
279      fail("Should have thrown IAE/NPE");
280    } catch (IllegalArgumentException | NullPointerException error) {
281      // success
282    }
283    // try null family
284    try {
285      Increment incNoFamily = new Increment(ROW);
286      incNoFamily.addColumn(null, COLUMN, 5);
287      fail("Should have thrown IAE");
288    } catch (IllegalArgumentException iax) {
289      // success
290    }
291  }
292
293  @Test
294  public void testIncrementOutOfOrder() throws Exception {
295    LOG.info("Starting " + this.name.getMethodName());
296    final TableName TABLENAME =
297      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
298    Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
299
300    byte[][] QUALIFIERS =
301      new byte[][] { Bytes.toBytes("B"), Bytes.toBytes("A"), Bytes.toBytes("C") };
302
303    Increment inc = new Increment(ROW);
304    for (int i = 0; i < QUALIFIERS.length; i++) {
305      inc.addColumn(FAMILY, QUALIFIERS[i], 1);
306    }
307    ht.increment(inc);
308
309    // Verify expected results
310    Get get = new Get(ROW);
311    Result r = ht.get(get);
312    Cell[] kvs = r.rawCells();
313    assertEquals(3, kvs.length);
314    assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[1], 1);
315    assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[0], 1);
316    assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 1);
317
318    // Now try multiple columns again
319    inc = new Increment(ROW);
320    for (int i = 0; i < QUALIFIERS.length; i++) {
321      inc.addColumn(FAMILY, QUALIFIERS[i], 1);
322    }
323    ht.increment(inc);
324
325    // Verify
326    r = ht.get(get);
327    kvs = r.rawCells();
328    assertEquals(3, kvs.length);
329    assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[1], 2);
330    assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[0], 2);
331    assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 2);
332  }
333
334  @Test
335  public void testIncrementOnSameColumn() throws Exception {
336    LOG.info("Starting " + this.name.getMethodName());
337    final byte[] TABLENAME = Bytes.toBytes(filterStringSoTableNameSafe(this.name.getMethodName()));
338    Table ht = TEST_UTIL.createTable(TableName.valueOf(TABLENAME), FAMILY);
339
340    byte[][] QUALIFIERS =
341      new byte[][] { Bytes.toBytes("A"), Bytes.toBytes("B"), Bytes.toBytes("C") };
342
343    Increment inc = new Increment(ROW);
344    for (int i = 0; i < QUALIFIERS.length; i++) {
345      inc.addColumn(FAMILY, QUALIFIERS[i], 1);
346      inc.addColumn(FAMILY, QUALIFIERS[i], 1);
347    }
348    ht.increment(inc);
349
350    // Verify expected results
351    Get get = new Get(ROW);
352    Result r = ht.get(get);
353    Cell[] kvs = r.rawCells();
354    assertEquals(3, kvs.length);
355    assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[0], 1);
356    assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[1], 1);
357    assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 1);
358
359    // Now try multiple columns again
360    inc = new Increment(ROW);
361    for (int i = 0; i < QUALIFIERS.length; i++) {
362      inc.addColumn(FAMILY, QUALIFIERS[i], 1);
363      inc.addColumn(FAMILY, QUALIFIERS[i], 1);
364    }
365    ht.increment(inc);
366
367    // Verify
368    r = ht.get(get);
369    kvs = r.rawCells();
370    assertEquals(3, kvs.length);
371    assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[0], 2);
372    assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[1], 2);
373    assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 2);
374
375    ht.close();
376  }
377
378  @Test
379  public void testIncrementIncrZeroAtFirst() throws Exception {
380    LOG.info("Starting " + this.name.getMethodName());
381    final TableName TABLENAME =
382      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
383    Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
384
385    byte[] col1 = Bytes.toBytes("col1");
386    byte[] col2 = Bytes.toBytes("col2");
387    byte[] col3 = Bytes.toBytes("col3");
388
389    // Now increment zero at first time incr
390    Increment inc = new Increment(ROW);
391    inc.addColumn(FAMILY, col1, 0);
392    ht.increment(inc);
393
394    // Verify expected results
395    Get get = new Get(ROW);
396    Result r = ht.get(get);
397    Cell[] kvs = r.rawCells();
398    assertEquals(1, kvs.length);
399    assertNotNull(kvs[0]);
400    assertIncrementKey(kvs[0], ROW, FAMILY, col1, 0);
401
402    // Now try multiple columns by different amounts
403    inc = new Increment(ROW);
404    inc.addColumn(FAMILY, col1, 1);
405    inc.addColumn(FAMILY, col2, 0);
406    inc.addColumn(FAMILY, col3, 2);
407    ht.increment(inc);
408    // Verify
409    get = new Get(ROW);
410    r = ht.get(get);
411    kvs = r.rawCells();
412    assertEquals(3, kvs.length);
413    assertNotNull(kvs[0]);
414    assertNotNull(kvs[1]);
415    assertNotNull(kvs[2]);
416    assertIncrementKey(kvs[0], ROW, FAMILY, col1, 1);
417    assertIncrementKey(kvs[1], ROW, FAMILY, col2, 0);
418    assertIncrementKey(kvs[2], ROW, FAMILY, col3, 2);
419  }
420
421  @Test
422  public void testIncrement() throws Exception {
423    LOG.info("Starting " + this.name.getMethodName());
424    final TableName TABLENAME =
425      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
426    Table ht = TEST_UTIL.createTable(TABLENAME, FAMILY);
427
428    byte[][] ROWS = new byte[][] { Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c"),
429      Bytes.toBytes("d"), Bytes.toBytes("e"), Bytes.toBytes("f"), Bytes.toBytes("g"),
430      Bytes.toBytes("h"), Bytes.toBytes("i") };
431    byte[][] QUALIFIERS = new byte[][] { Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c"),
432      Bytes.toBytes("d"), Bytes.toBytes("e"), Bytes.toBytes("f"), Bytes.toBytes("g"),
433      Bytes.toBytes("h"), Bytes.toBytes("i") };
434
435    // Do some simple single-column increments
436
437    // First with old API
438    ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[0], 1);
439    ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[1], 2);
440    ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[2], 3);
441    ht.incrementColumnValue(ROW, FAMILY, QUALIFIERS[3], 4);
442
443    // Now increment things incremented with old and do some new
444    Increment inc = new Increment(ROW);
445    inc.addColumn(FAMILY, QUALIFIERS[1], 1);
446    inc.addColumn(FAMILY, QUALIFIERS[3], 1);
447    inc.addColumn(FAMILY, QUALIFIERS[4], 1);
448    ht.increment(inc);
449
450    // Verify expected results
451    Get get = new Get(ROW);
452    Result r = ht.get(get);
453    Cell[] kvs = r.rawCells();
454    assertEquals(5, kvs.length);
455    assertIncrementKey(kvs[0], ROW, FAMILY, QUALIFIERS[0], 1);
456    assertIncrementKey(kvs[1], ROW, FAMILY, QUALIFIERS[1], 3);
457    assertIncrementKey(kvs[2], ROW, FAMILY, QUALIFIERS[2], 3);
458    assertIncrementKey(kvs[3], ROW, FAMILY, QUALIFIERS[3], 5);
459    assertIncrementKey(kvs[4], ROW, FAMILY, QUALIFIERS[4], 1);
460
461    // Now try multiple columns by different amounts
462    inc = new Increment(ROWS[0]);
463    for (int i = 0; i < QUALIFIERS.length; i++) {
464      inc.addColumn(FAMILY, QUALIFIERS[i], i + 1);
465    }
466    ht.increment(inc);
467    // Verify
468    get = new Get(ROWS[0]);
469    r = ht.get(get);
470    kvs = r.rawCells();
471    assertEquals(QUALIFIERS.length, kvs.length);
472    for (int i = 0; i < QUALIFIERS.length; i++) {
473      assertIncrementKey(kvs[i], ROWS[0], FAMILY, QUALIFIERS[i], i + 1);
474    }
475
476    // Re-increment them
477    inc = new Increment(ROWS[0]);
478    for (int i = 0; i < QUALIFIERS.length; i++) {
479      inc.addColumn(FAMILY, QUALIFIERS[i], i + 1);
480    }
481    ht.increment(inc);
482    // Verify
483    r = ht.get(get);
484    kvs = r.rawCells();
485    assertEquals(QUALIFIERS.length, kvs.length);
486    for (int i = 0; i < QUALIFIERS.length; i++) {
487      assertIncrementKey(kvs[i], ROWS[0], FAMILY, QUALIFIERS[i], 2 * (i + 1));
488    }
489
490    // Verify that an Increment of an amount of zero, returns current count; i.e. same as for above
491    // test, that is: 2 * (i + 1).
492    inc = new Increment(ROWS[0]);
493    for (int i = 0; i < QUALIFIERS.length; i++) {
494      inc.addColumn(FAMILY, QUALIFIERS[i], 0);
495    }
496    ht.increment(inc);
497    r = ht.get(get);
498    kvs = r.rawCells();
499    assertEquals(QUALIFIERS.length, kvs.length);
500    for (int i = 0; i < QUALIFIERS.length; i++) {
501      assertIncrementKey(kvs[i], ROWS[0], FAMILY, QUALIFIERS[i], 2 * (i + 1));
502    }
503  }
504
505  @Test
506  public void testIncrementWithCustomTimestamp() throws IOException {
507    TableName TABLENAME = TableName.valueOf(name.getMethodName());
508    Table table = TEST_UTIL.createTable(TABLENAME, FAMILY);
509    long timestamp = 999;
510    Increment increment = new Increment(ROW);
511    increment.add(ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(ROW)
512      .setFamily(FAMILY).setQualifier(QUALIFIER).setTimestamp(timestamp)
513      .setType(KeyValue.Type.Put.getCode()).setValue(Bytes.toBytes(100L)).build());
514    Result r = table.increment(increment);
515    assertEquals(1, r.size());
516    assertEquals(timestamp, r.rawCells()[0].getTimestamp());
517    r = table.get(new Get(ROW));
518    assertEquals(1, r.size());
519    assertEquals(timestamp, r.rawCells()[0].getTimestamp());
520    r = table.increment(increment);
521    assertEquals(1, r.size());
522    assertNotEquals(timestamp, r.rawCells()[0].getTimestamp());
523    r = table.get(new Get(ROW));
524    assertEquals(1, r.size());
525    assertNotEquals(timestamp, r.rawCells()[0].getTimestamp());
526  }
527
528  /**
529   * Call over to the adjacent class's method of same name.
530   */
531  static void assertIncrementKey(Cell key, byte[] row, byte[] family, byte[] qualifier, long value)
532    throws Exception {
533    TestFromClientSide.assertIncrementKey(key, row, family, qualifier, value);
534  }
535
536  public static String filterStringSoTableNameSafe(final String str) {
537    return str.replaceAll("\\[fast\\=(.*)\\]", ".FAST.is.$1");
538  }
539
540  /*
541   * Test that we have only 1 ttl tag with increment mutation.
542   */
543  @Test
544  public void testIncrementWithTtlTags() throws Exception {
545    LOG.info("Starting " + this.name.getMethodName());
546    final TableName tableName =
547      TableName.valueOf(filterStringSoTableNameSafe(this.name.getMethodName()));
548    Table ht = TEST_UTIL.createTable(tableName, FAMILY);
549    final byte[] COLUMN = Bytes.toBytes("column");
550
551    Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
552    // Set RPC_CODEC_CONF_KEY to KeyValueCodecWithTags so that scan will return tags.
553    conf.set(RPC_CODEC_CONF_KEY, KeyValueCodecWithTags.class.getName());
554    conf.set(DEFAULT_CODEC_CLASS, "");
555    try (Connection connection = ConnectionFactory.createConnection(conf);
556      Table table = connection.getTable(tableName)) {
557      for (int i = 0; i < 10; i++) {
558        Increment inc = new Increment(ROW);
559        inc.addColumn(FAMILY, COLUMN, 1);
560        long ttl = i + 3600000;
561        inc.setTTL(ttl);
562        ht.increment(inc);
563
564        Scan scan = new Scan().withStartRow(ROW);
565        ResultScanner scanner = table.getScanner(scan);
566        int count = 0;
567        Result result;
568        while ((result = scanner.next()) != null) {
569          Cell[] cells = result.rawCells();
570          for (Cell cell : cells) {
571            List<Tag> tags = PrivateCellUtil.getTags(cell);
572            // Make sure there is only 1 tag.
573            assertEquals(1, tags.size());
574            Tag tag = tags.get(0);
575            assertEquals(TagType.TTL_TAG_TYPE, tag.getType());
576            long ttlTagValue = Bytes.toLong(tag.getValueArray(), tag.getValueOffset());
577            assertEquals(ttl, ttlTagValue);
578          }
579          count++;
580        }
581        // Make sure there is only 1 result.
582        assertEquals(1, count);
583      }
584    }
585  }
586}