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