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.awaitility.Awaitility.await;
021import static org.hamcrest.MatcherAssert.assertThat;
022import static org.hamcrest.Matchers.hasItem;
023import static org.hamcrest.Matchers.instanceOf;
024import static org.junit.jupiter.api.Assertions.assertArrayEquals;
025import static org.junit.jupiter.api.Assertions.assertEquals;
026import static org.junit.jupiter.api.Assertions.assertFalse;
027import static org.junit.jupiter.api.Assertions.assertNotNull;
028import static org.junit.jupiter.api.Assertions.assertNull;
029import static org.junit.jupiter.api.Assertions.assertThrows;
030import static org.junit.jupiter.api.Assertions.assertTrue;
031
032import java.io.IOException;
033import java.time.Duration;
034import java.util.ArrayList;
035import java.util.Arrays;
036import java.util.LinkedList;
037import java.util.List;
038import java.util.Map;
039import java.util.NavigableMap;
040import java.util.concurrent.Callable;
041import java.util.concurrent.ExecutorService;
042import java.util.concurrent.Executors;
043import java.util.concurrent.atomic.AtomicReference;
044import org.apache.commons.lang3.ArrayUtils;
045import org.apache.hadoop.conf.Configuration;
046import org.apache.hadoop.fs.Path;
047import org.apache.hadoop.hbase.Cell;
048import org.apache.hadoop.hbase.CellScanner;
049import org.apache.hadoop.hbase.CellUtil;
050import org.apache.hadoop.hbase.CompareOperator;
051import org.apache.hadoop.hbase.DoNotRetryIOException;
052import org.apache.hadoop.hbase.HConstants;
053import org.apache.hadoop.hbase.HRegionLocation;
054import org.apache.hadoop.hbase.PrivateCellUtil;
055import org.apache.hadoop.hbase.ServerName;
056import org.apache.hadoop.hbase.TableName;
057import org.apache.hadoop.hbase.Waiter;
058import org.apache.hadoop.hbase.client.Scan.ReadType;
059import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
060import org.apache.hadoop.hbase.filter.BinaryComparator;
061import org.apache.hadoop.hbase.filter.FilterList;
062import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
063import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
064import org.apache.hadoop.hbase.filter.RowFilter;
065import org.apache.hadoop.hbase.filter.SubstringComparator;
066import org.apache.hadoop.hbase.filter.ValueFilter;
067import org.apache.hadoop.hbase.io.hfile.BlockCache;
068import org.apache.hadoop.hbase.io.hfile.CacheConfig;
069import org.apache.hadoop.hbase.regionserver.HRegion;
070import org.apache.hadoop.hbase.regionserver.HRegionServer;
071import org.apache.hadoop.hbase.regionserver.HStore;
072import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException;
073import org.apache.hadoop.hbase.util.Bytes;
074import org.apache.hadoop.hbase.util.CommonFSUtils;
075import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
076import org.apache.hadoop.hbase.util.FSUtils;
077import org.junit.jupiter.api.Disabled;
078import org.junit.jupiter.api.TestTemplate;
079import org.slf4j.Logger;
080import org.slf4j.LoggerFactory;
081
082/**
083 * Run tests that use the HBase clients; {@link Table}. Sets up the HBase mini cluster once at start
084 * and runs through all client tests. Each creates a table named for the method and does its stuff
085 * against that. Parameterized to run with different registry implementations.
086 */
087public class FromClientSideTest5 extends FromClientSideTestBase {
088
089  protected FromClientSideTest5(Class<? extends ConnectionRegistry> registryImpl,
090    int numHedgedReqs) {
091    super(registryImpl, numHedgedReqs);
092  }
093
094  private static final Logger LOG = LoggerFactory.getLogger(FromClientSideTest5.class);
095
096  @TestTemplate
097  public void testGetClosestRowBefore() throws IOException, InterruptedException {
098
099    final byte[] firstRow = Bytes.toBytes("row111");
100    final byte[] secondRow = Bytes.toBytes("row222");
101    final byte[] thirdRow = Bytes.toBytes("row333");
102    final byte[] forthRow = Bytes.toBytes("row444");
103    final byte[] beforeFirstRow = Bytes.toBytes("row");
104    final byte[] beforeSecondRow = Bytes.toBytes("row22");
105    final byte[] beforeThirdRow = Bytes.toBytes("row33");
106    final byte[] beforeForthRow = Bytes.toBytes("row44");
107
108    try (
109      Table table = TEST_UTIL.createTable(tableName,
110        new byte[][] { HConstants.CATALOG_FAMILY, Bytes.toBytes("info2") }, 1, 1024);
111      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
112
113      // set block size to 64 to making 2 kvs into one block, bypassing the walkForwardInSingleRow
114      // in Store.rowAtOrBeforeFromStoreFile
115      String regionName = locator.getAllRegionLocations().get(0).getRegion().getEncodedName();
116      HRegion region = TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
117      Put put1 = new Put(firstRow);
118      Put put2 = new Put(secondRow);
119      Put put3 = new Put(thirdRow);
120      Put put4 = new Put(forthRow);
121      byte[] one = new byte[] { 1 };
122      byte[] two = new byte[] { 2 };
123      byte[] three = new byte[] { 3 };
124      byte[] four = new byte[] { 4 };
125
126      put1.addColumn(HConstants.CATALOG_FAMILY, null, one);
127      put2.addColumn(HConstants.CATALOG_FAMILY, null, two);
128      put3.addColumn(HConstants.CATALOG_FAMILY, null, three);
129      put4.addColumn(HConstants.CATALOG_FAMILY, null, four);
130      table.put(put1);
131      table.put(put2);
132      table.put(put3);
133      table.put(put4);
134      region.flush(true);
135
136      Result result;
137
138      // Test before first that null is returned
139      result = getReverseScanResult(table, beforeFirstRow);
140      assertNull(result);
141
142      // Test at first that first is returned
143      result = getReverseScanResult(table, firstRow);
144      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
145      assertTrue(Bytes.equals(result.getRow(), firstRow));
146      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), one));
147
148      // Test in between first and second that first is returned
149      result = getReverseScanResult(table, beforeSecondRow);
150      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
151      assertTrue(Bytes.equals(result.getRow(), firstRow));
152      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), one));
153
154      // Test at second make sure second is returned
155      result = getReverseScanResult(table, secondRow);
156      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
157      assertTrue(Bytes.equals(result.getRow(), secondRow));
158      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), two));
159
160      // Test in second and third, make sure second is returned
161      result = getReverseScanResult(table, beforeThirdRow);
162      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
163      assertTrue(Bytes.equals(result.getRow(), secondRow));
164      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), two));
165
166      // Test at third make sure third is returned
167      result = getReverseScanResult(table, thirdRow);
168      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
169      assertTrue(Bytes.equals(result.getRow(), thirdRow));
170      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), three));
171
172      // Test in third and forth, make sure third is returned
173      result = getReverseScanResult(table, beforeForthRow);
174      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
175      assertTrue(Bytes.equals(result.getRow(), thirdRow));
176      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), three));
177
178      // Test at forth make sure forth is returned
179      result = getReverseScanResult(table, forthRow);
180      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
181      assertTrue(Bytes.equals(result.getRow(), forthRow));
182      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), four));
183
184      // Test after forth make sure forth is returned
185      result = getReverseScanResult(table, Bytes.add(forthRow, one));
186      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
187      assertTrue(Bytes.equals(result.getRow(), forthRow));
188      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), four));
189    }
190  }
191
192  private Result getReverseScanResult(Table table, byte[] row) throws IOException {
193    Scan scan = new Scan().withStartRow(row);
194    scan.setReadType(ReadType.PREAD);
195    scan.setReversed(true);
196    scan.setCaching(1);
197    scan.addFamily(HConstants.CATALOG_FAMILY);
198    try (ResultScanner scanner = table.getScanner(scan)) {
199      return scanner.next();
200    }
201  }
202
203  @TestTemplate
204  public void testRowMutations() throws Exception {
205    LOG.info("Starting testRowMutations");
206    TEST_UTIL.createTable(tableName, FAMILY);
207    try (Connection conn = getConnection(); Table t = conn.getTable(tableName)) {
208      byte[][] QUALIFIERS = new byte[][] { Bytes.toBytes("a"), Bytes.toBytes("b"),
209        Bytes.toBytes("c"), Bytes.toBytes("d") };
210
211      // Test for Put operations
212      RowMutations arm = new RowMutations(ROW);
213      Put p = new Put(ROW);
214      p.addColumn(FAMILY, QUALIFIERS[0], VALUE);
215      arm.add(p);
216      Result r = t.mutateRow(arm);
217      assertTrue(r.getExists());
218      assertTrue(r.isEmpty());
219
220      Get g = new Get(ROW);
221      r = t.get(g);
222      assertEquals(0, Bytes.compareTo(VALUE, r.getValue(FAMILY, QUALIFIERS[0])));
223
224      // Test for Put and Delete operations
225      arm = new RowMutations(ROW);
226      p = new Put(ROW);
227      p.addColumn(FAMILY, QUALIFIERS[1], VALUE);
228      arm.add(p);
229      Delete d = new Delete(ROW);
230      d.addColumns(FAMILY, QUALIFIERS[0]);
231      arm.add(d);
232      // TODO: Trying mutateRow again. The batch was failing with a one try only.
233      r = t.mutateRow(arm);
234      assertTrue(r.getExists());
235      assertTrue(r.isEmpty());
236
237      r = t.get(g);
238      assertEquals(0, Bytes.compareTo(VALUE, r.getValue(FAMILY, QUALIFIERS[1])));
239      assertNull(r.getValue(FAMILY, QUALIFIERS[0]));
240
241      // Test for Increment and Append operations
242      arm = new RowMutations(ROW);
243      arm.add(Arrays.asList(new Put(ROW).addColumn(FAMILY, QUALIFIERS[0], VALUE),
244        new Delete(ROW).addColumns(FAMILY, QUALIFIERS[1]),
245        new Increment(ROW).addColumn(FAMILY, QUALIFIERS[2], 5L),
246        new Append(ROW).addColumn(FAMILY, QUALIFIERS[3], Bytes.toBytes("abc"))));
247      r = t.mutateRow(arm);
248      assertTrue(r.getExists());
249      assertEquals(5L, Bytes.toLong(r.getValue(FAMILY, QUALIFIERS[2])));
250      assertEquals("abc", Bytes.toString(r.getValue(FAMILY, QUALIFIERS[3])));
251
252      g = new Get(ROW);
253      r = t.get(g);
254      assertEquals(0, Bytes.compareTo(VALUE, r.getValue(FAMILY, QUALIFIERS[0])));
255      assertNull(r.getValue(FAMILY, QUALIFIERS[1]));
256      assertEquals(5L, Bytes.toLong(r.getValue(FAMILY, QUALIFIERS[2])));
257      assertEquals("abc", Bytes.toString(r.getValue(FAMILY, QUALIFIERS[3])));
258
259      // Test that we get a region level exception
260      RowMutations nceRm = new RowMutations(ROW);
261      p = new Put(ROW);
262      p.addColumn(new byte[] { 'b', 'o', 'g', 'u', 's' }, QUALIFIERS[0], VALUE);
263      nceRm.add(p);
264      Exception e = assertThrows(Exception.class, () -> t.mutateRow(nceRm),
265        "Expected NoSuchColumnFamilyException");
266      if (!(e instanceof NoSuchColumnFamilyException)) {
267        assertThat(e, instanceOf(RetriesExhaustedWithDetailsException.class));
268        List<Throwable> causes = ((RetriesExhaustedWithDetailsException) e).getCauses();
269        assertThat(causes, hasItem(instanceOf(NoSuchColumnFamilyException.class)));
270      }
271    }
272  }
273
274  @TestTemplate
275  public void testBatchAppendWithReturnResultFalse() throws Exception {
276    LOG.info("Starting testBatchAppendWithReturnResultFalse");
277    TEST_UTIL.createTable(tableName, FAMILY);
278    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
279      Append append1 = new Append(Bytes.toBytes("row1"));
280      append1.setReturnResults(false);
281      append1.addColumn(FAMILY, Bytes.toBytes("f1"), Bytes.toBytes("value1"));
282      Append append2 = new Append(Bytes.toBytes("row1"));
283      append2.setReturnResults(false);
284      append2.addColumn(FAMILY, Bytes.toBytes("f1"), Bytes.toBytes("value2"));
285      List<Append> appends = new ArrayList<>();
286      appends.add(append1);
287      appends.add(append2);
288      Object[] results = new Object[2];
289      table.batch(appends, results);
290      assertEquals(2, results.length);
291      for (Object r : results) {
292        Result result = (Result) r;
293        assertTrue(result.isEmpty());
294      }
295    }
296  }
297
298  @TestTemplate
299  public void testAppend() throws Exception {
300    LOG.info("Starting testAppend");
301    TEST_UTIL.createTable(tableName, FAMILY);
302    try (Connection conn = getConnection(); Table t = conn.getTable(tableName)) {
303      byte[] v1 = Bytes.toBytes("42");
304      byte[] v2 = Bytes.toBytes("23");
305      byte[][] QUALIFIERS =
306        new byte[][] { Bytes.toBytes("b"), Bytes.toBytes("a"), Bytes.toBytes("c") };
307      Append a = new Append(ROW);
308      a.addColumn(FAMILY, QUALIFIERS[0], v1);
309      a.addColumn(FAMILY, QUALIFIERS[1], v2);
310      a.setReturnResults(false);
311      assertEmptyResult(t.append(a));
312
313      a = new Append(ROW);
314      a.addColumn(FAMILY, QUALIFIERS[0], v2);
315      a.addColumn(FAMILY, QUALIFIERS[1], v1);
316      a.addColumn(FAMILY, QUALIFIERS[2], v2);
317      Result r = t.append(a);
318      assertEquals(0, Bytes.compareTo(Bytes.add(v1, v2), r.getValue(FAMILY, QUALIFIERS[0])));
319      assertEquals(0, Bytes.compareTo(Bytes.add(v2, v1), r.getValue(FAMILY, QUALIFIERS[1])));
320      // QUALIFIERS[2] previously not exist, verify both value and timestamp are correct
321      assertEquals(0, Bytes.compareTo(v2, r.getValue(FAMILY, QUALIFIERS[2])));
322      assertEquals(r.getColumnLatestCell(FAMILY, QUALIFIERS[0]).getTimestamp(),
323        r.getColumnLatestCell(FAMILY, QUALIFIERS[2]).getTimestamp());
324    }
325  }
326
327  private List<Result> doAppend(final boolean walUsed) throws IOException {
328    LOG.info("Starting testAppend, walUsed is " + walUsed);
329    TableName tableName = TableName.valueOf(
330      this.tableName.getNameAsString() + (walUsed ? "_testAppendWithWAL" : "testAppendWithoutWAL"));
331    TEST_UTIL.createTable(tableName, FAMILY);
332    try (Connection conn = getConnection(); Table t = conn.getTable(tableName)) {
333      final byte[] row1 = Bytes.toBytes("c");
334      final byte[] row2 = Bytes.toBytes("b");
335      final byte[] row3 = Bytes.toBytes("a");
336      final byte[] qual = Bytes.toBytes("qual");
337      Put put_0 = new Put(row2);
338      put_0.addColumn(FAMILY, qual, Bytes.toBytes("put"));
339      Put put_1 = new Put(row3);
340      put_1.addColumn(FAMILY, qual, Bytes.toBytes("put"));
341      Append append_0 = new Append(row1);
342      append_0.addColumn(FAMILY, qual, Bytes.toBytes("i"));
343      Append append_1 = new Append(row1);
344      append_1.addColumn(FAMILY, qual, Bytes.toBytes("k"));
345      Append append_2 = new Append(row1);
346      append_2.addColumn(FAMILY, qual, Bytes.toBytes("e"));
347      if (!walUsed) {
348        append_2.setDurability(Durability.SKIP_WAL);
349      }
350      Append append_3 = new Append(row1);
351      append_3.addColumn(FAMILY, qual, Bytes.toBytes("a"));
352      Scan s = new Scan();
353      s.setCaching(1);
354      t.append(append_0);
355      t.put(put_0);
356      t.put(put_1);
357      List<Result> results = new LinkedList<>();
358      try (ResultScanner scanner = t.getScanner(s)) {
359        // get one row(should be row3) from the scanner to make sure that we have send a request to
360        // region server, which means we have already set the read point, so later we should not see
361        // the new appended values.
362        Result r = scanner.next();
363        assertNotNull(r);
364        results.add(r);
365        t.append(append_1);
366        t.append(append_2);
367        t.append(append_3);
368        for (;;) {
369          r = scanner.next();
370          if (r == null) {
371            break;
372          }
373          results.add(r);
374        }
375      }
376      return results;
377    } finally {
378      TEST_UTIL.deleteTable(tableName);
379    }
380  }
381
382  @TestTemplate
383  public void testAppendWithoutWAL() throws Exception {
384    List<Result> resultsWithWal = doAppend(true);
385    List<Result> resultsWithoutWal = doAppend(false);
386    assertEquals(resultsWithWal.size(), resultsWithoutWal.size());
387    for (int i = 0; i < resultsWithWal.size(); ++i) {
388      Result resultWithWal = resultsWithWal.get(i);
389      Result resultWithoutWal = resultsWithoutWal.get(i);
390      assertEquals(resultWithWal.rawCells().length, resultWithoutWal.rawCells().length);
391      for (int j = 0; j < resultWithWal.rawCells().length; ++j) {
392        Cell cellWithWal = resultWithWal.rawCells()[j];
393        Cell cellWithoutWal = resultWithoutWal.rawCells()[j];
394        assertArrayEquals(CellUtil.cloneRow(cellWithWal), CellUtil.cloneRow(cellWithoutWal));
395        assertArrayEquals(CellUtil.cloneFamily(cellWithWal), CellUtil.cloneFamily(cellWithoutWal));
396        assertArrayEquals(CellUtil.cloneQualifier(cellWithWal),
397          CellUtil.cloneQualifier(cellWithoutWal));
398        assertArrayEquals(CellUtil.cloneValue(cellWithWal), CellUtil.cloneValue(cellWithoutWal));
399      }
400    }
401  }
402
403  @TestTemplate
404  public void testClientPoolRoundRobin() throws IOException {
405    int poolSize = 3;
406    int numVersions = poolSize * 2;
407    Configuration conf = TEST_UTIL.getConfiguration();
408    conf.set(HConstants.HBASE_CLIENT_IPC_POOL_TYPE, "round-robin");
409    conf.setInt(HConstants.HBASE_CLIENT_IPC_POOL_SIZE, poolSize);
410    TEST_UTIL.createTable(tableName, new byte[][] { FAMILY }, Integer.MAX_VALUE);
411    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
412      final long ts = EnvironmentEdgeManager.currentTime();
413      Get get = new Get(ROW);
414      get.addColumn(FAMILY, QUALIFIER);
415      get.readAllVersions();
416
417      for (int versions = 1; versions <= numVersions; versions++) {
418        Put put = new Put(ROW);
419        put.addColumn(FAMILY, QUALIFIER, ts + versions, VALUE);
420        table.put(put);
421
422        Result result = table.get(get);
423        NavigableMap<Long, byte[]> navigableMap = result.getMap().get(FAMILY).get(QUALIFIER);
424
425        assertEquals(versions, navigableMap.size(), "The number of versions of '"
426          + Bytes.toString(FAMILY) + ":" + Bytes.toString(QUALIFIER) + " did not match");
427        for (Map.Entry<Long, byte[]> entry : navigableMap.entrySet()) {
428          assertTrue(Bytes.equals(VALUE, entry.getValue()),
429            "The value at time " + entry.getKey() + " did not match what was put");
430        }
431      }
432    }
433  }
434
435  @Disabled("Flakey: HBASE-8989")
436  @TestTemplate
437  public void testClientPoolThreadLocal() throws IOException {
438    int poolSize = Integer.MAX_VALUE;
439    int numVersions = 3;
440    Configuration conf = TEST_UTIL.getConfiguration();
441    conf.set(HConstants.HBASE_CLIENT_IPC_POOL_TYPE, "thread-local");
442    conf.setInt(HConstants.HBASE_CLIENT_IPC_POOL_SIZE, poolSize);
443    TEST_UTIL.createTable(tableName, new byte[][] { FAMILY }, 3);
444    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
445      final long ts = EnvironmentEdgeManager.currentTime();
446      final Get get = new Get(ROW);
447      get.addColumn(FAMILY, QUALIFIER);
448      get.readAllVersions();
449
450      for (int versions = 1; versions <= numVersions; versions++) {
451        Put put = new Put(ROW);
452        put.addColumn(FAMILY, QUALIFIER, ts + versions, VALUE);
453        table.put(put);
454
455        Result result = table.get(get);
456        NavigableMap<Long, byte[]> navigableMap = result.getMap().get(FAMILY).get(QUALIFIER);
457
458        assertEquals(versions, navigableMap.size(), "The number of versions of '"
459          + Bytes.toString(FAMILY) + ":" + Bytes.toString(QUALIFIER) + " did not match");
460        for (Map.Entry<Long, byte[]> entry : navigableMap.entrySet()) {
461          assertTrue(Bytes.equals(VALUE, entry.getValue()),
462            "The value at time " + entry.getKey() + " did not match what was put");
463        }
464      }
465
466      final Object waitLock = new Object();
467      ExecutorService executorService = Executors.newFixedThreadPool(numVersions);
468      final AtomicReference<AssertionError> error = new AtomicReference<>(null);
469      for (int versions = numVersions; versions < numVersions * 2; versions++) {
470        final int versionsCopy = versions;
471        executorService.submit((Callable<Void>) () -> {
472          try {
473            Put put = new Put(ROW);
474            put.addColumn(FAMILY, QUALIFIER, ts + versionsCopy, VALUE);
475            table.put(put);
476
477            Result result = table.get(get);
478            NavigableMap<Long, byte[]> navigableMap = result.getMap().get(FAMILY).get(QUALIFIER);
479
480            assertEquals(versionsCopy, navigableMap.size(),
481              "The number of versions of '" + Bytes.toString(FAMILY) + ":"
482                + Bytes.toString(QUALIFIER) + " did not match " + versionsCopy);
483            for (Map.Entry<Long, byte[]> entry : navigableMap.entrySet()) {
484              assertTrue(Bytes.equals(VALUE, entry.getValue()),
485                "The value at time " + entry.getKey() + " did not match what was put");
486            }
487            synchronized (waitLock) {
488              waitLock.wait();
489            }
490          } catch (Exception ignored) {
491          } catch (AssertionError e) {
492            // the error happens in a thread, it won't fail the test,
493            // need to pass it to the caller for proper handling.
494            error.set(e);
495            LOG.error(e.toString(), e);
496          }
497
498          return null;
499        });
500      }
501      synchronized (waitLock) {
502        waitLock.notifyAll();
503      }
504      executorService.shutdownNow();
505      assertNull(error.get());
506    }
507  }
508
509  /**
510   * Test ScanMetrics
511   */
512  @TestTemplate
513  @SuppressWarnings({ "unused", "checkstyle:EmptyBlock" })
514  public void testScanMetrics() throws Exception {
515    // Set up test table:
516    // Create table:
517    TEST_UTIL.createTable(tableName, FAMILY);
518    try (Connection conn = getConnection(); Table ht = conn.getTable(tableName)) {
519      int numOfRegions;
520      try (RegionLocator r = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
521        numOfRegions = r.getStartKeys().length;
522      }
523      // Create 3 rows in the table, with rowkeys starting with "zzz*" so that
524      // scan are forced to hit all the regions.
525      Put put1 = new Put(Bytes.toBytes("zzz1"));
526      put1.addColumn(FAMILY, QUALIFIER, VALUE);
527      Put put2 = new Put(Bytes.toBytes("zzz2"));
528      put2.addColumn(FAMILY, QUALIFIER, VALUE);
529      Put put3 = new Put(Bytes.toBytes("zzz3"));
530      put3.addColumn(FAMILY, QUALIFIER, VALUE);
531      ht.put(Arrays.asList(put1, put2, put3));
532
533      Scan scan1 = new Scan();
534      int numRecords = 0;
535      try (ResultScanner scanner = ht.getScanner(scan1)) {
536        for (Result result : scanner) {
537          numRecords++;
538        }
539
540        LOG.info("test data has {} records.", numRecords);
541
542        // by default, scan metrics collection is turned off
543        assertNull(scanner.getScanMetrics());
544      }
545
546      // turn on scan metrics
547      Scan scan2 = new Scan();
548      scan2.setScanMetricsEnabled(true);
549      scan2.setCaching(numRecords + 1);
550      try (ResultScanner scanner = ht.getScanner(scan2)) {
551        for (Result result : scanner.next(numRecords - 1)) {
552        }
553        assertNotNull(scanner.getScanMetrics());
554      }
555
556      // set caching to 1, because metrics are collected in each roundtrip only
557      scan2 = new Scan();
558      scan2.setScanMetricsEnabled(true);
559      scan2.setCaching(1);
560      try (ResultScanner scanner = ht.getScanner(scan2)) {
561        // per HBASE-5717, this should still collect even if you don't run all the way to
562        // the end of the scanner. So this is asking for 2 of the 3 rows we inserted.
563        for (Result result : scanner.next(numRecords - 1)) {
564        }
565        ScanMetrics scanMetrics = scanner.getScanMetrics();
566        assertEquals(numOfRegions, scanMetrics.countOfRegions.get(),
567          "Did not access all the regions in the table");
568      }
569
570      // check byte counters
571      scan2 = new Scan();
572      scan2.setScanMetricsEnabled(true);
573      scan2.setCaching(1);
574      try (ResultScanner scanner = ht.getScanner(scan2)) {
575        int numBytes = 0;
576        for (Result result : scanner) {
577          for (Cell cell : result.listCells()) {
578            numBytes += PrivateCellUtil.estimatedSerializedSizeOf(cell);
579          }
580        }
581        ScanMetrics scanMetrics = scanner.getScanMetrics();
582        assertEquals(numBytes, scanMetrics.countOfBytesInResults.get(),
583          "Did not count the result bytes");
584      }
585
586      // check byte counters on a small scan
587      scan2 = new Scan();
588      scan2.setScanMetricsEnabled(true);
589      scan2.setCaching(1);
590      scan2.setReadType(ReadType.PREAD);
591      try (ResultScanner scanner = ht.getScanner(scan2)) {
592        int numBytes = 0;
593        for (Result result : scanner) {
594          for (Cell cell : result.listCells()) {
595            numBytes += PrivateCellUtil.estimatedSerializedSizeOf(cell);
596          }
597        }
598        ScanMetrics scanMetrics = scanner.getScanMetrics();
599        assertEquals(numBytes, scanMetrics.countOfBytesInResults.get(),
600          "Did not count the result bytes");
601      }
602
603      // now, test that the metrics are still collected even if you don't call close, but do
604      // run past the end of all the records
605      /**
606       * There seems to be a timing issue here. Comment out for now. Fix when time. Scan
607       * scanWithoutClose = new Scan(); scanWithoutClose.setCaching(1);
608       * scanWithoutClose.setScanMetricsEnabled(true); ResultScanner scannerWithoutClose =
609       * ht.getScanner(scanWithoutClose); for (Result result : scannerWithoutClose.next(numRecords +
610       * 1)) { } ScanMetrics scanMetricsWithoutClose = getScanMetrics(scanWithoutClose);
611       * assertEquals("Did not access all the regions in the table", numOfRegions,
612       * scanMetricsWithoutClose.countOfRegions.get());
613       */
614
615      // finally,
616      // test that the metrics are collected correctly if you both run past all the records,
617      // AND close the scanner
618      Scan scanWithClose = new Scan();
619      // make sure we can set caching up to the number of a scanned values
620      scanWithClose.setCaching(numRecords);
621      scanWithClose.setScanMetricsEnabled(true);
622      try (ResultScanner scannerWithClose = ht.getScanner(scanWithClose)) {
623        for (Result result : scannerWithClose.next(numRecords + 1)) {
624        }
625        scannerWithClose.close();
626        ScanMetrics scanMetricsWithClose = scannerWithClose.getScanMetrics();
627        assertEquals(numOfRegions, scanMetricsWithClose.countOfRegions.get(),
628          "Did not access all the regions in the table");
629      }
630    } finally {
631      TEST_UTIL.deleteTable(tableName);
632    }
633  }
634
635  /**
636   * Tests that cache on write works all the way up from the client-side. Performs inserts, flushes,
637   * and compactions, verifying changes in the block cache along the way.
638   */
639  @TestTemplate
640  public void testCacheOnWriteEvictOnClose() throws Exception {
641    byte[] data = Bytes.toBytes("data");
642    TEST_UTIL.createTable(tableName, FAMILY);
643    try (Connection conn = getConnection(); Table table = conn.getTable(tableName);
644      RegionLocator locator = conn.getRegionLocator(tableName)) {
645      // get the block cache and region
646      String regionName = locator.getAllRegionLocations().get(0).getRegion().getEncodedName();
647
648      HRegion region = TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
649      HStore store = region.getStores().iterator().next();
650      CacheConfig cacheConf = store.getCacheConfig();
651      cacheConf.setCacheDataOnWrite(true);
652      cacheConf.setEvictOnClose(true);
653      BlockCache cache = cacheConf.getBlockCache().get();
654
655      // establish baseline stats
656      long startBlockCount = cache.getBlockCount();
657      long startBlockHits = cache.getStats().getHitCount();
658      long startBlockMiss = cache.getStats().getMissCount();
659
660      // wait till baseline is stable, (minimal 500 ms)
661      for (int i = 0; i < 5; i++) {
662        Thread.sleep(100);
663        if (
664          startBlockCount != cache.getBlockCount()
665            || startBlockHits != cache.getStats().getHitCount()
666            || startBlockMiss != cache.getStats().getMissCount()
667        ) {
668          startBlockCount = cache.getBlockCount();
669          startBlockHits = cache.getStats().getHitCount();
670          startBlockMiss = cache.getStats().getMissCount();
671          i = -1;
672        }
673      }
674
675      // insert data
676      Put put = new Put(ROW);
677      put.addColumn(FAMILY, QUALIFIER, data);
678      table.put(put);
679      assertTrue(Bytes.equals(table.get(new Get(ROW)).value(), data));
680
681      // data was in memstore so don't expect any changes
682      assertEquals(startBlockCount, cache.getBlockCount());
683      assertEquals(startBlockHits, cache.getStats().getHitCount());
684      assertEquals(startBlockMiss, cache.getStats().getMissCount());
685
686      // flush the data
687      LOG.debug("Flushing cache");
688      region.flush(true);
689
690      // expect two more blocks in cache - DATA and ROOT_INDEX
691      // , no change in hits/misses
692      long expectedBlockCount = startBlockCount + 2;
693      long expectedBlockHits = startBlockHits;
694      long expectedBlockMiss = startBlockMiss;
695      assertEquals(expectedBlockCount, cache.getBlockCount());
696      assertEquals(expectedBlockHits, cache.getStats().getHitCount());
697      assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
698      // read the data and expect same blocks, one new hit, no misses
699      assertTrue(Bytes.equals(table.get(new Get(ROW)).value(), data));
700      assertEquals(expectedBlockCount, cache.getBlockCount());
701      assertEquals(++expectedBlockHits, cache.getStats().getHitCount());
702      assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
703      // insert a second column, read the row, no new blocks, one new hit
704      byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
705      byte[] data2 = Bytes.add(data, data);
706      put = new Put(ROW);
707      put.addColumn(FAMILY, QUALIFIER2, data2);
708      table.put(put);
709      Result r = table.get(new Get(ROW));
710      assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER), data));
711      assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER2), data2));
712      assertEquals(expectedBlockCount, cache.getBlockCount());
713      assertEquals(++expectedBlockHits, cache.getStats().getHitCount());
714      assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
715      // flush, one new block
716      LOG.info("Flushing cache");
717      region.flush(true);
718
719      // + 1 for Index Block, +1 for data block
720      expectedBlockCount += 2;
721      assertEquals(expectedBlockCount, cache.getBlockCount());
722      assertEquals(expectedBlockHits, cache.getStats().getHitCount());
723      assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
724      // compact, net minus two blocks, two hits, no misses
725      LOG.info("Compacting");
726      assertEquals(2, store.getStorefilesCount());
727      region.compact(true);
728      store.closeAndArchiveCompactedFiles();
729      waitForStoreFileCount(store, 1, 10000); // wait 10 seconds max
730      assertEquals(1, store.getStorefilesCount());
731      // evicted two data blocks and two index blocks and compaction does not cache new blocks
732      expectedBlockCount = startBlockCount;
733      assertEquals(expectedBlockCount, cache.getBlockCount());
734      expectedBlockHits += 2;
735      assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
736      assertEquals(expectedBlockHits, cache.getStats().getHitCount());
737      // read the row, this should be a cache miss because we don't cache data
738      // blocks on compaction
739      r = table.get(new Get(ROW));
740      assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER), data));
741      assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER2), data2));
742      expectedBlockCount += 1; // cached one data block
743      assertEquals(expectedBlockCount, cache.getBlockCount());
744      assertEquals(expectedBlockHits, cache.getStats().getHitCount());
745      assertEquals(++expectedBlockMiss, cache.getStats().getMissCount());
746    }
747  }
748
749  private void waitForStoreFileCount(HStore store, int count, int timeout)
750    throws InterruptedException {
751    await().atMost(Duration.ofMillis(timeout))
752      .untilAsserted(() -> assertEquals(count, store.getStorefilesCount()));
753  }
754
755  /**
756   * Tests the non cached version of getRegionLocator by moving a region.
757   */
758  @TestTemplate
759  public void testNonCachedGetRegionLocation() throws Exception {
760    // Test Initialization.
761    byte[] family1 = Bytes.toBytes("f1");
762    byte[] family2 = Bytes.toBytes("f2");
763    TEST_UTIL.createTable(tableName, new byte[][] { family1, family2 }, 10);
764    try (Connection conn = getConnection(); Table ignored = conn.getTable(tableName);
765      Admin admin = conn.getAdmin();
766      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
767      List<HRegionLocation> allRegionLocations = locator.getAllRegionLocations();
768      assertEquals(1, allRegionLocations.size());
769      RegionInfo regionInfo = allRegionLocations.get(0).getRegion();
770      ServerName addrBefore = allRegionLocations.get(0).getServerName();
771      // Verify region location before move.
772      HRegionLocation addrCache = locator.getRegionLocation(regionInfo.getStartKey(), false);
773      HRegionLocation addrNoCache = locator.getRegionLocation(regionInfo.getStartKey(), true);
774
775      assertEquals(addrBefore.getPort(), addrCache.getPort());
776      assertEquals(addrBefore.getPort(), addrNoCache.getPort());
777
778      // Make sure more than one server.
779      if (TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size() <= 1) {
780        TEST_UTIL.getMiniHBaseCluster().startRegionServer();
781        Waiter.waitFor(TEST_UTIL.getConfiguration(), 30000, new Waiter.Predicate<Exception>() {
782          @Override
783          public boolean evaluate() throws Exception {
784            return TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size() > 1;
785          }
786        });
787      }
788
789      ServerName addrAfter = null;
790      // Now move the region to a different server.
791      for (int i = 0; i
792          < TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size(); i++) {
793        HRegionServer regionServer = TEST_UTIL.getHBaseCluster().getRegionServer(i);
794        ServerName addr = regionServer.getServerName();
795        if (addr.getPort() != addrBefore.getPort()) {
796          admin.move(regionInfo.getEncodedNameAsBytes(), addr);
797          // Wait for the region to move.
798          Thread.sleep(5000);
799          addrAfter = addr;
800          break;
801        }
802      }
803
804      // Verify the region was moved.
805      addrCache = locator.getRegionLocation(regionInfo.getStartKey(), false);
806      addrNoCache = locator.getRegionLocation(regionInfo.getStartKey(), true);
807      assertNotNull(addrAfter);
808      assertTrue(addrAfter.getPort() != addrCache.getPort());
809      assertEquals(addrAfter.getPort(), addrNoCache.getPort());
810    }
811  }
812
813  /**
814   * Tests getRegionsInRange by creating some regions over which a range of keys spans; then
815   * changing the key range.
816   */
817  @TestTemplate
818  public void testGetRegionsInRange() throws Exception {
819    // Test Initialization.
820    byte[] startKey = Bytes.toBytes("ddc");
821    byte[] endKey = Bytes.toBytes("mmm");
822    TEST_UTIL.createMultiRegionTable(tableName, new byte[][] { FAMILY }, 10);
823
824    int numOfRegions;
825    try (Connection conn = getConnection(); RegionLocator r = conn.getRegionLocator(tableName)) {
826      numOfRegions = r.getStartKeys().length;
827    }
828    assertEquals(26, numOfRegions);
829
830    // Get the regions in this range
831    List<HRegionLocation> regionsList = getRegionsInRange(tableName, startKey, endKey);
832    assertEquals(10, regionsList.size());
833
834    // Change the start key
835    startKey = Bytes.toBytes("fff");
836    regionsList = getRegionsInRange(tableName, startKey, endKey);
837    assertEquals(7, regionsList.size());
838
839    // Change the end key
840    endKey = Bytes.toBytes("nnn");
841    regionsList = getRegionsInRange(tableName, startKey, endKey);
842    assertEquals(8, regionsList.size());
843
844    // Empty start key
845    regionsList = getRegionsInRange(tableName, HConstants.EMPTY_START_ROW, endKey);
846    assertEquals(13, regionsList.size());
847
848    // Empty end key
849    regionsList = getRegionsInRange(tableName, startKey, HConstants.EMPTY_END_ROW);
850    assertEquals(21, regionsList.size());
851
852    // Both start and end keys empty
853    regionsList =
854      getRegionsInRange(tableName, HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
855    assertEquals(26, regionsList.size());
856
857    // Change the end key to somewhere in the last block
858    endKey = Bytes.toBytes("zzz1");
859    regionsList = getRegionsInRange(tableName, startKey, endKey);
860    assertEquals(21, regionsList.size());
861
862    // Change the start key to somewhere in the first block
863    startKey = Bytes.toBytes("aac");
864    regionsList = getRegionsInRange(tableName, startKey, endKey);
865    assertEquals(26, regionsList.size());
866
867    // Make start and end key the same
868    startKey = Bytes.toBytes("ccc");
869    endKey = Bytes.toBytes("ccc");
870    regionsList = getRegionsInRange(tableName, startKey, endKey);
871    assertEquals(1, regionsList.size());
872  }
873
874  private List<HRegionLocation> getRegionsInRange(TableName tableName, byte[] startKey,
875    byte[] endKey) throws IOException {
876    List<HRegionLocation> regionsInRange = new ArrayList<>();
877    byte[] currentKey = startKey;
878    final boolean endKeyIsEndOfTable = Bytes.equals(endKey, HConstants.EMPTY_END_ROW);
879    try (Connection conn = getConnection(); RegionLocator r = conn.getRegionLocator(tableName)) {
880      do {
881        HRegionLocation regionLocation = r.getRegionLocation(currentKey);
882        regionsInRange.add(regionLocation);
883        currentKey = regionLocation.getRegion().getEndKey();
884      } while (
885        !Bytes.equals(currentKey, HConstants.EMPTY_END_ROW)
886          && (endKeyIsEndOfTable || Bytes.compareTo(currentKey, endKey) < 0)
887      );
888      return regionsInRange;
889    }
890  }
891
892  @TestTemplate
893  public void testJira6912() throws Exception {
894    TEST_UTIL.createTable(tableName, new byte[][] { FAMILY }, 10);
895    try (Connection conn = getConnection(); Table foo = conn.getTable(tableName)) {
896      List<Put> puts = new ArrayList<>();
897      for (int i = 0; i != 100; i++) {
898        Put put = new Put(Bytes.toBytes(i));
899        put.addColumn(FAMILY, FAMILY, Bytes.toBytes(i));
900        puts.add(put);
901      }
902      foo.put(puts);
903      // If i comment this out it works
904      TEST_UTIL.flush();
905
906      Scan scan = new Scan();
907      scan.withStartRow(Bytes.toBytes(1));
908      scan.withStopRow(Bytes.toBytes(3));
909      scan.addColumn(FAMILY, FAMILY);
910      scan.setFilter(
911        new RowFilter(CompareOperator.NOT_EQUAL, new BinaryComparator(Bytes.toBytes(1))));
912
913      try (ResultScanner scanner = foo.getScanner(scan)) {
914        Result[] bar = scanner.next(100);
915        assertEquals(1, bar.length);
916      }
917    }
918  }
919
920  @TestTemplate
921  public void testScanNullQualifier() throws IOException {
922    TEST_UTIL.createTable(tableName, FAMILY);
923    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
924      Put put = new Put(ROW);
925      put.addColumn(FAMILY, QUALIFIER, VALUE);
926      table.put(put);
927
928      put = new Put(ROW);
929      put.addColumn(FAMILY, null, VALUE);
930      table.put(put);
931      LOG.info("Row put");
932
933      Scan scan = new Scan();
934      scan.addColumn(FAMILY, null);
935
936      ResultScanner scanner = table.getScanner(scan);
937      Result[] bar = scanner.next(100);
938      assertEquals(1, bar.length);
939      assertEquals(1, bar[0].size());
940
941      scan = new Scan();
942      scan.addFamily(FAMILY);
943
944      scanner = table.getScanner(scan);
945      bar = scanner.next(100);
946      assertEquals(1, bar.length);
947      assertEquals(2, bar[0].size());
948    }
949  }
950
951  @TestTemplate
952  public void testRawScanRespectsVersions() throws Exception {
953    TEST_UTIL.createTable(tableName, FAMILY);
954    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
955      byte[] row = Bytes.toBytes("row");
956
957      // put the same row 4 times, with different values
958      Put p = new Put(row);
959      p.addColumn(FAMILY, QUALIFIER, 10, VALUE);
960      table.put(p);
961      p = new Put(row);
962      p.addColumn(FAMILY, QUALIFIER, 11, ArrayUtils.add(VALUE, (byte) 2));
963      table.put(p);
964
965      p = new Put(row);
966      p.addColumn(FAMILY, QUALIFIER, 12, ArrayUtils.add(VALUE, (byte) 3));
967      table.put(p);
968
969      p = new Put(row);
970      p.addColumn(FAMILY, QUALIFIER, 13, ArrayUtils.add(VALUE, (byte) 4));
971      table.put(p);
972
973      int versions = 4;
974      Scan s = new Scan().withStartRow(row);
975      // get all the possible versions
976      s.readAllVersions();
977      s.setRaw(true);
978
979      try (ResultScanner scanner = table.getScanner(s)) {
980        int count = 0;
981        for (Result r : scanner) {
982          assertEquals(versions, r.listCells().size(),
983            "Found an unexpected number of results for the row!");
984          count++;
985        }
986        assertEquals(1, count,
987          "Found more than a single row when raw scanning the table with a single row!");
988      }
989
990      // then if we decrease the number of versions, but keep the scan raw, we should see exactly
991      // that number of versions
992      versions = 2;
993      s.readVersions(versions);
994      try (ResultScanner scanner = table.getScanner(s)) {
995        int count = 0;
996        for (Result r : scanner) {
997          assertEquals(versions, r.listCells().size(),
998            "Found an unexpected number of results for the row!");
999          count++;
1000        }
1001        assertEquals(1, count,
1002          "Found more than a single row when raw scanning the table with a single row!");
1003      }
1004
1005      // finally, if we turn off raw scanning, but max out the number of versions, we should go back
1006      // to seeing just three
1007      versions = 3;
1008      s.readVersions(versions);
1009      try (ResultScanner scanner = table.getScanner(s)) {
1010        int count = 0;
1011        for (Result r : scanner) {
1012          assertEquals(versions, r.listCells().size(),
1013            "Found an unexpected number of results for the row!");
1014          count++;
1015        }
1016        assertEquals(1, count,
1017          "Found more than a single row when raw scanning the table with a single row!");
1018      }
1019
1020    }
1021    TEST_UTIL.deleteTable(tableName);
1022  }
1023
1024  @TestTemplate
1025  public void testEmptyFilterList() throws Exception {
1026    // Test Initialization.
1027    TEST_UTIL.createTable(tableName, FAMILY);
1028    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
1029      // Insert one row each region
1030      Put put = new Put(Bytes.toBytes("row"));
1031      put.addColumn(FAMILY, QUALIFIER, VALUE);
1032      table.put(put);
1033
1034      List<Result> scanResults = new LinkedList<>();
1035      Scan scan = new Scan();
1036      scan.setFilter(new FilterList());
1037      try (ResultScanner scanner = table.getScanner(scan)) {
1038        for (Result r : scanner) {
1039          scanResults.add(r);
1040        }
1041      }
1042      assertEquals(1, scanResults.size());
1043      Get g = new Get(Bytes.toBytes("row"));
1044      g.setFilter(new FilterList());
1045      Result getResult = table.get(g);
1046      Result scanResult = scanResults.get(0);
1047      assertEquals(scanResult.rawCells().length, getResult.rawCells().length);
1048      for (int i = 0; i != scanResult.rawCells().length; ++i) {
1049        Cell scanCell = scanResult.rawCells()[i];
1050        Cell getCell = getResult.rawCells()[i];
1051        assertEquals(0, Bytes.compareTo(CellUtil.cloneRow(scanCell), CellUtil.cloneRow(getCell)));
1052        assertEquals(0,
1053          Bytes.compareTo(CellUtil.cloneFamily(scanCell), CellUtil.cloneFamily(getCell)));
1054        assertEquals(0,
1055          Bytes.compareTo(CellUtil.cloneQualifier(scanCell), CellUtil.cloneQualifier(getCell)));
1056        assertEquals(0,
1057          Bytes.compareTo(CellUtil.cloneValue(scanCell), CellUtil.cloneValue(getCell)));
1058      }
1059    }
1060  }
1061
1062  @TestTemplate
1063  public void testSmallScan() throws Exception {
1064    // Test Initialization.
1065    TEST_UTIL.createTable(tableName, FAMILY);
1066    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
1067      // Insert one row each region
1068      int insertNum = 10;
1069      for (int i = 0; i < 10; i++) {
1070        Put put = new Put(Bytes.toBytes("row" + String.format("%03d", i)));
1071        put.addColumn(FAMILY, QUALIFIER, VALUE);
1072        table.put(put);
1073      }
1074
1075      // normal scan
1076      try (ResultScanner scanner = table.getScanner(new Scan())) {
1077        int count = 0;
1078        for (Result r : scanner) {
1079          assertFalse(r.isEmpty());
1080          count++;
1081        }
1082        assertEquals(insertNum, count);
1083      }
1084
1085      // small scan
1086      Scan scan = new Scan().withStartRow(HConstants.EMPTY_START_ROW)
1087        .withStopRow(HConstants.EMPTY_END_ROW, true);
1088      scan.setReadType(ReadType.PREAD);
1089      scan.setCaching(2);
1090      try (ResultScanner scanner = table.getScanner(scan)) {
1091        int count = 0;
1092        for (Result r : scanner) {
1093          assertFalse(r.isEmpty());
1094          count++;
1095        }
1096        assertEquals(insertNum, count);
1097      }
1098    }
1099  }
1100
1101  @TestTemplate
1102  public void testFilterAllRecords() throws IOException {
1103    Scan scan = new Scan();
1104    scan.setBatch(1);
1105    scan.setCaching(1);
1106    // Filter out any records
1107    scan.setFilter(new FilterList(new FirstKeyOnlyFilter(), new InclusiveStopFilter(new byte[0])));
1108    try (Connection conn = getConnection();
1109      Table table = conn.getTable(TableName.META_TABLE_NAME)) {
1110      try (ResultScanner s = table.getScanner(scan)) {
1111        assertNull(s.next());
1112      }
1113    }
1114  }
1115
1116  @TestTemplate
1117  public void testCellSizeLimit() throws IOException {
1118    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName)
1119      .setValue(HRegion.HBASE_MAX_CELL_SIZE_KEY, Integer.toString(10 * 1024))
1120      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)).build();
1121    try (Connection conn = getConnection()) {
1122      try (Admin admin = conn.getAdmin()) {
1123        admin.createTable(tableDescriptor);
1124      }
1125      try (Table t = conn.getTable(tableName)) {
1126        // Will succeed
1127        t.put(new Put(ROW).addColumn(FAMILY, QUALIFIER, Bytes.toBytes(0L)));
1128        t.increment(new Increment(ROW).addColumn(FAMILY, QUALIFIER, 1L));
1129
1130        // Will succeed
1131        t.put(new Put(ROW).addColumn(FAMILY, QUALIFIER, new byte[9 * 1024]));
1132
1133        // Will fail
1134        assertThrows(IOException.class,
1135          () -> t.put(new Put(ROW).addColumn(FAMILY, QUALIFIER, new byte[10 * 1024])),
1136          "Oversize cell failed to trigger exception");
1137        assertThrows(IOException.class,
1138          () -> t.append(new Append(ROW).addColumn(FAMILY, QUALIFIER, new byte[2 * 1024])),
1139          "Oversize cell failed to trigger exception");
1140      }
1141    }
1142  }
1143
1144  @TestTemplate
1145  public void testCellSizeNoLimit() throws IOException {
1146
1147    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName)
1148      .setValue(HRegion.HBASE_MAX_CELL_SIZE_KEY, Integer.toString(0))
1149      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)).build();
1150    try (Connection conn = getConnection()) {
1151      try (Admin admin = conn.getAdmin()) {
1152        admin.createTable(tableDescriptor);
1153      }
1154      // Will succeed
1155      try (Table ht = conn.getTable(tableName)) {
1156        ht.put(new Put(ROW).addColumn(FAMILY, QUALIFIER,
1157          new byte[HRegion.DEFAULT_MAX_CELL_SIZE - 1024]));
1158        ht.append(new Append(ROW).addColumn(FAMILY, QUALIFIER, new byte[1024 + 1]));
1159      }
1160    }
1161  }
1162
1163  @TestTemplate
1164  public void testDeleteSpecifiedVersionOfSpecifiedColumn() throws Exception {
1165    TEST_UTIL.createTable(tableName, FAMILY, 5);
1166    byte[][] VALUES = makeN(VALUE, 5);
1167    long[] ts = { 1000, 2000, 3000, 4000, 5000 };
1168    try (Connection conn = getConnection(); Table ht = conn.getTable(tableName)) {
1169      Put put = new Put(ROW);
1170      // Put version 1000,2000,3000,4000 of column FAMILY:QUALIFIER
1171      for (int t = 0; t < 4; t++) {
1172        put.addColumn(FAMILY, QUALIFIER, ts[t], VALUES[t]);
1173      }
1174      ht.put(put);
1175
1176      Delete delete = new Delete(ROW);
1177      // Delete version 3000 of column FAMILY:QUALIFIER
1178      delete.addColumn(FAMILY, QUALIFIER, ts[2]);
1179      ht.delete(delete);
1180
1181      Get get = new Get(ROW);
1182      get.addColumn(FAMILY, QUALIFIER);
1183      get.readVersions(Integer.MAX_VALUE);
1184      Result result = ht.get(get);
1185      // verify version 1000,2000,4000 remains for column FAMILY:QUALIFIER
1186      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { ts[0], ts[1], ts[3] },
1187        new byte[][] { VALUES[0], VALUES[1], VALUES[3] }, 0, 2);
1188
1189      delete = new Delete(ROW);
1190      // Delete a version 5000 of column FAMILY:QUALIFIER which didn't exist
1191      delete.addColumn(FAMILY, QUALIFIER, ts[4]);
1192      ht.delete(delete);
1193
1194      get = new Get(ROW);
1195      get.addColumn(FAMILY, QUALIFIER);
1196      get.readVersions(Integer.MAX_VALUE);
1197      result = ht.get(get);
1198      // verify version 1000,2000,4000 remains for column FAMILY:QUALIFIER
1199      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { ts[0], ts[1], ts[3] },
1200        new byte[][] { VALUES[0], VALUES[1], VALUES[3] }, 0, 2);
1201    }
1202  }
1203
1204  @TestTemplate
1205  public void testDeleteLatestVersionOfSpecifiedColumn() throws Exception {
1206    TEST_UTIL.createTable(tableName, FAMILY, 5);
1207    byte[][] VALUES = makeN(VALUE, 5);
1208    long[] ts = { 1000, 2000, 3000, 4000, 5000 };
1209    try (Connection conn = getConnection(); Table ht = conn.getTable(tableName)) {
1210      Put put = new Put(ROW);
1211      // Put version 1000,2000,3000,4000 of column FAMILY:QUALIFIER
1212      for (int t = 0; t < 4; t++) {
1213        put.addColumn(FAMILY, QUALIFIER, ts[t], VALUES[t]);
1214      }
1215      ht.put(put);
1216
1217      Delete delete = new Delete(ROW);
1218      // Delete latest version of column FAMILY:QUALIFIER
1219      delete.addColumn(FAMILY, QUALIFIER);
1220      ht.delete(delete);
1221
1222      Get get = new Get(ROW);
1223      get.addColumn(FAMILY, QUALIFIER);
1224      get.readVersions(Integer.MAX_VALUE);
1225      Result result = ht.get(get);
1226      // verify version 1000,2000,3000 remains for column FAMILY:QUALIFIER
1227      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { ts[0], ts[1], ts[2] },
1228        new byte[][] { VALUES[0], VALUES[1], VALUES[2] }, 0, 2);
1229
1230      delete = new Delete(ROW);
1231      // Delete two latest version of column FAMILY:QUALIFIER
1232      delete.addColumn(FAMILY, QUALIFIER);
1233      delete.addColumn(FAMILY, QUALIFIER);
1234      ht.delete(delete);
1235
1236      get = new Get(ROW);
1237      get.addColumn(FAMILY, QUALIFIER);
1238      get.readVersions(Integer.MAX_VALUE);
1239      result = ht.get(get);
1240      // verify version 1000 remains for column FAMILY:QUALIFIER
1241      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { ts[0] },
1242        new byte[][] { VALUES[0] }, 0, 0);
1243
1244      put = new Put(ROW);
1245      // Put a version 5000 of column FAMILY:QUALIFIER
1246      put.addColumn(FAMILY, QUALIFIER, ts[4], VALUES[4]);
1247      ht.put(put);
1248
1249      get = new Get(ROW);
1250      get.addColumn(FAMILY, QUALIFIER);
1251      get.readVersions(Integer.MAX_VALUE);
1252      result = ht.get(get);
1253      // verify version 1000,5000 remains for column FAMILY:QUALIFIER
1254      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { ts[0], ts[4] },
1255        new byte[][] { VALUES[0], VALUES[4] }, 0, 1);
1256    }
1257  }
1258
1259  /**
1260   * Test for HBASE-17125
1261   */
1262  @TestTemplate
1263  public void testReadWithFilter() throws Exception {
1264    TEST_UTIL.createTable(tableName, FAMILY, 3);
1265    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
1266      byte[] VALUEA = Bytes.toBytes("value-a");
1267      byte[] VALUEB = Bytes.toBytes("value-b");
1268      long[] ts = { 1000, 2000, 3000, 4000 };
1269
1270      Put put = new Put(ROW);
1271      // Put version 1000,2000,3000,4000 of column FAMILY:QUALIFIER
1272      for (int t = 0; t <= 3; t++) {
1273        if (t <= 1) {
1274          put.addColumn(FAMILY, QUALIFIER, ts[t], VALUEA);
1275        } else {
1276          put.addColumn(FAMILY, QUALIFIER, ts[t], VALUEB);
1277        }
1278      }
1279      table.put(put);
1280
1281      Scan scan = new Scan()
1282        .setFilter(new ValueFilter(CompareOperator.EQUAL, new SubstringComparator("value-a")))
1283        .readVersions(3);
1284      Result result = getSingleScanResult(table, scan);
1285      // ts[0] has gone from user view. Only read ts[2] which value is less or equal to 3
1286      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { ts[1] }, new byte[][] { VALUEA },
1287        0, 0);
1288
1289      Get get = new Get(ROW)
1290        .setFilter(new ValueFilter(CompareOperator.EQUAL, new SubstringComparator("value-a")))
1291        .readVersions(3);
1292      result = table.get(get);
1293      // ts[0] has gone from user view. Only read ts[2] which value is less or equal to 3
1294      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { ts[1] }, new byte[][] { VALUEA },
1295        0, 0);
1296
1297      // Test with max versions 1, it should still read ts[1]
1298      scan = new Scan()
1299        .setFilter(new ValueFilter(CompareOperator.EQUAL, new SubstringComparator("value-a")))
1300        .readVersions(1);
1301      result = getSingleScanResult(table, scan);
1302      // ts[0] has gone from user view. Only read ts[2] which value is less or equal to 3
1303      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { ts[1] }, new byte[][] { VALUEA },
1304        0, 0);
1305
1306      // Test with max versions 1, it should still read ts[1]
1307      get = new Get(ROW)
1308        .setFilter(new ValueFilter(CompareOperator.EQUAL, new SubstringComparator("value-a")))
1309        .readVersions(1);
1310      result = table.get(get);
1311      // ts[0] has gone from user view. Only read ts[2] which value is less or equal to 3
1312      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { ts[1] }, new byte[][] { VALUEA },
1313        0, 0);
1314
1315      // Test with max versions 5, it should still read ts[1]
1316      scan = new Scan()
1317        .setFilter(new ValueFilter(CompareOperator.EQUAL, new SubstringComparator("value-a")))
1318        .readVersions(5);
1319      result = getSingleScanResult(table, scan);
1320      // ts[0] has gone from user view. Only read ts[2] which value is less or equal to 3
1321      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { ts[1] }, new byte[][] { VALUEA },
1322        0, 0);
1323
1324      // Test with max versions 5, it should still read ts[1]
1325      get = new Get(ROW)
1326        .setFilter(new ValueFilter(CompareOperator.EQUAL, new SubstringComparator("value-a")))
1327        .readVersions(5);
1328      result = table.get(get);
1329      // ts[0] has gone from user view. Only read ts[2] which value is less or equal to 3
1330      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { ts[1] }, new byte[][] { VALUEA },
1331        0, 0);
1332    }
1333  }
1334
1335  @TestTemplate
1336  public void testCellUtilTypeMethods() throws IOException {
1337    TEST_UTIL.createTable(tableName, FAMILY);
1338    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
1339      final byte[] row = Bytes.toBytes("p");
1340      Put p = new Put(row);
1341      p.addColumn(FAMILY, QUALIFIER, VALUE);
1342      table.put(p);
1343
1344      try (ResultScanner scanner = table.getScanner(new Scan())) {
1345        Result result = scanner.next();
1346        assertNotNull(result);
1347        CellScanner cs = result.cellScanner();
1348        assertTrue(cs.advance());
1349        Cell c = cs.current();
1350        assertTrue(CellUtil.isPut(c));
1351        assertFalse(CellUtil.isDelete(c));
1352        assertFalse(cs.advance());
1353        assertNull(scanner.next());
1354      }
1355
1356      Delete d = new Delete(row);
1357      d.addColumn(FAMILY, QUALIFIER);
1358      table.delete(d);
1359
1360      Scan scan = new Scan();
1361      scan.setRaw(true);
1362      try (ResultScanner scanner = table.getScanner(scan)) {
1363        Result result = scanner.next();
1364        assertNotNull(result);
1365        CellScanner cs = result.cellScanner();
1366        assertTrue(cs.advance());
1367
1368        // First cell should be the delete (masking the Put)
1369        Cell c = cs.current();
1370        assertTrue(CellUtil.isDelete(c), "Cell should be a Delete: " + c);
1371        assertFalse(CellUtil.isPut(c), "Cell should not be a Put: " + c);
1372
1373        // Second cell should be the original Put
1374        assertTrue(cs.advance());
1375        c = cs.current();
1376        assertFalse(CellUtil.isDelete(c), "Cell should not be a Delete: " + c);
1377        assertTrue(CellUtil.isPut(c), "Cell should be a Put: " + c);
1378
1379        // No more cells in this row
1380        assertFalse(cs.advance());
1381
1382        // No more results in this scan
1383        assertNull(scanner.next());
1384      }
1385    }
1386  }
1387
1388  @TestTemplate
1389  public void testCreateTableWithZeroRegionReplicas() throws Exception {
1390    TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
1391      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(Bytes.toBytes("cf")))
1392      .setRegionReplication(0).build();
1393
1394    try (Connection conn = getConnection(); Admin admin = conn.getAdmin()) {
1395      assertThrows(DoNotRetryIOException.class, () -> admin.createTable(desc));
1396    }
1397  }
1398
1399  @TestTemplate
1400  public void testModifyTableWithZeroRegionReplicas() throws Exception {
1401    TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
1402      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(Bytes.toBytes("cf"))).build();
1403    TableDescriptor newDesc =
1404      TableDescriptorBuilder.newBuilder(desc).setRegionReplication(0).build();
1405    try (Connection conn = getConnection(); Admin admin = conn.getAdmin()) {
1406      admin.createTable(desc);
1407      assertThrows(DoNotRetryIOException.class, () -> admin.modifyTable(newDesc));
1408    }
1409  }
1410
1411  @TestTemplate
1412  public void testModifyTableWithMemstoreData() throws Exception {
1413    createTableAndValidateTableSchemaModification(tableName, true);
1414  }
1415
1416  @TestTemplate
1417  public void testDeleteCFWithMemstoreData() throws Exception {
1418    createTableAndValidateTableSchemaModification(tableName, false);
1419  }
1420
1421  /**
1422   * Create table and validate online schema modification
1423   * @param tableName   Table name
1424   * @param modifyTable Modify table if true otherwise delete column family
1425   * @throws IOException in case of failures
1426   */
1427  private void createTableAndValidateTableSchemaModification(TableName tableName,
1428    boolean modifyTable) throws Exception {
1429    try (Connection conn = getConnection(); Admin admin = conn.getAdmin()) {
1430      // Create table with two Cfs
1431      byte[] cf1 = Bytes.toBytes("cf1");
1432      byte[] cf2 = Bytes.toBytes("cf2");
1433      TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(tableName)
1434        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(cf1))
1435        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(cf2)).build();
1436      admin.createTable(tableDesc);
1437
1438      Table t = TEST_UTIL.getConnection().getTable(tableName);
1439      // Insert few records and flush the table
1440      t.put(new Put(ROW).addColumn(cf1, QUALIFIER, Bytes.toBytes("val1")));
1441      t.put(new Put(ROW).addColumn(cf2, QUALIFIER, Bytes.toBytes("val2")));
1442      admin.flush(tableName);
1443      Path tableDir = CommonFSUtils.getTableDir(TEST_UTIL.getDefaultRootDirPath(), tableName);
1444      List<Path> regionDirs = FSUtils.getRegionDirs(TEST_UTIL.getTestFileSystem(), tableDir);
1445      assertEquals(1, regionDirs.size());
1446      List<Path> familyDirs =
1447        FSUtils.getFamilyDirs(TEST_UTIL.getTestFileSystem(), regionDirs.get(0));
1448      assertEquals(2, familyDirs.size());
1449
1450      // Insert record but dont flush the table
1451      t.put(new Put(ROW).addColumn(cf1, QUALIFIER, Bytes.toBytes("val2")));
1452      t.put(new Put(ROW).addColumn(cf2, QUALIFIER, Bytes.toBytes("val2")));
1453
1454      if (modifyTable) {
1455        tableDesc = TableDescriptorBuilder.newBuilder(tableDesc).removeColumnFamily(cf2).build();
1456        admin.modifyTable(tableDesc);
1457      } else {
1458        admin.deleteColumnFamily(tableName, cf2);
1459      }
1460      // After table modification or delete family there should be only one CF in FS
1461      familyDirs = FSUtils.getFamilyDirs(TEST_UTIL.getTestFileSystem(), regionDirs.get(0));
1462      assertEquals(1, familyDirs.size(), "CF dir count should be 1, but was " + familyDirs.size());
1463    }
1464  }
1465}