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}