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