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