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.hamcrest.CoreMatchers.containsString;
021import static org.hamcrest.CoreMatchers.instanceOf;
022import static org.hamcrest.MatcherAssert.assertThat;
023import static org.junit.Assert.assertArrayEquals;
024import static org.junit.Assert.assertEquals;
025import static org.junit.Assert.assertFalse;
026import static org.junit.Assert.assertNull;
027import static org.junit.Assert.assertTrue;
028import static org.junit.Assert.fail;
029
030import java.io.IOException;
031import java.io.UncheckedIOException;
032import java.util.ArrayList;
033import java.util.Arrays;
034import java.util.List;
035import java.util.Optional;
036import java.util.concurrent.CompletableFuture;
037import java.util.concurrent.ExecutionException;
038import java.util.concurrent.ForkJoinPool;
039import java.util.concurrent.Future;
040import java.util.concurrent.TimeUnit;
041import java.util.concurrent.TimeoutException;
042import java.util.function.Function;
043import java.util.stream.Collectors;
044import java.util.stream.IntStream;
045import org.apache.hadoop.hbase.Cell;
046import org.apache.hadoop.hbase.HBaseClassTestRule;
047import org.apache.hadoop.hbase.HBaseTestingUtil;
048import org.apache.hadoop.hbase.TableName;
049import org.apache.hadoop.hbase.coprocessor.ObserverContext;
050import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor;
051import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
052import org.apache.hadoop.hbase.coprocessor.RegionObserver;
053import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException;
054import org.apache.hadoop.hbase.testclassification.ClientTests;
055import org.apache.hadoop.hbase.testclassification.LargeTests;
056import org.apache.hadoop.hbase.util.Bytes;
057import org.junit.After;
058import org.junit.AfterClass;
059import org.junit.Before;
060import org.junit.BeforeClass;
061import org.junit.ClassRule;
062import org.junit.Test;
063import org.junit.experimental.categories.Category;
064import org.junit.runner.RunWith;
065import org.junit.runners.Parameterized;
066import org.junit.runners.Parameterized.Parameter;
067import org.junit.runners.Parameterized.Parameters;
068
069@RunWith(Parameterized.class)
070@Category({ LargeTests.class, ClientTests.class })
071public class TestAsyncTableBatch {
072
073  @ClassRule
074  public static final HBaseClassTestRule CLASS_RULE =
075    HBaseClassTestRule.forClass(TestAsyncTableBatch.class);
076
077  private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
078
079  private static TableName TABLE_NAME = TableName.valueOf("async");
080
081  private static byte[] FAMILY = Bytes.toBytes("cf");
082
083  private static byte[] CQ = Bytes.toBytes("cq");
084  private static byte[] CQ1 = Bytes.toBytes("cq1");
085
086  private static int COUNT = 1000;
087
088  private static AsyncConnection CONN;
089
090  private static byte[][] SPLIT_KEYS;
091
092  private static int MAX_KEY_VALUE_SIZE = 64 * 1024;
093
094  @Parameter(0)
095  public String tableType;
096
097  @Parameter(1)
098  public Function<TableName, AsyncTable<?>> tableGetter;
099
100  private static AsyncTable<?> getRawTable(TableName tableName) {
101    return CONN.getTable(tableName);
102  }
103
104  private static AsyncTable<?> getTable(TableName tableName) {
105    return CONN.getTable(tableName, ForkJoinPool.commonPool());
106  }
107
108  @Parameters(name = "{index}: type={0}")
109  public static List<Object[]> params() {
110    Function<TableName, AsyncTable<?>> rawTableGetter = TestAsyncTableBatch::getRawTable;
111    Function<TableName, AsyncTable<?>> tableGetter = TestAsyncTableBatch::getTable;
112    return Arrays.asList(new Object[] { "raw", rawTableGetter },
113      new Object[] { "normal", tableGetter });
114  }
115
116  @BeforeClass
117  public static void setUp() throws Exception {
118    TEST_UTIL.getConfiguration().setInt(ConnectionConfiguration.MAX_KEYVALUE_SIZE_KEY,
119      MAX_KEY_VALUE_SIZE);
120    TEST_UTIL.startMiniCluster(3);
121    SPLIT_KEYS = new byte[8][];
122    for (int i = 111; i < 999; i += 111) {
123      SPLIT_KEYS[i / 111 - 1] = Bytes.toBytes(String.format("%03d", i));
124    }
125    CONN = ConnectionFactory.createAsyncConnection(TEST_UTIL.getConfiguration()).get();
126  }
127
128  @AfterClass
129  public static void tearDown() throws Exception {
130    CONN.close();
131    TEST_UTIL.shutdownMiniCluster();
132  }
133
134  @Before
135  public void setUpBeforeTest() throws IOException, InterruptedException {
136    TEST_UTIL.createTable(TABLE_NAME, FAMILY, SPLIT_KEYS);
137    TEST_UTIL.waitTableAvailable(TABLE_NAME);
138  }
139
140  @After
141  public void tearDownAfterTest() throws IOException {
142    Admin admin = TEST_UTIL.getAdmin();
143    if (admin.isTableEnabled(TABLE_NAME)) {
144      admin.disableTable(TABLE_NAME);
145    }
146    admin.deleteTable(TABLE_NAME);
147  }
148
149  private byte[] getRow(int i) {
150    return Bytes.toBytes(String.format("%03d", i));
151  }
152
153  @Test
154  public void test()
155    throws InterruptedException, ExecutionException, IOException, TimeoutException {
156    AsyncTable<?> table = tableGetter.apply(TABLE_NAME);
157    table.putAll(IntStream.range(0, COUNT)
158      .mapToObj(i -> new Put(getRow(i)).addColumn(FAMILY, CQ, Bytes.toBytes(i)))
159      .collect(Collectors.toList())).get();
160    List<Result> results = table.getAll(IntStream.range(0, COUNT)
161      .mapToObj(i -> Arrays.asList(new Get(getRow(i)), new Get(Arrays.copyOf(getRow(i), 4))))
162      .flatMap(l -> l.stream()).collect(Collectors.toList())).get();
163    assertEquals(2 * COUNT, results.size());
164    for (int i = 0; i < COUNT; i++) {
165      assertEquals(i, Bytes.toInt(results.get(2 * i).getValue(FAMILY, CQ)));
166      assertTrue(results.get(2 * i + 1).isEmpty());
167    }
168    Admin admin = TEST_UTIL.getAdmin();
169    admin.flush(TABLE_NAME);
170    List<Future<?>> splitFutures =
171      TEST_UTIL.getHBaseCluster().getRegions(TABLE_NAME).stream().map(r -> {
172        byte[] startKey = r.getRegionInfo().getStartKey();
173        int number = startKey.length == 0 ? 55 : Integer.parseInt(Bytes.toString(startKey));
174        byte[] splitPoint = Bytes.toBytes(String.format("%03d", number + 55));
175        try {
176          return admin.splitRegionAsync(r.getRegionInfo().getRegionName(), splitPoint);
177        } catch (IOException e) {
178          throw new UncheckedIOException(e);
179        }
180      }).collect(Collectors.toList());
181    for (Future<?> future : splitFutures) {
182      future.get(30, TimeUnit.SECONDS);
183    }
184    table
185      .deleteAll(
186        IntStream.range(0, COUNT).mapToObj(i -> new Delete(getRow(i))).collect(Collectors.toList()))
187      .get();
188    results = table
189      .getAll(
190        IntStream.range(0, COUNT).mapToObj(i -> new Get(getRow(i))).collect(Collectors.toList()))
191      .get();
192    assertEquals(COUNT, results.size());
193    results.forEach(r -> assertTrue(r.isEmpty()));
194  }
195
196  @Test
197  public void testWithRegionServerFailover() throws Exception {
198    AsyncTable<?> table = tableGetter.apply(TABLE_NAME);
199    table.putAll(IntStream.range(0, COUNT)
200      .mapToObj(i -> new Put(getRow(i)).addColumn(FAMILY, CQ, Bytes.toBytes(i)))
201      .collect(Collectors.toList())).get();
202    TEST_UTIL.getMiniHBaseCluster().getRegionServer(0).abort("Aborting for tests");
203    Thread.sleep(100);
204    table.putAll(IntStream.range(COUNT, 2 * COUNT)
205      .mapToObj(i -> new Put(getRow(i)).addColumn(FAMILY, CQ, Bytes.toBytes(i)))
206      .collect(Collectors.toList())).get();
207    List<Result> results = table.getAll(
208      IntStream.range(0, 2 * COUNT).mapToObj(i -> new Get(getRow(i))).collect(Collectors.toList()))
209      .get();
210    assertEquals(2 * COUNT, results.size());
211    results.forEach(r -> assertFalse(r.isEmpty()));
212    table.deleteAll(IntStream.range(0, 2 * COUNT).mapToObj(i -> new Delete(getRow(i)))
213      .collect(Collectors.toList())).get();
214    results = table.getAll(
215      IntStream.range(0, 2 * COUNT).mapToObj(i -> new Get(getRow(i))).collect(Collectors.toList()))
216      .get();
217    assertEquals(2 * COUNT, results.size());
218    results.forEach(r -> assertTrue(r.isEmpty()));
219  }
220
221  @Test
222  public void testMixed() throws InterruptedException, ExecutionException, IOException {
223    AsyncTable<?> table = tableGetter.apply(TABLE_NAME);
224    table.putAll(IntStream.range(0, 7)
225      .mapToObj(i -> new Put(Bytes.toBytes(i)).addColumn(FAMILY, CQ, Bytes.toBytes((long) i)))
226      .collect(Collectors.toList())).get();
227    List<Row> actions = new ArrayList<>();
228    actions.add(new Get(Bytes.toBytes(0)));
229    actions.add(new Put(Bytes.toBytes(1)).addColumn(FAMILY, CQ, Bytes.toBytes(2L)));
230    actions.add(new Delete(Bytes.toBytes(2)));
231    actions.add(new Increment(Bytes.toBytes(3)).addColumn(FAMILY, CQ, 1));
232    actions.add(new Append(Bytes.toBytes(4)).addColumn(FAMILY, CQ, Bytes.toBytes(4)));
233    RowMutations rm = new RowMutations(Bytes.toBytes(5));
234    rm.add((Mutation) new Put(Bytes.toBytes(5)).addColumn(FAMILY, CQ, Bytes.toBytes(100L)));
235    rm.add((Mutation) new Put(Bytes.toBytes(5)).addColumn(FAMILY, CQ1, Bytes.toBytes(200L)));
236    actions.add(rm);
237    actions.add(new Get(Bytes.toBytes(6)));
238
239    List<Object> results = table.batchAll(actions).get();
240    assertEquals(7, results.size());
241    Result getResult = (Result) results.get(0);
242    assertEquals(0, Bytes.toLong(getResult.getValue(FAMILY, CQ)));
243    assertEquals(2, Bytes.toLong(table.get(new Get(Bytes.toBytes(1))).get().getValue(FAMILY, CQ)));
244    assertTrue(table.get(new Get(Bytes.toBytes(2))).get().isEmpty());
245    Result incrementResult = (Result) results.get(3);
246    assertEquals(4, Bytes.toLong(incrementResult.getValue(FAMILY, CQ)));
247    Result appendResult = (Result) results.get(4);
248    byte[] appendValue = appendResult.getValue(FAMILY, CQ);
249    assertEquals(12, appendValue.length);
250    assertEquals(4, Bytes.toLong(appendValue));
251    assertEquals(4, Bytes.toInt(appendValue, 8));
252    assertEquals(100,
253      Bytes.toLong(table.get(new Get(Bytes.toBytes(5))).get().getValue(FAMILY, CQ)));
254    assertEquals(200,
255      Bytes.toLong(table.get(new Get(Bytes.toBytes(5))).get().getValue(FAMILY, CQ1)));
256    getResult = (Result) results.get(6);
257    assertEquals(6, Bytes.toLong(getResult.getValue(FAMILY, CQ)));
258  }
259
260  public static final class ErrorInjectObserver implements RegionCoprocessor, RegionObserver {
261
262    @Override
263    public Optional<RegionObserver> getRegionObserver() {
264      return Optional.of(this);
265    }
266
267    @Override
268    public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get,
269      List<Cell> results) throws IOException {
270      if (e.getEnvironment().getRegionInfo().getEndKey().length == 0) {
271        throw new DoNotRetryRegionException("Inject Error");
272      }
273    }
274  }
275
276  @Test
277  public void testPartialSuccess() throws IOException, InterruptedException, ExecutionException {
278    Admin admin = TEST_UTIL.getAdmin();
279    TableDescriptor htd = TableDescriptorBuilder.newBuilder(admin.getDescriptor(TABLE_NAME))
280      .setCoprocessor(ErrorInjectObserver.class.getName()).build();
281    admin.modifyTable(htd);
282    AsyncTable<?> table = tableGetter.apply(TABLE_NAME);
283    table.putAll(Arrays.asList(SPLIT_KEYS).stream().map(k -> new Put(k).addColumn(FAMILY, CQ, k))
284      .collect(Collectors.toList())).get();
285    List<CompletableFuture<Result>> futures = table
286      .get(Arrays.asList(SPLIT_KEYS).stream().map(k -> new Get(k)).collect(Collectors.toList()));
287    for (int i = 0; i < SPLIT_KEYS.length - 1; i++) {
288      assertArrayEquals(SPLIT_KEYS[i], futures.get(i).get().getValue(FAMILY, CQ));
289    }
290    try {
291      futures.get(SPLIT_KEYS.length - 1).get();
292      fail();
293    } catch (ExecutionException e) {
294      assertThat(e.getCause(), instanceOf(RetriesExhaustedException.class));
295    }
296  }
297
298  @Test
299  public void testPartialSuccessOnSameRegion() throws InterruptedException, ExecutionException {
300    AsyncTable<?> table = tableGetter.apply(TABLE_NAME);
301    List<CompletableFuture<Object>> futures = table.batch(Arrays.asList(
302      new Put(Bytes.toBytes("put")).addColumn(Bytes.toBytes("not-exists"), CQ,
303        Bytes.toBytes("bad")),
304      new Increment(Bytes.toBytes("inc")).addColumn(FAMILY, CQ, 1),
305      new Put(Bytes.toBytes("put")).addColumn(FAMILY, CQ, Bytes.toBytes("good"))));
306    try {
307      futures.get(0).get();
308      fail();
309    } catch (ExecutionException e) {
310      assertThat(e.getCause(), instanceOf(RetriesExhaustedException.class));
311      assertThat(e.getCause().getCause(), instanceOf(NoSuchColumnFamilyException.class));
312    }
313    assertEquals(1, Bytes.toLong(((Result) futures.get(1).get()).getValue(FAMILY, CQ)));
314    assertTrue(((Result) futures.get(2).get()).isEmpty());
315    assertEquals("good",
316      Bytes.toString(table.get(new Get(Bytes.toBytes("put"))).get().getValue(FAMILY, CQ)));
317  }
318
319  @Test
320  public void testInvalidPut() {
321    AsyncTable<?> table = tableGetter.apply(TABLE_NAME);
322    try {
323      table.batch(Arrays.asList(new Delete(Bytes.toBytes(0)), new Put(Bytes.toBytes(0))));
324      fail("Should fail since the put does not contain any cells");
325    } catch (IllegalArgumentException e) {
326      assertThat(e.getMessage(), containsString("No columns to insert"));
327    }
328
329    try {
330      table.batch(
331        Arrays.asList(new Put(Bytes.toBytes(0)).addColumn(FAMILY, CQ, new byte[MAX_KEY_VALUE_SIZE]),
332          new Delete(Bytes.toBytes(0))));
333      fail("Should fail since the put exceeds the max key value size");
334    } catch (IllegalArgumentException e) {
335      assertThat(e.getMessage(), containsString("KeyValue size too large"));
336    }
337  }
338
339  @Test
340  public void testInvalidPutInRowMutations() throws IOException {
341    final byte[] row = Bytes.toBytes(0);
342
343    AsyncTable<?> table = tableGetter.apply(TABLE_NAME);
344    try {
345      table.batch(Arrays.asList(new Delete(row), new RowMutations(row).add(new Put(row))));
346      fail("Should fail since the put does not contain any cells");
347    } catch (IllegalArgumentException e) {
348      assertThat(e.getMessage(), containsString("No columns to insert"));
349    }
350
351    try {
352      table.batch(Arrays.asList(
353        new RowMutations(row).add(new Put(row).addColumn(FAMILY, CQ, new byte[MAX_KEY_VALUE_SIZE])),
354        new Delete(row)));
355      fail("Should fail since the put exceeds the max key value size");
356    } catch (IllegalArgumentException e) {
357      assertThat(e.getMessage(), containsString("KeyValue size too large"));
358    }
359  }
360
361  @Test
362  public void testInvalidPutInRowMutationsInCheckAndMutate() throws IOException {
363    final byte[] row = Bytes.toBytes(0);
364
365    AsyncTable<?> table = tableGetter.apply(TABLE_NAME);
366    try {
367      table.batch(Arrays.asList(new Delete(row), CheckAndMutate.newBuilder(row)
368        .ifNotExists(FAMILY, CQ).build(new RowMutations(row).add(new Put(row)))));
369      fail("Should fail since the put does not contain any cells");
370    } catch (IllegalArgumentException e) {
371      assertThat(e.getMessage(), containsString("No columns to insert"));
372    }
373
374    try {
375      table.batch(Arrays.asList(
376        CheckAndMutate.newBuilder(row).ifNotExists(FAMILY, CQ)
377          .build(new RowMutations(row)
378            .add(new Put(row).addColumn(FAMILY, CQ, new byte[MAX_KEY_VALUE_SIZE]))),
379        new Delete(row)));
380      fail("Should fail since the put exceeds the max key value size");
381    } catch (IllegalArgumentException e) {
382      assertThat(e.getMessage(), containsString("KeyValue size too large"));
383    }
384  }
385
386  @Test
387  public void testWithCheckAndMutate() throws Exception {
388    AsyncTable<?> table = tableGetter.apply(TABLE_NAME);
389
390    byte[] row1 = Bytes.toBytes("row1");
391    byte[] row2 = Bytes.toBytes("row2");
392    byte[] row3 = Bytes.toBytes("row3");
393    byte[] row4 = Bytes.toBytes("row4");
394    byte[] row5 = Bytes.toBytes("row5");
395    byte[] row6 = Bytes.toBytes("row6");
396    byte[] row7 = Bytes.toBytes("row7");
397
398    table
399      .putAll(Arrays.asList(new Put(row1).addColumn(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a")),
400        new Put(row2).addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b")),
401        new Put(row3).addColumn(FAMILY, Bytes.toBytes("C"), Bytes.toBytes("c")),
402        new Put(row4).addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d")),
403        new Put(row5).addColumn(FAMILY, Bytes.toBytes("E"), Bytes.toBytes("e")),
404        new Put(row6).addColumn(FAMILY, Bytes.toBytes("F"), Bytes.toBytes(10L)),
405        new Put(row7).addColumn(FAMILY, Bytes.toBytes("G"), Bytes.toBytes("g"))))
406      .get();
407
408    CheckAndMutate checkAndMutate1 =
409      CheckAndMutate.newBuilder(row1).ifEquals(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a"))
410        .build(new RowMutations(row1)
411          .add(new Put(row1).addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("g")))
412          .add(new Delete(row1).addColumns(FAMILY, Bytes.toBytes("A")))
413          .add(new Increment(row1).addColumn(FAMILY, Bytes.toBytes("C"), 3L))
414          .add(new Append(row1).addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d"))));
415    Get get = new Get(row2).addColumn(FAMILY, Bytes.toBytes("B"));
416    RowMutations mutations =
417      new RowMutations(row3).add(new Delete(row3).addColumns(FAMILY, Bytes.toBytes("C")))
418        .add(new Put(row3).addColumn(FAMILY, Bytes.toBytes("F"), Bytes.toBytes("f")))
419        .add(new Increment(row3).addColumn(FAMILY, Bytes.toBytes("A"), 5L))
420        .add(new Append(row3).addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b")));
421    CheckAndMutate checkAndMutate2 =
422      CheckAndMutate.newBuilder(row4).ifEquals(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("a"))
423        .build(new Put(row4).addColumn(FAMILY, Bytes.toBytes("E"), Bytes.toBytes("h")));
424    Put put = new Put(row5).addColumn(FAMILY, Bytes.toBytes("E"), Bytes.toBytes("f"));
425    CheckAndMutate checkAndMutate3 =
426      CheckAndMutate.newBuilder(row6).ifEquals(FAMILY, Bytes.toBytes("F"), Bytes.toBytes(10L))
427        .build(new Increment(row6).addColumn(FAMILY, Bytes.toBytes("F"), 1));
428    CheckAndMutate checkAndMutate4 =
429      CheckAndMutate.newBuilder(row7).ifEquals(FAMILY, Bytes.toBytes("G"), Bytes.toBytes("g"))
430        .build(new Append(row7).addColumn(FAMILY, Bytes.toBytes("G"), Bytes.toBytes("g")));
431
432    List<Row> actions = Arrays.asList(checkAndMutate1, get, mutations, checkAndMutate2, put,
433      checkAndMutate3, checkAndMutate4);
434    List<Object> results = table.batchAll(actions).get();
435
436    CheckAndMutateResult checkAndMutateResult = (CheckAndMutateResult) results.get(0);
437    assertTrue(checkAndMutateResult.isSuccess());
438    assertEquals(3L,
439      Bytes.toLong(checkAndMutateResult.getResult().getValue(FAMILY, Bytes.toBytes("C"))));
440    assertEquals("d",
441      Bytes.toString(checkAndMutateResult.getResult().getValue(FAMILY, Bytes.toBytes("D"))));
442
443    assertEquals("b",
444      Bytes.toString(((Result) results.get(1)).getValue(FAMILY, Bytes.toBytes("B"))));
445
446    Result result = (Result) results.get(2);
447    assertTrue(result.getExists());
448    assertEquals(5L, Bytes.toLong(result.getValue(FAMILY, Bytes.toBytes("A"))));
449    assertEquals("b", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("B"))));
450
451    checkAndMutateResult = (CheckAndMutateResult) results.get(3);
452    assertFalse(checkAndMutateResult.isSuccess());
453    assertNull(checkAndMutateResult.getResult());
454
455    assertTrue(((Result) results.get(4)).isEmpty());
456
457    checkAndMutateResult = (CheckAndMutateResult) results.get(5);
458    assertTrue(checkAndMutateResult.isSuccess());
459    assertEquals(11,
460      Bytes.toLong(checkAndMutateResult.getResult().getValue(FAMILY, Bytes.toBytes("F"))));
461
462    checkAndMutateResult = (CheckAndMutateResult) results.get(6);
463    assertTrue(checkAndMutateResult.isSuccess());
464    assertEquals("gg",
465      Bytes.toString(checkAndMutateResult.getResult().getValue(FAMILY, Bytes.toBytes("G"))));
466
467    result = table.get(new Get(row1)).get();
468    assertEquals("g", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("B"))));
469    assertNull(result.getValue(FAMILY, Bytes.toBytes("A")));
470    assertEquals(3L, Bytes.toLong(result.getValue(FAMILY, Bytes.toBytes("C"))));
471    assertEquals("d", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("D"))));
472
473    result = table.get(new Get(row3)).get();
474    assertNull(result.getValue(FAMILY, Bytes.toBytes("C")));
475    assertEquals("f", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("F"))));
476    assertNull(Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("C"))));
477    assertEquals(5L, Bytes.toLong(result.getValue(FAMILY, Bytes.toBytes("A"))));
478    assertEquals("b", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("B"))));
479
480    result = table.get(new Get(row4)).get();
481    assertEquals("d", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("D"))));
482
483    result = table.get(new Get(row5)).get();
484    assertEquals("f", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("E"))));
485
486    result = table.get(new Get(row6)).get();
487    assertEquals(11, Bytes.toLong(result.getValue(FAMILY, Bytes.toBytes("F"))));
488
489    result = table.get(new Get(row7)).get();
490    assertEquals("gg", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("G"))));
491  }
492}