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.regionserver; 019 020import static org.apache.hadoop.hbase.HBaseTestingUtil.COLUMNS; 021import static org.apache.hadoop.hbase.HBaseTestingUtil.fam1; 022import static org.apache.hadoop.hbase.HBaseTestingUtil.fam2; 023import static org.apache.hadoop.hbase.HBaseTestingUtil.fam3; 024import static org.junit.Assert.assertArrayEquals; 025import static org.junit.Assert.assertEquals; 026import static org.junit.Assert.assertFalse; 027import static org.junit.Assert.assertNotNull; 028import static org.junit.Assert.assertNull; 029import static org.junit.Assert.assertThrows; 030import static org.junit.Assert.assertTrue; 031import static org.junit.Assert.fail; 032import static org.mockito.ArgumentMatchers.any; 033import static org.mockito.ArgumentMatchers.anyLong; 034import static org.mockito.ArgumentMatchers.anyString; 035import static org.mockito.ArgumentMatchers.isA; 036import static org.mockito.Mockito.atLeast; 037import static org.mockito.Mockito.doAnswer; 038import static org.mockito.Mockito.doThrow; 039import static org.mockito.Mockito.mock; 040import static org.mockito.Mockito.never; 041import static org.mockito.Mockito.spy; 042import static org.mockito.Mockito.times; 043import static org.mockito.Mockito.verify; 044import static org.mockito.Mockito.when; 045 046import java.io.IOException; 047import java.io.InterruptedIOException; 048import java.math.BigDecimal; 049import java.security.PrivilegedExceptionAction; 050import java.util.ArrayList; 051import java.util.Arrays; 052import java.util.Collection; 053import java.util.HashSet; 054import java.util.List; 055import java.util.Map; 056import java.util.NavigableMap; 057import java.util.Objects; 058import java.util.Set; 059import java.util.TreeMap; 060import java.util.concurrent.Callable; 061import java.util.concurrent.CountDownLatch; 062import java.util.concurrent.ExecutorService; 063import java.util.concurrent.Executors; 064import java.util.concurrent.Future; 065import java.util.concurrent.TimeUnit; 066import java.util.concurrent.atomic.AtomicBoolean; 067import java.util.concurrent.atomic.AtomicInteger; 068import java.util.concurrent.atomic.AtomicLong; 069import java.util.concurrent.atomic.AtomicReference; 070import java.util.stream.Collectors; 071import org.apache.commons.lang3.RandomStringUtils; 072import org.apache.hadoop.conf.Configuration; 073import org.apache.hadoop.fs.FSDataOutputStream; 074import org.apache.hadoop.fs.FileStatus; 075import org.apache.hadoop.fs.FileSystem; 076import org.apache.hadoop.fs.Path; 077import org.apache.hadoop.hbase.ArrayBackedTag; 078import org.apache.hadoop.hbase.Cell; 079import org.apache.hadoop.hbase.Cell.Type; 080import org.apache.hadoop.hbase.CellBuilderFactory; 081import org.apache.hadoop.hbase.CellBuilderType; 082import org.apache.hadoop.hbase.CellUtil; 083import org.apache.hadoop.hbase.CompareOperator; 084import org.apache.hadoop.hbase.CompatibilitySingletonFactory; 085import org.apache.hadoop.hbase.DoNotRetryIOException; 086import org.apache.hadoop.hbase.DroppedSnapshotException; 087import org.apache.hadoop.hbase.ExtendedCell; 088import org.apache.hadoop.hbase.ExtendedCellBuilderFactory; 089import org.apache.hadoop.hbase.HBaseClassTestRule; 090import org.apache.hadoop.hbase.HBaseConfiguration; 091import org.apache.hadoop.hbase.HBaseTestingUtil; 092import org.apache.hadoop.hbase.HConstants; 093import org.apache.hadoop.hbase.HConstants.OperationStatusCode; 094import org.apache.hadoop.hbase.HDFSBlocksDistribution; 095import org.apache.hadoop.hbase.KeyValue; 096import org.apache.hadoop.hbase.MultithreadedTestUtil; 097import org.apache.hadoop.hbase.MultithreadedTestUtil.RepeatingTestThread; 098import org.apache.hadoop.hbase.MultithreadedTestUtil.TestThread; 099import org.apache.hadoop.hbase.NotServingRegionException; 100import org.apache.hadoop.hbase.PrivateCellUtil; 101import org.apache.hadoop.hbase.RegionTooBusyException; 102import org.apache.hadoop.hbase.ServerName; 103import org.apache.hadoop.hbase.SingleProcessHBaseCluster; 104import org.apache.hadoop.hbase.StartTestingClusterOption; 105import org.apache.hadoop.hbase.TableName; 106import org.apache.hadoop.hbase.TagType; 107import org.apache.hadoop.hbase.Waiter; 108import org.apache.hadoop.hbase.client.Append; 109import org.apache.hadoop.hbase.client.CheckAndMutate; 110import org.apache.hadoop.hbase.client.CheckAndMutateResult; 111import org.apache.hadoop.hbase.client.ClientInternalHelper; 112import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 113import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 114import org.apache.hadoop.hbase.client.Delete; 115import org.apache.hadoop.hbase.client.Durability; 116import org.apache.hadoop.hbase.client.Get; 117import org.apache.hadoop.hbase.client.Increment; 118import org.apache.hadoop.hbase.client.Mutation; 119import org.apache.hadoop.hbase.client.Put; 120import org.apache.hadoop.hbase.client.RegionInfo; 121import org.apache.hadoop.hbase.client.RegionInfoBuilder; 122import org.apache.hadoop.hbase.client.Result; 123import org.apache.hadoop.hbase.client.RowMutations; 124import org.apache.hadoop.hbase.client.Scan; 125import org.apache.hadoop.hbase.client.Table; 126import org.apache.hadoop.hbase.client.TableDescriptor; 127import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 128import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 129import org.apache.hadoop.hbase.coprocessor.MetaTableMetrics; 130import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor; 131import org.apache.hadoop.hbase.coprocessor.RegionObserver; 132import org.apache.hadoop.hbase.exceptions.FailedSanityCheckException; 133import org.apache.hadoop.hbase.filter.BigDecimalComparator; 134import org.apache.hadoop.hbase.filter.BinaryComparator; 135import org.apache.hadoop.hbase.filter.ColumnCountGetFilter; 136import org.apache.hadoop.hbase.filter.Filter; 137import org.apache.hadoop.hbase.filter.FilterBase; 138import org.apache.hadoop.hbase.filter.FilterList; 139import org.apache.hadoop.hbase.filter.NullComparator; 140import org.apache.hadoop.hbase.filter.PrefixFilter; 141import org.apache.hadoop.hbase.filter.SingleColumnValueExcludeFilter; 142import org.apache.hadoop.hbase.filter.SingleColumnValueFilter; 143import org.apache.hadoop.hbase.filter.SubstringComparator; 144import org.apache.hadoop.hbase.filter.ValueFilter; 145import org.apache.hadoop.hbase.io.TimeRange; 146import org.apache.hadoop.hbase.io.hfile.HFile; 147import org.apache.hadoop.hbase.monitoring.MonitoredRPCHandler; 148import org.apache.hadoop.hbase.monitoring.MonitoredTask; 149import org.apache.hadoop.hbase.monitoring.TaskMonitor; 150import org.apache.hadoop.hbase.regionserver.Region.Operation; 151import org.apache.hadoop.hbase.regionserver.Region.RowLock; 152import org.apache.hadoop.hbase.regionserver.TestHStore.FaultyFileSystem; 153import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequestImpl; 154import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker; 155import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory; 156import org.apache.hadoop.hbase.regionserver.wal.AbstractFSWAL; 157import org.apache.hadoop.hbase.regionserver.wal.AsyncFSWAL; 158import org.apache.hadoop.hbase.regionserver.wal.FSHLog; 159import org.apache.hadoop.hbase.regionserver.wal.MetricsWALSource; 160import org.apache.hadoop.hbase.regionserver.wal.WALUtil; 161import org.apache.hadoop.hbase.replication.regionserver.ReplicationObserver; 162import org.apache.hadoop.hbase.security.User; 163import org.apache.hadoop.hbase.test.MetricsAssertHelper; 164import org.apache.hadoop.hbase.testclassification.LargeTests; 165import org.apache.hadoop.hbase.testclassification.VerySlowRegionServerTests; 166import org.apache.hadoop.hbase.util.Bytes; 167import org.apache.hadoop.hbase.util.CommonFSUtils; 168import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 169import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper; 170import org.apache.hadoop.hbase.util.HFileArchiveUtil; 171import org.apache.hadoop.hbase.util.IncrementingEnvironmentEdge; 172import org.apache.hadoop.hbase.util.ManualEnvironmentEdge; 173import org.apache.hadoop.hbase.util.Threads; 174import org.apache.hadoop.hbase.wal.AbstractFSWALProvider; 175import org.apache.hadoop.hbase.wal.FaultyFSLog; 176import org.apache.hadoop.hbase.wal.NettyAsyncFSWALConfigHelper; 177import org.apache.hadoop.hbase.wal.WAL; 178import org.apache.hadoop.hbase.wal.WALEdit; 179import org.apache.hadoop.hbase.wal.WALEditInternalHelper; 180import org.apache.hadoop.hbase.wal.WALFactory; 181import org.apache.hadoop.hbase.wal.WALKeyImpl; 182import org.apache.hadoop.hbase.wal.WALProvider; 183import org.apache.hadoop.hbase.wal.WALProvider.Writer; 184import org.apache.hadoop.hbase.wal.WALSplitUtil; 185import org.apache.hadoop.hbase.wal.WALStreamReader; 186import org.junit.After; 187import org.junit.Assert; 188import org.junit.Before; 189import org.junit.ClassRule; 190import org.junit.Ignore; 191import org.junit.Rule; 192import org.junit.Test; 193import org.junit.experimental.categories.Category; 194import org.junit.rules.ExpectedException; 195import org.junit.rules.TestName; 196import org.mockito.ArgumentCaptor; 197import org.mockito.ArgumentMatcher; 198import org.mockito.invocation.InvocationOnMock; 199import org.mockito.stubbing.Answer; 200import org.slf4j.Logger; 201import org.slf4j.LoggerFactory; 202 203import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 204import org.apache.hbase.thirdparty.com.google.protobuf.ByteString; 205import org.apache.hbase.thirdparty.io.netty.channel.EventLoopGroup; 206import org.apache.hbase.thirdparty.io.netty.channel.nio.NioEventLoopGroup; 207import org.apache.hbase.thirdparty.io.netty.channel.socket.nio.NioSocketChannel; 208 209import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 210import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.CompactionDescriptor; 211import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.FlushDescriptor; 212import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.FlushDescriptor.FlushAction; 213import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.FlushDescriptor.StoreFlushDescriptor; 214import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.RegionEventDescriptor; 215import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.StoreDescriptor; 216 217/** 218 * Basic stand-alone testing of HRegion. No clusters! A lot of the meta information for an HRegion 219 * now lives inside other HRegions or in the HBaseMaster, so only basic testing is possible. 220 */ 221@Category({ VerySlowRegionServerTests.class, LargeTests.class }) 222@SuppressWarnings("deprecation") 223public class TestHRegion { 224 225 @ClassRule 226 public static final HBaseClassTestRule CLASS_RULE = 227 HBaseClassTestRule.forClass(TestHRegion.class); 228 229 // Do not spin up clusters in here. If you need to spin up a cluster, do it 230 // over in TestHRegionOnCluster. 231 private static final Logger LOG = LoggerFactory.getLogger(TestHRegion.class); 232 @Rule 233 public TestName name = new TestName(); 234 @Rule 235 public final ExpectedException thrown = ExpectedException.none(); 236 237 private static final String COLUMN_FAMILY = "MyCF"; 238 private static final byte[] COLUMN_FAMILY_BYTES = Bytes.toBytes(COLUMN_FAMILY); 239 private static final EventLoopGroup GROUP = new NioEventLoopGroup(); 240 241 HRegion region = null; 242 // Do not run unit tests in parallel (? Why not? It don't work? Why not? St.Ack) 243 protected static HBaseTestingUtil TEST_UTIL; 244 public static Configuration CONF; 245 private String dir; 246 private final int MAX_VERSIONS = 2; 247 248 // Test names 249 protected TableName tableName; 250 protected String method; 251 protected final byte[] qual = Bytes.toBytes("qual"); 252 protected final byte[] qual1 = Bytes.toBytes("qual1"); 253 protected final byte[] qual2 = Bytes.toBytes("qual2"); 254 protected final byte[] qual3 = Bytes.toBytes("qual3"); 255 protected final byte[] value = Bytes.toBytes("value"); 256 protected final byte[] value1 = Bytes.toBytes("value1"); 257 protected final byte[] value2 = Bytes.toBytes("value2"); 258 protected final byte[] row = Bytes.toBytes("rowA"); 259 protected final byte[] row2 = Bytes.toBytes("rowB"); 260 261 protected final MetricsAssertHelper metricsAssertHelper = 262 CompatibilitySingletonFactory.getInstance(MetricsAssertHelper.class); 263 264 @Before 265 public void setup() throws IOException { 266 TEST_UTIL = new HBaseTestingUtil(); 267 CONF = TEST_UTIL.getConfiguration(); 268 // Disable directory sharing to prevent race conditions when tests run in parallel. 269 // Each test instance gets its own isolated directories to avoid one test's tearDown() 270 // deleting directories another parallel test is still using. 271 CONF.setBoolean("hbase.test.disable-directory-sharing", true); 272 NettyAsyncFSWALConfigHelper.setEventLoopConfig(CONF, GROUP, NioSocketChannel.class); 273 dir = TEST_UTIL.getDataTestDir("TestHRegion").toString(); 274 method = name.getMethodName(); 275 tableName = TableName.valueOf(method); 276 CONF.set(CompactingMemStore.IN_MEMORY_FLUSH_THRESHOLD_FACTOR_KEY, String.valueOf(0.09)); 277 CONF.setLong(AbstractFSWAL.WAL_SYNC_TIMEOUT_MS, 10000); 278 } 279 280 @After 281 public void tearDown() throws IOException { 282 // Region may have been closed, but it is still no harm if we close it again here using HTU. 283 HBaseTestingUtil.closeRegionAndWAL(region); 284 EnvironmentEdgeManagerTestHelper.reset(); 285 LOG.info("Cleaning test directory: " + TEST_UTIL.getDataTestDir()); 286 TEST_UTIL.cleanupTestDir(); 287 } 288 289 /** 290 * Test that I can use the max flushed sequence id after the close. 291 */ 292 @Test 293 public void testSequenceId() throws IOException { 294 region = initHRegion(tableName, method, CONF, COLUMN_FAMILY_BYTES); 295 assertEquals(HConstants.NO_SEQNUM, region.getMaxFlushedSeqId()); 296 // Weird. This returns 0 if no store files or no edits. Afraid to change it. 297 assertEquals(0, (long) region.getMaxStoreSeqId().get(COLUMN_FAMILY_BYTES)); 298 HBaseTestingUtil.closeRegionAndWAL(this.region); 299 assertEquals(HConstants.NO_SEQNUM, region.getMaxFlushedSeqId()); 300 assertEquals(0, (long) region.getMaxStoreSeqId().get(COLUMN_FAMILY_BYTES)); 301 HRegion oldRegion = region; 302 try { 303 // Open region again. 304 region = initHRegion(tableName, method, CONF, COLUMN_FAMILY_BYTES); 305 byte[] value = Bytes.toBytes(method); 306 // Make a random put against our cf. 307 Put put = new Put(value); 308 put.addColumn(COLUMN_FAMILY_BYTES, null, value); 309 region.put(put); 310 // No flush yet so init numbers should still be in place. 311 assertEquals(HConstants.NO_SEQNUM, region.getMaxFlushedSeqId()); 312 assertEquals(0, (long) region.getMaxStoreSeqId().get(COLUMN_FAMILY_BYTES)); 313 region.flush(true); 314 long max = region.getMaxFlushedSeqId(); 315 HBaseTestingUtil.closeRegionAndWAL(this.region); 316 assertEquals(max, region.getMaxFlushedSeqId()); 317 this.region = null; 318 } finally { 319 HBaseTestingUtil.closeRegionAndWAL(oldRegion); 320 } 321 } 322 323 /** 324 * Test for Bug 2 of HBASE-10466. "Bug 2: Conditions for the first flush of region close 325 * (so-called pre-flush) If memstoreSize is smaller than a certain value, or when region close 326 * starts a flush is ongoing, the first flush is skipped and only the second flush takes place. 327 * However, two flushes are required in case previous flush fails and leaves some data in 328 * snapshot. The bug could cause loss of data in current memstore. The fix is removing all 329 * conditions except abort check so we ensure 2 flushes for region close." 330 */ 331 @Test 332 public void testCloseCarryingSnapshot() throws IOException { 333 region = initHRegion(tableName, method, CONF, COLUMN_FAMILY_BYTES); 334 HStore store = region.getStore(COLUMN_FAMILY_BYTES); 335 // Get some random bytes. 336 byte[] value = Bytes.toBytes(method); 337 // Make a random put against our cf. 338 Put put = new Put(value); 339 put.addColumn(COLUMN_FAMILY_BYTES, null, value); 340 // First put something in current memstore, which will be in snapshot after flusher.prepare() 341 region.put(put); 342 StoreFlushContext storeFlushCtx = store.createFlushContext(12345, FlushLifeCycleTracker.DUMMY); 343 storeFlushCtx.prepare(); 344 // Second put something in current memstore 345 put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("abc"), value); 346 region.put(put); 347 // Close with something in memstore and something in the snapshot. Make sure all is cleared. 348 HBaseTestingUtil.closeRegionAndWAL(region); 349 assertEquals(0, region.getMemStoreDataSize()); 350 region = null; 351 } 352 353 /* 354 * This test is for verifying memstore snapshot size is correctly updated in case of rollback See 355 * HBASE-10845 356 */ 357 @Test 358 public void testMemstoreSnapshotSize() throws IOException { 359 class MyFaultyFSLog extends FaultyFSLog { 360 StoreFlushContext storeFlushCtx; 361 362 public MyFaultyFSLog(FileSystem fs, Path rootDir, String logName, Configuration conf) 363 throws IOException { 364 super(fs, rootDir, logName, conf); 365 } 366 367 void setStoreFlushCtx(StoreFlushContext storeFlushCtx) { 368 this.storeFlushCtx = storeFlushCtx; 369 } 370 371 @Override 372 protected void doSync(long txid, boolean forceSync) throws IOException { 373 storeFlushCtx.prepare(); 374 super.doSync(txid, forceSync); 375 } 376 } 377 378 FileSystem fs = FileSystem.get(CONF); 379 Path rootDir = new Path(dir + method); 380 fs.mkdirs(new Path(rootDir, method)); 381 MyFaultyFSLog faultyLog = new MyFaultyFSLog(fs, rootDir, method, CONF); 382 faultyLog.init(); 383 region = initHRegion(tableName, null, null, CONF, false, Durability.SYNC_WAL, faultyLog, 384 COLUMN_FAMILY_BYTES); 385 386 HStore store = region.getStore(COLUMN_FAMILY_BYTES); 387 // Get some random bytes. 388 byte[] value = Bytes.toBytes(method); 389 faultyLog.setStoreFlushCtx(store.createFlushContext(12345, FlushLifeCycleTracker.DUMMY)); 390 391 Put put = new Put(value); 392 put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("abc"), value); 393 faultyLog.setFailureType(FaultyFSLog.FailureType.SYNC); 394 boolean threwIOE = false; 395 try { 396 region.put(put); 397 } catch (IOException ioe) { 398 threwIOE = true; 399 } finally { 400 assertTrue("The regionserver should have thrown an exception", threwIOE); 401 } 402 MemStoreSize mss = store.getFlushableSize(); 403 assertTrue("flushable size should be zero, but it is " + mss, mss.getDataSize() == 0); 404 } 405 406 /** 407 * Create a WAL outside of the usual helper in 408 * {@link HBaseTestingUtil#createWal(Configuration, Path, RegionInfo)} because that method doesn't 409 * play nicely with FaultyFileSystem. Call this method before overriding {@code fs.file.impl}. 410 * @param callingMethod a unique component for the path, probably the name of the test method. 411 */ 412 private static WAL createWALCompatibleWithFaultyFileSystem(String callingMethod, 413 Configuration conf, TableName tableName) throws IOException { 414 final Path logDir = TEST_UTIL.getDataTestDirOnTestFS(callingMethod + ".log"); 415 final Configuration walConf = new Configuration(conf); 416 CommonFSUtils.setRootDir(walConf, logDir); 417 return new WALFactory(walConf, callingMethod) 418 .getWAL(RegionInfoBuilder.newBuilder(tableName).build()); 419 } 420 421 @Test 422 public void testMemstoreSizeAccountingWithFailedPostBatchMutate() throws IOException { 423 FileSystem fs = FileSystem.get(CONF); 424 Path rootDir = new Path(dir + method); 425 fs.mkdirs(new Path(rootDir, method)); 426 FSHLog hLog = new FSHLog(fs, rootDir, method, CONF); 427 hLog.init(); 428 region = initHRegion(tableName, null, null, CONF, false, Durability.SYNC_WAL, hLog, 429 COLUMN_FAMILY_BYTES); 430 HStore store = region.getStore(COLUMN_FAMILY_BYTES); 431 assertEquals(0, region.getMemStoreDataSize()); 432 433 // Put one value 434 byte[] value = Bytes.toBytes(method); 435 Put put = new Put(value); 436 put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("abc"), value); 437 region.put(put); 438 long onePutSize = region.getMemStoreDataSize(); 439 assertTrue(onePutSize > 0); 440 441 RegionCoprocessorHost mockedCPHost = mock(RegionCoprocessorHost.class); 442 doThrow(new IOException()).when(mockedCPHost).postBatchMutate(any()); 443 region.setCoprocessorHost(mockedCPHost); 444 445 put = new Put(value); 446 put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("dfg"), value); 447 try { 448 region.put(put); 449 fail("Should have failed with IOException"); 450 } catch (IOException expected) { 451 } 452 long expectedSize = onePutSize * 2; 453 assertEquals("memstoreSize should be incremented", expectedSize, region.getMemStoreDataSize()); 454 assertEquals("flushable size should be incremented", expectedSize, 455 store.getFlushableSize().getDataSize()); 456 457 region.setCoprocessorHost(null); 458 } 459 460 /** 461 * A test case of HBASE-21041 462 */ 463 @Test 464 public void testFlushAndMemstoreSizeCounting() throws Exception { 465 byte[] family = Bytes.toBytes("family"); 466 this.region = initHRegion(tableName, method, CONF, family); 467 for (byte[] row : HBaseTestingUtil.ROWS) { 468 Put put = new Put(row); 469 put.addColumn(family, family, row); 470 region.put(put); 471 } 472 region.flush(true); 473 // After flush, data size should be zero 474 assertEquals(0, region.getMemStoreDataSize()); 475 // After flush, a new active mutable segment is created, so the heap size 476 // should equal to MutableSegment.DEEP_OVERHEAD 477 assertEquals(MutableSegment.DEEP_OVERHEAD, region.getMemStoreHeapSize()); 478 // After flush, offheap should be zero 479 assertEquals(0, region.getMemStoreOffHeapSize()); 480 } 481 482 /** 483 * Test we do not lose data if we fail a flush and then close. Part of HBase-10466. Tests the 484 * following from the issue description: "Bug 1: Wrong calculation of HRegion.memstoreSize: When a 485 * flush fails, data to be flushed is kept in each MemStore's snapshot and wait for next flush 486 * attempt to continue on it. But when the next flush succeeds, the counter of total memstore size 487 * in HRegion is always deduced by the sum of current memstore sizes instead of snapshots left 488 * from previous failed flush. This calculation is problematic that almost every time there is 489 * failed flush, HRegion.memstoreSize gets reduced by a wrong value. If region flush could not 490 * proceed for a couple cycles, the size in current memstore could be much larger than the 491 * snapshot. It's likely to drift memstoreSize much smaller than expected. In extreme case, if the 492 * error accumulates to even bigger than HRegion's memstore size limit, any further flush is 493 * skipped because flush does not do anything if memstoreSize is not larger than 0." 494 */ 495 @Test 496 public void testFlushSizeAccounting() throws Exception { 497 final Configuration conf = HBaseConfiguration.create(CONF); 498 final WAL wal = createWALCompatibleWithFaultyFileSystem(method, conf, tableName); 499 // Only retry once. 500 conf.setInt("hbase.hstore.flush.retries.number", 1); 501 final User user = User.createUserForTesting(conf, method, new String[] { "foo" }); 502 // Inject our faulty LocalFileSystem 503 conf.setClass("fs.file.impl", FaultyFileSystem.class, FileSystem.class); 504 user.runAs(new PrivilegedExceptionAction<Object>() { 505 @Override 506 public Object run() throws Exception { 507 // Make sure it worked (above is sensitive to caching details in hadoop core) 508 FileSystem fs = FileSystem.get(conf); 509 Assert.assertEquals(FaultyFileSystem.class, fs.getClass()); 510 FaultyFileSystem ffs = (FaultyFileSystem) fs; 511 HRegion region = null; 512 try { 513 // Initialize region 514 region = initHRegion(tableName, null, null, CONF, false, Durability.SYNC_WAL, wal, 515 COLUMN_FAMILY_BYTES); 516 long size = region.getMemStoreDataSize(); 517 Assert.assertEquals(0, size); 518 // Put one item into memstore. Measure the size of one item in memstore. 519 Put p1 = new Put(row); 520 p1.add(new KeyValue(row, COLUMN_FAMILY_BYTES, qual1, 1, (byte[]) null)); 521 region.put(p1); 522 final long sizeOfOnePut = region.getMemStoreDataSize(); 523 // Fail a flush which means the current memstore will hang out as memstore 'snapshot'. 524 try { 525 LOG.info("Flushing"); 526 region.flush(true); 527 Assert.fail("Didn't bubble up IOE!"); 528 } catch (DroppedSnapshotException dse) { 529 // What we are expecting 530 region.closing.set(false); // this is needed for the rest of the test to work 531 } 532 // Make it so all writes succeed from here on out 533 ffs.fault.set(false); 534 // Check sizes. Should still be the one entry. 535 Assert.assertEquals(sizeOfOnePut, region.getMemStoreDataSize()); 536 // Now add two entries so that on this next flush that fails, we can see if we 537 // subtract the right amount, the snapshot size only. 538 Put p2 = new Put(row); 539 p2.add(new KeyValue(row, COLUMN_FAMILY_BYTES, qual2, 2, (byte[]) null)); 540 p2.add(new KeyValue(row, COLUMN_FAMILY_BYTES, qual3, 3, (byte[]) null)); 541 region.put(p2); 542 long expectedSize = sizeOfOnePut * 3; 543 Assert.assertEquals(expectedSize, region.getMemStoreDataSize()); 544 // Do a successful flush. It will clear the snapshot only. Thats how flushes work. 545 // If already a snapshot, we clear it else we move the memstore to be snapshot and flush 546 // it 547 region.flush(true); 548 // Make sure our memory accounting is right. 549 Assert.assertEquals(sizeOfOnePut * 2, region.getMemStoreDataSize()); 550 } finally { 551 HBaseTestingUtil.closeRegionAndWAL(region); 552 } 553 return null; 554 } 555 }); 556 FileSystem.closeAllForUGI(user.getUGI()); 557 } 558 559 @Test 560 public void testCloseWithFailingFlush() throws Exception { 561 final Configuration conf = HBaseConfiguration.create(CONF); 562 final WAL wal = createWALCompatibleWithFaultyFileSystem(method, conf, tableName); 563 // Only retry once. 564 conf.setInt("hbase.hstore.flush.retries.number", 1); 565 final User user = User.createUserForTesting(conf, this.method, new String[] { "foo" }); 566 // Inject our faulty LocalFileSystem 567 conf.setClass("fs.file.impl", FaultyFileSystem.class, FileSystem.class); 568 user.runAs(new PrivilegedExceptionAction<Object>() { 569 @Override 570 public Object run() throws Exception { 571 // Make sure it worked (above is sensitive to caching details in hadoop core) 572 FileSystem fs = FileSystem.get(conf); 573 Assert.assertEquals(FaultyFileSystem.class, fs.getClass()); 574 FaultyFileSystem ffs = (FaultyFileSystem) fs; 575 HRegion region = null; 576 try { 577 // Initialize region 578 region = initHRegion(tableName, null, null, CONF, false, Durability.SYNC_WAL, wal, 579 COLUMN_FAMILY_BYTES); 580 long size = region.getMemStoreDataSize(); 581 Assert.assertEquals(0, size); 582 // Put one item into memstore. Measure the size of one item in memstore. 583 Put p1 = new Put(row); 584 p1.add(new KeyValue(row, COLUMN_FAMILY_BYTES, qual1, 1, (byte[]) null)); 585 region.put(p1); 586 // Manufacture an outstanding snapshot -- fake a failed flush by doing prepare step only. 587 HStore store = region.getStore(COLUMN_FAMILY_BYTES); 588 StoreFlushContext storeFlushCtx = 589 store.createFlushContext(12345, FlushLifeCycleTracker.DUMMY); 590 storeFlushCtx.prepare(); 591 // Now add two entries to the foreground memstore. 592 Put p2 = new Put(row); 593 p2.add(new KeyValue(row, COLUMN_FAMILY_BYTES, qual2, 2, (byte[]) null)); 594 p2.add(new KeyValue(row, COLUMN_FAMILY_BYTES, qual3, 3, (byte[]) null)); 595 region.put(p2); 596 // Now try close on top of a failing flush. 597 HBaseTestingUtil.closeRegionAndWAL(region); 598 region = null; 599 fail(); 600 } catch (DroppedSnapshotException dse) { 601 // Expected 602 LOG.info("Expected DroppedSnapshotException"); 603 } finally { 604 // Make it so all writes succeed from here on out so can close clean 605 ffs.fault.set(false); 606 HBaseTestingUtil.closeRegionAndWAL(region); 607 } 608 return null; 609 } 610 }); 611 FileSystem.closeAllForUGI(user.getUGI()); 612 } 613 614 @Test 615 public void testCompactionAffectedByScanners() throws Exception { 616 byte[] family = Bytes.toBytes("family"); 617 this.region = initHRegion(tableName, method, CONF, family); 618 619 Put put = new Put(Bytes.toBytes("r1")); 620 put.addColumn(family, Bytes.toBytes("q1"), Bytes.toBytes("v1")); 621 region.put(put); 622 region.flush(true); 623 624 Scan scan = new Scan(); 625 scan.readVersions(3); 626 // open the first scanner 627 try (RegionScanner scanner1 = region.getScanner(scan)) { 628 Delete delete = new Delete(Bytes.toBytes("r1")); 629 region.delete(delete); 630 region.flush(true); 631 // open the second scanner 632 try (RegionScanner scanner2 = region.getScanner(scan)) { 633 List<Cell> results = new ArrayList<>(); 634 635 LOG.info("Smallest read point:" + region.getSmallestReadPoint()); 636 637 // make a major compaction 638 region.compact(true); 639 640 // open the third scanner 641 try (RegionScanner scanner3 = region.getScanner(scan)) { 642 // get data from scanner 1, 2, 3 after major compaction 643 scanner1.next(results); 644 LOG.info(results.toString()); 645 assertEquals(1, results.size()); 646 647 results.clear(); 648 scanner2.next(results); 649 LOG.info(results.toString()); 650 assertEquals(0, results.size()); 651 652 results.clear(); 653 scanner3.next(results); 654 LOG.info(results.toString()); 655 assertEquals(0, results.size()); 656 } 657 } 658 } 659 } 660 661 @Test 662 public void testToShowNPEOnRegionScannerReseek() throws Exception { 663 byte[] family = Bytes.toBytes("family"); 664 this.region = initHRegion(tableName, method, CONF, family); 665 666 Put put = new Put(Bytes.toBytes("r1")); 667 put.addColumn(family, Bytes.toBytes("q1"), Bytes.toBytes("v1")); 668 region.put(put); 669 put = new Put(Bytes.toBytes("r2")); 670 put.addColumn(family, Bytes.toBytes("q1"), Bytes.toBytes("v1")); 671 region.put(put); 672 region.flush(true); 673 674 Scan scan = new Scan(); 675 scan.readVersions(3); 676 // open the first scanner 677 try (RegionScanner scanner1 = region.getScanner(scan)) { 678 LOG.info("Smallest read point:" + region.getSmallestReadPoint()); 679 680 region.compact(true); 681 682 scanner1.reseek(Bytes.toBytes("r2")); 683 List<Cell> results = new ArrayList<>(); 684 scanner1.next(results); 685 Cell keyValue = results.get(0); 686 assertTrue(Bytes.compareTo(CellUtil.cloneRow(keyValue), Bytes.toBytes("r2")) == 0); 687 scanner1.close(); 688 } 689 } 690 691 @Test 692 public void testArchiveRecoveredEditsReplay() throws Exception { 693 byte[] family = Bytes.toBytes("family"); 694 this.region = initHRegion(tableName, method, CONF, family); 695 final WALFactory wals = new WALFactory(CONF, method); 696 try { 697 Path regiondir = region.getRegionFileSystem().getRegionDir(); 698 FileSystem fs = region.getRegionFileSystem().getFileSystem(); 699 byte[] regionName = region.getRegionInfo().getEncodedNameAsBytes(); 700 701 Path recoveredEditsDir = WALSplitUtil.getRegionDirRecoveredEditsDir(regiondir); 702 703 long maxSeqId = 1050; 704 long minSeqId = 1000; 705 706 for (long i = minSeqId; i <= maxSeqId; i += 10) { 707 Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", i)); 708 fs.create(recoveredEdits); 709 WALProvider.Writer writer = wals.createRecoveredEditsWriter(fs, recoveredEdits); 710 711 long time = System.nanoTime(); 712 WALEdit edit = new WALEdit(); 713 WALEditInternalHelper.addExtendedCell(edit, 714 new KeyValue(row, family, Bytes.toBytes(i), time, KeyValue.Type.Put, Bytes.toBytes(i))); 715 writer.append(new WAL.Entry( 716 new WALKeyImpl(regionName, tableName, i, time, HConstants.DEFAULT_CLUSTER_ID), edit)); 717 718 writer.close(); 719 } 720 MonitoredTask status = TaskMonitor.get().createStatus(method); 721 Map<byte[], Long> maxSeqIdInStores = new TreeMap<>(Bytes.BYTES_COMPARATOR); 722 for (HStore store : region.getStores()) { 723 maxSeqIdInStores.put(Bytes.toBytes(store.getColumnFamilyName()), minSeqId - 1); 724 } 725 CONF.set("hbase.region.archive.recovered.edits", "true"); 726 CONF.set(CommonFSUtils.HBASE_WAL_DIR, "/custom_wal_dir"); 727 long seqId = region.replayRecoveredEditsIfAny(maxSeqIdInStores, null, status); 728 assertEquals(maxSeqId, seqId); 729 region.getMVCC().advanceTo(seqId); 730 String fakeFamilyName = recoveredEditsDir.getName(); 731 Path rootDir = new Path(CONF.get(HConstants.HBASE_DIR)); 732 Path storeArchiveDir = HFileArchiveUtil.getStoreArchivePathForRootDir(rootDir, 733 region.getRegionInfo(), Bytes.toBytes(fakeFamilyName)); 734 FileStatus[] list = TEST_UTIL.getTestFileSystem().listStatus(storeArchiveDir); 735 assertEquals(6, list.length); 736 } finally { 737 CONF.set("hbase.region.archive.recovered.edits", "false"); 738 CONF.set(CommonFSUtils.HBASE_WAL_DIR, ""); 739 HBaseTestingUtil.closeRegionAndWAL(this.region); 740 this.region = null; 741 wals.close(); 742 } 743 } 744 745 @Test 746 public void testSkipRecoveredEditsReplay() throws Exception { 747 byte[] family = Bytes.toBytes("family"); 748 this.region = initHRegion(tableName, method, CONF, family); 749 final WALFactory wals = new WALFactory(CONF, method); 750 try { 751 Path regiondir = region.getRegionFileSystem().getRegionDir(); 752 FileSystem fs = region.getRegionFileSystem().getFileSystem(); 753 byte[] regionName = region.getRegionInfo().getEncodedNameAsBytes(); 754 755 Path recoveredEditsDir = WALSplitUtil.getRegionDirRecoveredEditsDir(regiondir); 756 757 long maxSeqId = 1050; 758 long minSeqId = 1000; 759 760 for (long i = minSeqId; i <= maxSeqId; i += 10) { 761 Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", i)); 762 fs.create(recoveredEdits); 763 WALProvider.Writer writer = wals.createRecoveredEditsWriter(fs, recoveredEdits); 764 765 long time = System.nanoTime(); 766 WALEdit edit = new WALEdit(); 767 WALEditInternalHelper.addExtendedCell(edit, 768 new KeyValue(row, family, Bytes.toBytes(i), time, KeyValue.Type.Put, Bytes.toBytes(i))); 769 writer.append(new WAL.Entry( 770 new WALKeyImpl(regionName, tableName, i, time, HConstants.DEFAULT_CLUSTER_ID), edit)); 771 772 writer.close(); 773 } 774 MonitoredTask status = TaskMonitor.get().createStatus(method); 775 Map<byte[], Long> maxSeqIdInStores = new TreeMap<>(Bytes.BYTES_COMPARATOR); 776 for (HStore store : region.getStores()) { 777 maxSeqIdInStores.put(Bytes.toBytes(store.getColumnFamilyName()), minSeqId - 1); 778 } 779 long seqId = region.replayRecoveredEditsIfAny(maxSeqIdInStores, null, status); 780 assertEquals(maxSeqId, seqId); 781 region.getMVCC().advanceTo(seqId); 782 Get get = new Get(row); 783 Result result = region.get(get); 784 for (long i = minSeqId; i <= maxSeqId; i += 10) { 785 List<Cell> kvs = result.getColumnCells(family, Bytes.toBytes(i)); 786 assertEquals(1, kvs.size()); 787 assertArrayEquals(Bytes.toBytes(i), CellUtil.cloneValue(kvs.get(0))); 788 } 789 } finally { 790 HBaseTestingUtil.closeRegionAndWAL(this.region); 791 this.region = null; 792 wals.close(); 793 } 794 } 795 796 @Test 797 public void testSkipRecoveredEditsReplaySomeIgnored() throws Exception { 798 byte[] family = Bytes.toBytes("family"); 799 this.region = initHRegion(tableName, method, CONF, family); 800 final WALFactory wals = new WALFactory(CONF, method); 801 try { 802 Path regiondir = region.getRegionFileSystem().getRegionDir(); 803 FileSystem fs = region.getRegionFileSystem().getFileSystem(); 804 byte[] regionName = region.getRegionInfo().getEncodedNameAsBytes(); 805 806 Path recoveredEditsDir = WALSplitUtil.getRegionDirRecoveredEditsDir(regiondir); 807 808 long maxSeqId = 1050; 809 long minSeqId = 1000; 810 811 for (long i = minSeqId; i <= maxSeqId; i += 10) { 812 Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", i)); 813 fs.create(recoveredEdits); 814 WALProvider.Writer writer = wals.createRecoveredEditsWriter(fs, recoveredEdits); 815 816 long time = System.nanoTime(); 817 WALEdit edit = new WALEdit(); 818 WALEditInternalHelper.addExtendedCell(edit, 819 new KeyValue(row, family, Bytes.toBytes(i), time, KeyValue.Type.Put, Bytes.toBytes(i))); 820 writer.append(new WAL.Entry( 821 new WALKeyImpl(regionName, tableName, i, time, HConstants.DEFAULT_CLUSTER_ID), edit)); 822 823 writer.close(); 824 } 825 long recoverSeqId = 1030; 826 MonitoredTask status = TaskMonitor.get().createStatus(method); 827 Map<byte[], Long> maxSeqIdInStores = new TreeMap<>(Bytes.BYTES_COMPARATOR); 828 for (HStore store : region.getStores()) { 829 maxSeqIdInStores.put(Bytes.toBytes(store.getColumnFamilyName()), recoverSeqId - 1); 830 } 831 long seqId = region.replayRecoveredEditsIfAny(maxSeqIdInStores, null, status); 832 assertEquals(maxSeqId, seqId); 833 region.getMVCC().advanceTo(seqId); 834 Get get = new Get(row); 835 Result result = region.get(get); 836 for (long i = minSeqId; i <= maxSeqId; i += 10) { 837 List<Cell> kvs = result.getColumnCells(family, Bytes.toBytes(i)); 838 if (i < recoverSeqId) { 839 assertEquals(0, kvs.size()); 840 } else { 841 assertEquals(1, kvs.size()); 842 assertArrayEquals(Bytes.toBytes(i), CellUtil.cloneValue(kvs.get(0))); 843 } 844 } 845 } finally { 846 HBaseTestingUtil.closeRegionAndWAL(this.region); 847 this.region = null; 848 wals.close(); 849 } 850 } 851 852 @Test 853 public void testSkipRecoveredEditsReplayAllIgnored() throws Exception { 854 byte[] family = Bytes.toBytes("family"); 855 this.region = initHRegion(tableName, method, CONF, family); 856 Path regiondir = region.getRegionFileSystem().getRegionDir(); 857 FileSystem fs = region.getRegionFileSystem().getFileSystem(); 858 859 Path recoveredEditsDir = WALSplitUtil.getRegionDirRecoveredEditsDir(regiondir); 860 for (int i = 1000; i < 1050; i += 10) { 861 Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", i)); 862 FSDataOutputStream dos = fs.create(recoveredEdits); 863 dos.writeInt(i); 864 dos.close(); 865 } 866 long minSeqId = 2000; 867 Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", minSeqId - 1)); 868 FSDataOutputStream dos = fs.create(recoveredEdits); 869 dos.close(); 870 871 Map<byte[], Long> maxSeqIdInStores = new TreeMap<>(Bytes.BYTES_COMPARATOR); 872 for (HStore store : region.getStores()) { 873 maxSeqIdInStores.put(Bytes.toBytes(store.getColumnFamilyName()), minSeqId); 874 } 875 long seqId = region.replayRecoveredEditsIfAny(maxSeqIdInStores, null, null); 876 assertEquals(minSeqId, seqId); 877 } 878 879 @Test 880 public void testSkipRecoveredEditsReplayTheLastFileIgnored() throws Exception { 881 byte[] family = Bytes.toBytes("family"); 882 this.region = initHRegion(tableName, method, CONF, family); 883 final WALFactory wals = new WALFactory(CONF, method); 884 try { 885 Path regiondir = region.getRegionFileSystem().getRegionDir(); 886 FileSystem fs = region.getRegionFileSystem().getFileSystem(); 887 byte[] regionName = region.getRegionInfo().getEncodedNameAsBytes(); 888 byte[][] columns = region.getTableDescriptor().getColumnFamilyNames().toArray(new byte[0][]); 889 890 assertEquals(0, region.getStoreFileList(columns).size()); 891 892 Path recoveredEditsDir = WALSplitUtil.getRegionDirRecoveredEditsDir(regiondir); 893 894 long maxSeqId = 1050; 895 long minSeqId = 1000; 896 897 for (long i = minSeqId; i <= maxSeqId; i += 10) { 898 Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", i)); 899 fs.create(recoveredEdits); 900 WALProvider.Writer writer = wals.createRecoveredEditsWriter(fs, recoveredEdits); 901 902 long time = System.nanoTime(); 903 WALEdit edit = null; 904 if (i == maxSeqId) { 905 edit = WALEdit.createCompaction(region.getRegionInfo(), 906 CompactionDescriptor.newBuilder().setTableName(ByteString.copyFrom(tableName.getName())) 907 .setFamilyName(ByteString.copyFrom(regionName)) 908 .setEncodedRegionName(ByteString.copyFrom(regionName)) 909 .setStoreHomeDirBytes(ByteString.copyFrom(Bytes.toBytes(regiondir.toString()))) 910 .setRegionName(ByteString.copyFrom(region.getRegionInfo().getRegionName())).build()); 911 } else { 912 edit = new WALEdit(); 913 WALEditInternalHelper.addExtendedCell(edit, 914 new KeyValue(row, family, Bytes.toBytes(i), time, KeyValue.Type.Put, Bytes.toBytes(i))); 915 } 916 writer.append(new WAL.Entry( 917 new WALKeyImpl(regionName, tableName, i, time, HConstants.DEFAULT_CLUSTER_ID), edit)); 918 writer.close(); 919 } 920 921 long recoverSeqId = 1030; 922 Map<byte[], Long> maxSeqIdInStores = new TreeMap<>(Bytes.BYTES_COMPARATOR); 923 MonitoredTask status = TaskMonitor.get().createStatus(method); 924 for (HStore store : region.getStores()) { 925 maxSeqIdInStores.put(Bytes.toBytes(store.getColumnFamilyName()), recoverSeqId - 1); 926 } 927 long seqId = region.replayRecoveredEditsIfAny(maxSeqIdInStores, null, status); 928 assertEquals(maxSeqId, seqId); 929 930 // assert that the files are flushed 931 assertEquals(1, region.getStoreFileList(columns).size()); 932 933 } finally { 934 HBaseTestingUtil.closeRegionAndWAL(this.region); 935 this.region = null; 936 wals.close(); 937 } 938 } 939 940 @Test 941 public void testRecoveredEditsReplayCompaction() throws Exception { 942 testRecoveredEditsReplayCompaction(false); 943 testRecoveredEditsReplayCompaction(true); 944 } 945 946 public void testRecoveredEditsReplayCompaction(boolean mismatchedRegionName) throws Exception { 947 CONF.setClass(HConstants.REGION_IMPL, HRegionForTesting.class, Region.class); 948 byte[] family = Bytes.toBytes("family"); 949 this.region = initHRegion(tableName, method, CONF, family); 950 final WALFactory wals = new WALFactory(CONF, method); 951 try { 952 Path regiondir = region.getRegionFileSystem().getRegionDir(); 953 FileSystem fs = region.getRegionFileSystem().getFileSystem(); 954 byte[] regionName = region.getRegionInfo().getEncodedNameAsBytes(); 955 956 long maxSeqId = 3; 957 long minSeqId = 0; 958 959 for (long i = minSeqId; i < maxSeqId; i++) { 960 Put put = new Put(Bytes.toBytes(i)); 961 put.addColumn(family, Bytes.toBytes(i), Bytes.toBytes(i)); 962 region.put(put); 963 region.flush(true); 964 } 965 966 // this will create a region with 3 files 967 assertEquals(3, region.getStore(family).getStorefilesCount()); 968 List<Path> storeFiles = new ArrayList<>(3); 969 for (HStoreFile sf : region.getStore(family).getStorefiles()) { 970 storeFiles.add(sf.getPath()); 971 } 972 973 // disable compaction completion 974 CONF.setBoolean("hbase.hstore.compaction.complete", false); 975 region.compactStores(); 976 977 // ensure that nothing changed 978 assertEquals(3, region.getStore(family).getStorefilesCount()); 979 980 // now find the compacted file, and manually add it to the recovered edits 981 Path tmpDir = new Path(region.getRegionFileSystem().getTempDir(), Bytes.toString(family)); 982 FileStatus[] files = CommonFSUtils.listStatus(fs, tmpDir); 983 String errorMsg = "Expected to find 1 file in the region temp directory " 984 + "from the compaction, could not find any"; 985 assertNotNull(errorMsg, files); 986 assertEquals(errorMsg, 1, files.length); 987 // move the file inside region dir 988 Path newFile = 989 region.getRegionFileSystem().commitStoreFile(Bytes.toString(family), files[0].getPath()); 990 991 byte[] encodedNameAsBytes = this.region.getRegionInfo().getEncodedNameAsBytes(); 992 byte[] fakeEncodedNameAsBytes = new byte[encodedNameAsBytes.length]; 993 for (int i = 0; i < encodedNameAsBytes.length; i++) { 994 // Mix the byte array to have a new encodedName 995 fakeEncodedNameAsBytes[i] = (byte) (encodedNameAsBytes[i] + 1); 996 } 997 998 CompactionDescriptor compactionDescriptor = ProtobufUtil.toCompactionDescriptor( 999 this.region.getRegionInfo(), mismatchedRegionName ? fakeEncodedNameAsBytes : null, family, 1000 storeFiles, Lists.newArrayList(newFile), 1001 region.getRegionFileSystem().getStoreDir(Bytes.toString(family))); 1002 1003 WALUtil.writeCompactionMarker(region.getWAL(), this.region.getReplicationScope(), 1004 this.region.getRegionInfo(), compactionDescriptor, region.getMVCC(), null); 1005 1006 Path recoveredEditsDir = WALSplitUtil.getRegionDirRecoveredEditsDir(regiondir); 1007 1008 Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", 1000)); 1009 fs.create(recoveredEdits); 1010 WALProvider.Writer writer = wals.createRecoveredEditsWriter(fs, recoveredEdits); 1011 1012 long time = System.nanoTime(); 1013 1014 writer.append(new WAL.Entry( 1015 new WALKeyImpl(regionName, tableName, 10, time, HConstants.DEFAULT_CLUSTER_ID), 1016 WALEdit.createCompaction(region.getRegionInfo(), compactionDescriptor))); 1017 writer.close(); 1018 1019 // close the region now, and reopen again 1020 region.getTableDescriptor(); 1021 region.getRegionInfo(); 1022 HBaseTestingUtil.closeRegionAndWAL(this.region); 1023 try { 1024 region = HRegion.openHRegion(region, null); 1025 } catch (WrongRegionException wre) { 1026 fail("Matching encoded region name should not have produced WrongRegionException"); 1027 } 1028 1029 // now check whether we have only one store file, the compacted one 1030 Collection<HStoreFile> sfs = region.getStore(family).getStorefiles(); 1031 for (HStoreFile sf : sfs) { 1032 LOG.info(Objects.toString(sf.getPath())); 1033 } 1034 if (!mismatchedRegionName) { 1035 assertEquals(1, region.getStore(family).getStorefilesCount()); 1036 } 1037 files = CommonFSUtils.listStatus(fs, tmpDir); 1038 assertTrue("Expected to find 0 files inside " + tmpDir, files == null || files.length == 0); 1039 1040 for (long i = minSeqId; i < maxSeqId; i++) { 1041 Get get = new Get(Bytes.toBytes(i)); 1042 Result result = region.get(get); 1043 byte[] value = result.getValue(family, Bytes.toBytes(i)); 1044 assertArrayEquals(Bytes.toBytes(i), value); 1045 } 1046 } finally { 1047 HBaseTestingUtil.closeRegionAndWAL(this.region); 1048 this.region = null; 1049 wals.close(); 1050 CONF.setClass(HConstants.REGION_IMPL, HRegion.class, Region.class); 1051 } 1052 } 1053 1054 @Test 1055 public void testFlushMarkers() throws Exception { 1056 // tests that flush markers are written to WAL and handled at recovered edits 1057 byte[] family = Bytes.toBytes("family"); 1058 Path logDir = TEST_UTIL.getDataTestDirOnTestFS(method + ".log"); 1059 final Configuration walConf = new Configuration(TEST_UTIL.getConfiguration()); 1060 CommonFSUtils.setRootDir(walConf, logDir); 1061 final WALFactory wals = new WALFactory(walConf, method); 1062 final WAL wal = wals.getWAL(RegionInfoBuilder.newBuilder(tableName).build()); 1063 1064 this.region = initHRegion(tableName, HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW, CONF, 1065 false, Durability.USE_DEFAULT, wal, family); 1066 try { 1067 Path regiondir = region.getRegionFileSystem().getRegionDir(); 1068 FileSystem fs = region.getRegionFileSystem().getFileSystem(); 1069 byte[] regionName = region.getRegionInfo().getEncodedNameAsBytes(); 1070 1071 long maxSeqId = 3; 1072 long minSeqId = 0; 1073 1074 for (long i = minSeqId; i < maxSeqId; i++) { 1075 Put put = new Put(Bytes.toBytes(i)); 1076 put.addColumn(family, Bytes.toBytes(i), Bytes.toBytes(i)); 1077 region.put(put); 1078 region.flush(true); 1079 } 1080 1081 // this will create a region with 3 files from flush 1082 assertEquals(3, region.getStore(family).getStorefilesCount()); 1083 List<String> storeFiles = new ArrayList<>(3); 1084 for (HStoreFile sf : region.getStore(family).getStorefiles()) { 1085 storeFiles.add(sf.getPath().getName()); 1086 } 1087 1088 // now verify that the flush markers are written 1089 wal.shutdown(); 1090 WALStreamReader reader = WALFactory.createStreamReader(fs, 1091 AbstractFSWALProvider.getCurrentFileName(wal), TEST_UTIL.getConfiguration()); 1092 try { 1093 List<WAL.Entry> flushDescriptors = new ArrayList<>(); 1094 long lastFlushSeqId = -1; 1095 while (true) { 1096 WAL.Entry entry = reader.next(); 1097 if (entry == null) { 1098 break; 1099 } 1100 Cell cell = entry.getEdit().getCells().get(0); 1101 if (WALEdit.isMetaEditFamily(cell)) { 1102 FlushDescriptor flushDesc = WALEdit.getFlushDescriptor(cell); 1103 assertNotNull(flushDesc); 1104 assertArrayEquals(tableName.getName(), flushDesc.getTableName().toByteArray()); 1105 if (flushDesc.getAction() == FlushAction.START_FLUSH) { 1106 assertTrue(flushDesc.getFlushSequenceNumber() > lastFlushSeqId); 1107 } else if (flushDesc.getAction() == FlushAction.COMMIT_FLUSH) { 1108 assertTrue(flushDesc.getFlushSequenceNumber() == lastFlushSeqId); 1109 } 1110 lastFlushSeqId = flushDesc.getFlushSequenceNumber(); 1111 assertArrayEquals(regionName, flushDesc.getEncodedRegionName().toByteArray()); 1112 assertEquals(1, flushDesc.getStoreFlushesCount()); // only one store 1113 StoreFlushDescriptor storeFlushDesc = flushDesc.getStoreFlushes(0); 1114 assertArrayEquals(family, storeFlushDesc.getFamilyName().toByteArray()); 1115 assertEquals("family", storeFlushDesc.getStoreHomeDir()); 1116 if (flushDesc.getAction() == FlushAction.START_FLUSH) { 1117 assertEquals(0, storeFlushDesc.getFlushOutputCount()); 1118 } else { 1119 assertEquals(1, storeFlushDesc.getFlushOutputCount()); // only one file from flush 1120 assertTrue(storeFiles.contains(storeFlushDesc.getFlushOutput(0))); 1121 } 1122 1123 flushDescriptors.add(entry); 1124 } 1125 } 1126 1127 assertEquals(3 * 2, flushDescriptors.size()); // START_FLUSH and COMMIT_FLUSH per flush 1128 1129 // now write those markers to the recovered edits again. 1130 1131 Path recoveredEditsDir = WALSplitUtil.getRegionDirRecoveredEditsDir(regiondir); 1132 1133 Path recoveredEdits = new Path(recoveredEditsDir, String.format("%019d", 1000)); 1134 fs.create(recoveredEdits); 1135 WALProvider.Writer writer = wals.createRecoveredEditsWriter(fs, recoveredEdits); 1136 1137 for (WAL.Entry entry : flushDescriptors) { 1138 writer.append(entry); 1139 } 1140 writer.close(); 1141 } finally { 1142 reader.close(); 1143 } 1144 1145 // close the region now, and reopen again 1146 HBaseTestingUtil.closeRegionAndWAL(this.region); 1147 region = HRegion.openHRegion(region, null); 1148 1149 // now check whether we have can read back the data from region 1150 for (long i = minSeqId; i < maxSeqId; i++) { 1151 Get get = new Get(Bytes.toBytes(i)); 1152 Result result = region.get(get); 1153 byte[] value = result.getValue(family, Bytes.toBytes(i)); 1154 assertArrayEquals(Bytes.toBytes(i), value); 1155 } 1156 } finally { 1157 HBaseTestingUtil.closeRegionAndWAL(this.region); 1158 this.region = null; 1159 wals.close(); 1160 } 1161 } 1162 1163 static class IsFlushWALMarker implements ArgumentMatcher<WALEdit> { 1164 volatile FlushAction[] actions; 1165 1166 public IsFlushWALMarker(FlushAction... actions) { 1167 this.actions = actions; 1168 } 1169 1170 @Override 1171 public boolean matches(WALEdit edit) { 1172 List<Cell> cells = edit.getCells(); 1173 if (cells.isEmpty()) { 1174 return false; 1175 } 1176 if (WALEdit.isMetaEditFamily(cells.get(0))) { 1177 FlushDescriptor desc; 1178 try { 1179 desc = WALEdit.getFlushDescriptor(cells.get(0)); 1180 } catch (IOException e) { 1181 LOG.warn(e.toString(), e); 1182 return false; 1183 } 1184 if (desc != null) { 1185 for (FlushAction action : actions) { 1186 if (desc.getAction() == action) { 1187 return true; 1188 } 1189 } 1190 } 1191 } 1192 return false; 1193 } 1194 1195 public IsFlushWALMarker set(FlushAction... actions) { 1196 this.actions = actions; 1197 return this; 1198 } 1199 } 1200 1201 @Test 1202 public void testFlushMarkersWALFail() throws Exception { 1203 // test the cases where the WAL append for flush markers fail. 1204 byte[] family = Bytes.toBytes("family"); 1205 1206 // spy an actual WAL implementation to throw exception (was not able to mock) 1207 Path logDir = TEST_UTIL.getDataTestDirOnTestFS(method + "log"); 1208 1209 final Configuration walConf = new Configuration(TEST_UTIL.getConfiguration()); 1210 CommonFSUtils.setRootDir(walConf, logDir); 1211 // Make up a WAL that we can manipulate at append time. 1212 class FailAppendFlushMarkerWAL extends FSHLog { 1213 volatile FlushAction[] flushActions = null; 1214 1215 public FailAppendFlushMarkerWAL(FileSystem fs, Path root, String logDir, Configuration conf) 1216 throws IOException { 1217 super(fs, root, logDir, conf); 1218 } 1219 1220 @Override 1221 protected Writer createWriterInstance(FileSystem fs, Path path) throws IOException { 1222 final Writer w = super.createWriterInstance(fs, path); 1223 return new Writer() { 1224 @Override 1225 public void close() throws IOException { 1226 w.close(); 1227 } 1228 1229 @Override 1230 public void sync(boolean forceSync) throws IOException { 1231 w.sync(forceSync); 1232 } 1233 1234 @Override 1235 public void append(Entry entry) throws IOException { 1236 List<Cell> cells = entry.getEdit().getCells(); 1237 if (WALEdit.isMetaEditFamily(cells.get(0))) { 1238 FlushDescriptor desc = WALEdit.getFlushDescriptor(cells.get(0)); 1239 if (desc != null) { 1240 for (FlushAction flushAction : flushActions) { 1241 if (desc.getAction().equals(flushAction)) { 1242 throw new IOException("Failed to append flush marker! " + flushAction); 1243 } 1244 } 1245 } 1246 } 1247 w.append(entry); 1248 } 1249 1250 @Override 1251 public long getLength() { 1252 return w.getLength(); 1253 } 1254 1255 @Override 1256 public long getSyncedLength() { 1257 return w.getSyncedLength(); 1258 } 1259 }; 1260 } 1261 } 1262 FileSystem fs = FileSystem.get(walConf); 1263 Path rootDir = CommonFSUtils.getRootDir(walConf); 1264 fs.mkdirs(new Path(rootDir, method)); 1265 FailAppendFlushMarkerWAL wal = new FailAppendFlushMarkerWAL(fs, rootDir, method, walConf); 1266 wal.init(); 1267 this.region = initHRegion(tableName, HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW, CONF, 1268 false, Durability.USE_DEFAULT, wal, family); 1269 int i = 0; 1270 Put put = new Put(Bytes.toBytes(i)); 1271 put.setDurability(Durability.SKIP_WAL); // have to skip mocked wal 1272 put.addColumn(family, Bytes.toBytes(i), Bytes.toBytes(i)); 1273 region.put(put); 1274 1275 // 1. Test case where START_FLUSH throws exception 1276 wal.flushActions = new FlushAction[] { FlushAction.START_FLUSH }; 1277 1278 // start cache flush will throw exception 1279 try { 1280 region.flush(true); 1281 fail("This should have thrown exception"); 1282 } catch (DroppedSnapshotException unexpected) { 1283 // this should not be a dropped snapshot exception. Meaning that RS will not abort 1284 throw unexpected; 1285 } catch (IOException expected) { 1286 // expected 1287 } 1288 // The WAL is hosed now. It has two edits appended. We cannot roll the log without it 1289 // throwing a DroppedSnapshotException to force an abort. Just clean up the mess. 1290 region.close(true); 1291 wal.close(); 1292 // release the snapshot and active segment, so netty will not report memory leak 1293 for (HStore store : region.getStores()) { 1294 AbstractMemStore memstore = (AbstractMemStore) store.memstore; 1295 memstore.doClearSnapShot(); 1296 memstore.close(); 1297 } 1298 1299 // 2. Test case where START_FLUSH succeeds but COMMIT_FLUSH will throw exception 1300 wal.flushActions = new FlushAction[] { FlushAction.COMMIT_FLUSH }; 1301 wal = new FailAppendFlushMarkerWAL(FileSystem.get(walConf), CommonFSUtils.getRootDir(walConf), 1302 method, walConf); 1303 wal.init(); 1304 this.region = initHRegion(tableName, HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW, CONF, 1305 false, Durability.USE_DEFAULT, wal, family); 1306 region.put(put); 1307 // 3. Test case where ABORT_FLUSH will throw exception. 1308 // Even if ABORT_FLUSH throws exception, we should not fail with IOE, but continue with 1309 // DroppedSnapshotException. Below COMMIT_FLUSH will cause flush to abort 1310 wal.flushActions = new FlushAction[] { FlushAction.COMMIT_FLUSH, FlushAction.ABORT_FLUSH }; 1311 1312 // we expect this exception, since we were able to write the snapshot, but failed to 1313 // write the flush marker to WAL 1314 assertThrows(DroppedSnapshotException.class, () -> region.flush(true)); 1315 1316 region.close(true); 1317 // release the snapshot and active segment, so netty will not report memory leak 1318 for (HStore store : region.getStores()) { 1319 AbstractMemStore memstore = (AbstractMemStore) store.memstore; 1320 memstore.doClearSnapShot(); 1321 memstore.close(); 1322 } 1323 region = null; 1324 } 1325 1326 @Test 1327 public void testGetWhileRegionClose() throws IOException { 1328 Configuration hc = initSplit(); 1329 int numRows = 100; 1330 byte[][] families = { fam1, fam2, fam3 }; 1331 1332 // Setting up region 1333 this.region = initHRegion(tableName, method, hc, families); 1334 // Put data in region 1335 final int startRow = 100; 1336 putData(startRow, numRows, qual1, families); 1337 putData(startRow, numRows, qual2, families); 1338 putData(startRow, numRows, qual3, families); 1339 final AtomicBoolean done = new AtomicBoolean(false); 1340 final AtomicInteger gets = new AtomicInteger(0); 1341 GetTillDoneOrException[] threads = new GetTillDoneOrException[10]; 1342 try { 1343 // Set ten threads running concurrently getting from the region. 1344 for (int i = 0; i < threads.length / 2; i++) { 1345 threads[i] = new GetTillDoneOrException(i, Bytes.toBytes("" + startRow), done, gets); 1346 threads[i].setDaemon(true); 1347 threads[i].start(); 1348 } 1349 // Artificially make the condition by setting closing flag explicitly. 1350 // I can't make the issue happen with a call to region.close(). 1351 this.region.closing.set(true); 1352 for (int i = threads.length / 2; i < threads.length; i++) { 1353 threads[i] = new GetTillDoneOrException(i, Bytes.toBytes("" + startRow), done, gets); 1354 threads[i].setDaemon(true); 1355 threads[i].start(); 1356 } 1357 } finally { 1358 done.set(true); 1359 for (GetTillDoneOrException t : threads) { 1360 if (t != null) { 1361 try { 1362 t.join(5000); 1363 } catch (InterruptedException e) { 1364 e.printStackTrace(); 1365 } 1366 } 1367 } 1368 if (this.region != null) { 1369 HBaseTestingUtil.closeRegionAndWAL(this.region); 1370 this.region = null; 1371 } 1372 } 1373 // Check for errors after threads have been stopped 1374 for (GetTillDoneOrException t : threads) { 1375 if (t.e != null) { 1376 LOG.info("Exception=" + t.e); 1377 assertFalse("Found a NPE in " + t.getName(), t.e instanceof NullPointerException); 1378 } 1379 } 1380 } 1381 1382 /* 1383 * Thread that does get on single row until 'done' flag is flipped. If an exception causes us to 1384 * fail, it records it. 1385 */ 1386 class GetTillDoneOrException extends Thread { 1387 private final Get g; 1388 private final AtomicBoolean done; 1389 private final AtomicInteger count; 1390 private Exception e; 1391 1392 GetTillDoneOrException(final int i, final byte[] r, final AtomicBoolean d, 1393 final AtomicInteger c) { 1394 super("getter." + i); 1395 this.g = new Get(r); 1396 this.done = d; 1397 this.count = c; 1398 } 1399 1400 @Override 1401 public void run() { 1402 while (!this.done.get() && !Thread.currentThread().isInterrupted()) { 1403 try { 1404 assertTrue(region.get(g).size() > 0); 1405 this.count.incrementAndGet(); 1406 } catch (Exception e) { 1407 this.e = e; 1408 break; 1409 } 1410 } 1411 } 1412 } 1413 1414 /* 1415 * An involved filter test. Has multiple column families and deletes in mix. 1416 */ 1417 @Test 1418 public void testWeirdCacheBehaviour() throws Exception { 1419 final TableName tableName = TableName.valueOf(name.getMethodName()); 1420 byte[][] FAMILIES = new byte[][] { Bytes.toBytes("trans-blob"), Bytes.toBytes("trans-type"), 1421 Bytes.toBytes("trans-date"), Bytes.toBytes("trans-tags"), Bytes.toBytes("trans-group") }; 1422 this.region = initHRegion(tableName, method, CONF, FAMILIES); 1423 String value = "this is the value"; 1424 String value2 = "this is some other value"; 1425 String keyPrefix1 = "prefix1"; 1426 String keyPrefix2 = "prefix2"; 1427 String keyPrefix3 = "prefix3"; 1428 putRows(this.region, 3, value, keyPrefix1); 1429 putRows(this.region, 3, value, keyPrefix2); 1430 putRows(this.region, 3, value, keyPrefix3); 1431 putRows(this.region, 3, value2, keyPrefix1); 1432 putRows(this.region, 3, value2, keyPrefix2); 1433 putRows(this.region, 3, value2, keyPrefix3); 1434 System.out.println("Checking values for key: " + keyPrefix1); 1435 assertEquals("Got back incorrect number of rows from scan", 3, 1436 getNumberOfRows(keyPrefix1, value2, this.region)); 1437 System.out.println("Checking values for key: " + keyPrefix2); 1438 assertEquals("Got back incorrect number of rows from scan", 3, 1439 getNumberOfRows(keyPrefix2, value2, this.region)); 1440 System.out.println("Checking values for key: " + keyPrefix3); 1441 assertEquals("Got back incorrect number of rows from scan", 3, 1442 getNumberOfRows(keyPrefix3, value2, this.region)); 1443 deleteColumns(this.region, value2, keyPrefix1); 1444 deleteColumns(this.region, value2, keyPrefix2); 1445 deleteColumns(this.region, value2, keyPrefix3); 1446 System.out.println("Starting important checks....."); 1447 assertEquals("Got back incorrect number of rows from scan: " + keyPrefix1, 0, 1448 getNumberOfRows(keyPrefix1, value2, this.region)); 1449 assertEquals("Got back incorrect number of rows from scan: " + keyPrefix2, 0, 1450 getNumberOfRows(keyPrefix2, value2, this.region)); 1451 assertEquals("Got back incorrect number of rows from scan: " + keyPrefix3, 0, 1452 getNumberOfRows(keyPrefix3, value2, this.region)); 1453 } 1454 1455 @Test 1456 public void testAppendWithReadOnlyTable() throws Exception { 1457 final TableName tableName = TableName.valueOf(name.getMethodName()); 1458 this.region = initHRegion(tableName, method, CONF, true, Bytes.toBytes("somefamily")); 1459 boolean exceptionCaught = false; 1460 Append append = new Append(Bytes.toBytes("somerow")); 1461 append.setDurability(Durability.SKIP_WAL); 1462 append.addColumn(Bytes.toBytes("somefamily"), Bytes.toBytes("somequalifier"), 1463 Bytes.toBytes("somevalue")); 1464 try { 1465 region.append(append); 1466 } catch (IOException e) { 1467 exceptionCaught = true; 1468 } 1469 assertTrue(exceptionCaught == true); 1470 } 1471 1472 @Test 1473 public void testIncrWithReadOnlyTable() throws Exception { 1474 final TableName tableName = TableName.valueOf(name.getMethodName()); 1475 this.region = initHRegion(tableName, method, CONF, true, Bytes.toBytes("somefamily")); 1476 boolean exceptionCaught = false; 1477 Increment inc = new Increment(Bytes.toBytes("somerow")); 1478 inc.setDurability(Durability.SKIP_WAL); 1479 inc.addColumn(Bytes.toBytes("somefamily"), Bytes.toBytes("somequalifier"), 1L); 1480 try { 1481 region.increment(inc); 1482 } catch (IOException e) { 1483 exceptionCaught = true; 1484 } 1485 assertTrue(exceptionCaught == true); 1486 } 1487 1488 private void deleteColumns(HRegion r, String value, String keyPrefix) throws IOException { 1489 int count = 0; 1490 try (InternalScanner scanner = buildScanner(keyPrefix, value, r)) { 1491 boolean more = false; 1492 List<Cell> results = new ArrayList<>(); 1493 do { 1494 more = scanner.next(results); 1495 if (results != null && !results.isEmpty()) { 1496 count++; 1497 } else { 1498 break; 1499 } 1500 Delete delete = new Delete(CellUtil.cloneRow(results.get(0))); 1501 delete.addColumn(Bytes.toBytes("trans-tags"), Bytes.toBytes("qual2")); 1502 r.delete(delete); 1503 results.clear(); 1504 } while (more); 1505 } 1506 assertEquals("Did not perform correct number of deletes", 3, count); 1507 } 1508 1509 private int getNumberOfRows(String keyPrefix, String value, HRegion r) throws Exception { 1510 try (InternalScanner resultScanner = buildScanner(keyPrefix, value, r)) { 1511 int numberOfResults = 0; 1512 List<Cell> results = new ArrayList<>(); 1513 boolean more = false; 1514 do { 1515 more = resultScanner.next(results); 1516 if (results != null && !results.isEmpty()) { 1517 numberOfResults++; 1518 } else { 1519 break; 1520 } 1521 for (Cell kv : results) { 1522 LOG.info("kv=" + kv.toString() + ", " + Bytes.toString(CellUtil.cloneValue(kv))); 1523 } 1524 results.clear(); 1525 } while (more); 1526 return numberOfResults; 1527 } 1528 } 1529 1530 private InternalScanner buildScanner(String keyPrefix, String value, HRegion r) 1531 throws IOException { 1532 // Defaults FilterList.Operator.MUST_PASS_ALL. 1533 FilterList allFilters = new FilterList(); 1534 allFilters.addFilter(new PrefixFilter(Bytes.toBytes(keyPrefix))); 1535 // Only return rows where this column value exists in the row. 1536 SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes.toBytes("trans-tags"), 1537 Bytes.toBytes("qual2"), CompareOperator.EQUAL, Bytes.toBytes(value)); 1538 filter.setFilterIfMissing(true); 1539 allFilters.addFilter(filter); 1540 Scan scan = new Scan(); 1541 scan.addFamily(Bytes.toBytes("trans-blob")); 1542 scan.addFamily(Bytes.toBytes("trans-type")); 1543 scan.addFamily(Bytes.toBytes("trans-date")); 1544 scan.addFamily(Bytes.toBytes("trans-tags")); 1545 scan.addFamily(Bytes.toBytes("trans-group")); 1546 scan.setFilter(allFilters); 1547 return r.getScanner(scan); 1548 } 1549 1550 private void putRows(HRegion r, int numRows, String value, String key) throws IOException { 1551 for (int i = 0; i < numRows; i++) { 1552 String row = key + "_" + i/* UUID.randomUUID().toString() */; 1553 System.out.println(String.format("Saving row: %s, with value %s", row, value)); 1554 Put put = new Put(Bytes.toBytes(row)); 1555 put.setDurability(Durability.SKIP_WAL); 1556 put.addColumn(Bytes.toBytes("trans-blob"), null, Bytes.toBytes("value for blob")); 1557 put.addColumn(Bytes.toBytes("trans-type"), null, Bytes.toBytes("statement")); 1558 put.addColumn(Bytes.toBytes("trans-date"), null, Bytes.toBytes("20090921010101999")); 1559 put.addColumn(Bytes.toBytes("trans-tags"), Bytes.toBytes("qual2"), Bytes.toBytes(value)); 1560 put.addColumn(Bytes.toBytes("trans-group"), null, Bytes.toBytes("adhocTransactionGroupId")); 1561 r.put(put); 1562 } 1563 } 1564 1565 @Test 1566 public void testFamilyWithAndWithoutColon() throws Exception { 1567 byte[] cf = Bytes.toBytes(COLUMN_FAMILY); 1568 this.region = initHRegion(tableName, method, CONF, cf); 1569 Put p = new Put(tableName.toBytes()); 1570 byte[] cfwithcolon = Bytes.toBytes(COLUMN_FAMILY + ":"); 1571 p.addColumn(cfwithcolon, cfwithcolon, cfwithcolon); 1572 boolean exception = false; 1573 try { 1574 this.region.put(p); 1575 } catch (NoSuchColumnFamilyException e) { 1576 exception = true; 1577 } 1578 assertTrue(exception); 1579 } 1580 1581 @Test 1582 public void testBatchPut_whileNoRowLocksHeld() throws IOException { 1583 final Put[] puts = new Put[10]; 1584 MetricsWALSource source = CompatibilitySingletonFactory.getInstance(MetricsWALSource.class); 1585 long syncs = prepareRegionForBachPut(puts, source, false); 1586 1587 OperationStatus[] codes = this.region.batchMutate(puts); 1588 assertEquals(10, codes.length); 1589 for (int i = 0; i < 10; i++) { 1590 assertEquals(OperationStatusCode.SUCCESS, codes[i].getOperationStatusCode()); 1591 } 1592 metricsAssertHelper.assertCounter("syncTimeNumOps", syncs + 1, source); 1593 1594 LOG.info("Next a batch put with one invalid family"); 1595 puts[5].addColumn(Bytes.toBytes("BAD_CF"), qual, value); 1596 codes = this.region.batchMutate(puts); 1597 assertEquals(10, codes.length); 1598 for (int i = 0; i < 10; i++) { 1599 assertEquals((i == 5) ? OperationStatusCode.BAD_FAMILY : OperationStatusCode.SUCCESS, 1600 codes[i].getOperationStatusCode()); 1601 } 1602 1603 metricsAssertHelper.assertCounter("syncTimeNumOps", syncs + 2, source); 1604 } 1605 1606 @Test 1607 public void testBatchPut_whileMultipleRowLocksHeld() throws Exception { 1608 final Put[] puts = new Put[10]; 1609 MetricsWALSource source = CompatibilitySingletonFactory.getInstance(MetricsWALSource.class); 1610 long syncs = prepareRegionForBachPut(puts, source, false); 1611 1612 puts[5].addColumn(Bytes.toBytes("BAD_CF"), qual, value); 1613 1614 LOG.info("batchPut will have to break into four batches to avoid row locks"); 1615 RowLock rowLock1 = region.getRowLock(Bytes.toBytes("row_2")); 1616 RowLock rowLock2 = region.getRowLock(Bytes.toBytes("row_1")); 1617 RowLock rowLock3 = region.getRowLock(Bytes.toBytes("row_3")); 1618 RowLock rowLock4 = region.getRowLock(Bytes.toBytes("row_3"), true); 1619 1620 MultithreadedTestUtil.TestContext ctx = new MultithreadedTestUtil.TestContext(CONF); 1621 final AtomicReference<OperationStatus[]> retFromThread = new AtomicReference<>(); 1622 final CountDownLatch startingPuts = new CountDownLatch(1); 1623 final CountDownLatch startingClose = new CountDownLatch(1); 1624 TestThread putter = new TestThread(ctx) { 1625 @Override 1626 public void doWork() throws IOException { 1627 startingPuts.countDown(); 1628 retFromThread.set(region.batchMutate(puts)); 1629 } 1630 }; 1631 LOG.info("...starting put thread while holding locks"); 1632 ctx.addThread(putter); 1633 ctx.startThreads(); 1634 1635 // Now attempt to close the region from another thread. Prior to HBASE-12565 1636 // this would cause the in-progress batchMutate operation to to fail with 1637 // exception because it use to release and re-acquire the close-guard lock 1638 // between batches. Caller then didn't get status indicating which writes succeeded. 1639 // We now expect this thread to block until the batchMutate call finishes. 1640 Thread regionCloseThread = new TestThread(ctx) { 1641 @Override 1642 public void doWork() { 1643 try { 1644 startingPuts.await(); 1645 // Give some time for the batch mutate to get in. 1646 // We don't want to race with the mutate 1647 Thread.sleep(10); 1648 startingClose.countDown(); 1649 HBaseTestingUtil.closeRegionAndWAL(region); 1650 region = null; 1651 } catch (IOException e) { 1652 throw new RuntimeException(e); 1653 } catch (InterruptedException e) { 1654 throw new RuntimeException(e); 1655 } 1656 } 1657 }; 1658 regionCloseThread.start(); 1659 1660 startingClose.await(); 1661 startingPuts.await(); 1662 Thread.sleep(100); 1663 LOG.info("...releasing row lock 1, which should let put thread continue"); 1664 rowLock1.release(); 1665 rowLock2.release(); 1666 rowLock3.release(); 1667 waitForCounter(source, "syncTimeNumOps", syncs + 1); 1668 1669 LOG.info("...joining on put thread"); 1670 ctx.stop(); 1671 regionCloseThread.join(); 1672 1673 OperationStatus[] codes = retFromThread.get(); 1674 for (int i = 0; i < codes.length; i++) { 1675 assertEquals((i == 5) ? OperationStatusCode.BAD_FAMILY : OperationStatusCode.SUCCESS, 1676 codes[i].getOperationStatusCode()); 1677 } 1678 rowLock4.release(); 1679 } 1680 1681 private void waitForCounter(MetricsWALSource source, String metricName, long expectedCount) 1682 throws InterruptedException { 1683 long startWait = EnvironmentEdgeManager.currentTime(); 1684 long currentCount; 1685 while ((currentCount = metricsAssertHelper.getCounter(metricName, source)) < expectedCount) { 1686 Thread.sleep(100); 1687 if (EnvironmentEdgeManager.currentTime() - startWait > 10000) { 1688 fail(String.format("Timed out waiting for '%s' >= '%s', currentCount=%s", metricName, 1689 expectedCount, currentCount)); 1690 } 1691 } 1692 } 1693 1694 @Test 1695 public void testAtomicBatchPut() throws IOException { 1696 final Put[] puts = new Put[10]; 1697 MetricsWALSource source = CompatibilitySingletonFactory.getInstance(MetricsWALSource.class); 1698 long syncs = prepareRegionForBachPut(puts, source, false); 1699 1700 // 1. Straight forward case, should succeed 1701 OperationStatus[] codes = this.region.batchMutate(puts, true); 1702 assertEquals(10, codes.length); 1703 for (int i = 0; i < 10; i++) { 1704 assertEquals(OperationStatusCode.SUCCESS, codes[i].getOperationStatusCode()); 1705 } 1706 metricsAssertHelper.assertCounter("syncTimeNumOps", syncs + 1, source); 1707 1708 // 2. Failed to get lock 1709 RowLock lock = region.getRowLock(Bytes.toBytes("row_" + 3)); 1710 // Method {@link HRegion#getRowLock(byte[])} is reentrant. As 'row_3' is locked in this 1711 // thread, need to run {@link HRegion#batchMutate(HRegion.BatchOperation)} in different thread 1712 MultithreadedTestUtil.TestContext ctx = new MultithreadedTestUtil.TestContext(CONF); 1713 final AtomicReference<IOException> retFromThread = new AtomicReference<>(); 1714 final CountDownLatch finishedPuts = new CountDownLatch(1); 1715 TestThread putter = new TestThread(ctx) { 1716 @Override 1717 public void doWork() throws IOException { 1718 try { 1719 region.batchMutate(puts, true); 1720 } catch (IOException ioe) { 1721 LOG.error("test failed!", ioe); 1722 retFromThread.set(ioe); 1723 } 1724 finishedPuts.countDown(); 1725 } 1726 }; 1727 LOG.info("...starting put thread while holding locks"); 1728 ctx.addThread(putter); 1729 ctx.startThreads(); 1730 LOG.info("...waiting for batch puts while holding locks"); 1731 try { 1732 finishedPuts.await(); 1733 } catch (InterruptedException e) { 1734 LOG.error("Interrupted!", e); 1735 } finally { 1736 if (lock != null) { 1737 lock.release(); 1738 } 1739 } 1740 assertNotNull(retFromThread.get()); 1741 metricsAssertHelper.assertCounter("syncTimeNumOps", syncs + 1, source); 1742 1743 // 3. Exception thrown in validation 1744 LOG.info("Next a batch put with one invalid family"); 1745 puts[5].addColumn(Bytes.toBytes("BAD_CF"), qual, value); 1746 thrown.expect(NoSuchColumnFamilyException.class); 1747 this.region.batchMutate(puts, true); 1748 } 1749 1750 @Test 1751 public void testBatchPutWithTsSlop() throws Exception { 1752 // add data with a timestamp that is too recent for range. Ensure assert 1753 CONF.setInt("hbase.hregion.keyvalue.timestamp.slop.millisecs", 1000); 1754 final Put[] puts = new Put[10]; 1755 MetricsWALSource source = CompatibilitySingletonFactory.getInstance(MetricsWALSource.class); 1756 1757 long syncs = prepareRegionForBachPut(puts, source, true); 1758 1759 OperationStatus[] codes = this.region.batchMutate(puts); 1760 assertEquals(10, codes.length); 1761 for (int i = 0; i < 10; i++) { 1762 assertEquals(OperationStatusCode.SANITY_CHECK_FAILURE, codes[i].getOperationStatusCode()); 1763 } 1764 metricsAssertHelper.assertCounter("syncTimeNumOps", syncs, source); 1765 } 1766 1767 /** Returns syncs initial syncTimeNumOps */ 1768 private long prepareRegionForBachPut(final Put[] puts, final MetricsWALSource source, 1769 boolean slop) throws IOException { 1770 this.region = initHRegion(tableName, method, CONF, COLUMN_FAMILY_BYTES); 1771 1772 LOG.info("First a batch put with all valid puts"); 1773 for (int i = 0; i < puts.length; i++) { 1774 puts[i] = slop 1775 ? new Put(Bytes.toBytes("row_" + i), Long.MAX_VALUE - 100) 1776 : new Put(Bytes.toBytes("row_" + i)); 1777 puts[i].addColumn(COLUMN_FAMILY_BYTES, qual, value); 1778 } 1779 1780 long syncs = metricsAssertHelper.getCounter("syncTimeNumOps", source); 1781 metricsAssertHelper.assertCounter("syncTimeNumOps", syncs, source); 1782 return syncs; 1783 } 1784 1785 // //////////////////////////////////////////////////////////////////////////// 1786 // checkAndMutate tests 1787 // //////////////////////////////////////////////////////////////////////////// 1788 @Test 1789 @Deprecated 1790 public void testCheckAndMutate_WithEmptyRowValue() throws IOException { 1791 byte[] row1 = Bytes.toBytes("row1"); 1792 byte[] fam1 = Bytes.toBytes("fam1"); 1793 byte[] qf1 = Bytes.toBytes("qualifier"); 1794 byte[] emptyVal = new byte[] {}; 1795 byte[] val1 = Bytes.toBytes("value1"); 1796 byte[] val2 = Bytes.toBytes("value2"); 1797 1798 // Setting up region 1799 this.region = initHRegion(tableName, method, CONF, fam1); 1800 // Putting empty data in key 1801 Put put = new Put(row1); 1802 put.addColumn(fam1, qf1, emptyVal); 1803 1804 // checkAndPut with empty value 1805 boolean res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 1806 new BinaryComparator(emptyVal), put); 1807 assertTrue(res); 1808 1809 // Putting data in key 1810 put = new Put(row1); 1811 put.addColumn(fam1, qf1, val1); 1812 1813 // checkAndPut with correct value 1814 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 1815 new BinaryComparator(emptyVal), put); 1816 assertTrue(res); 1817 1818 // not empty anymore 1819 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 1820 new BinaryComparator(emptyVal), put); 1821 assertFalse(res); 1822 1823 Delete delete = new Delete(row1); 1824 delete.addColumn(fam1, qf1); 1825 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 1826 new BinaryComparator(emptyVal), delete); 1827 assertFalse(res); 1828 1829 put = new Put(row1); 1830 put.addColumn(fam1, qf1, val2); 1831 // checkAndPut with correct value 1832 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(val1), 1833 put); 1834 assertTrue(res); 1835 1836 // checkAndDelete with correct value 1837 delete = new Delete(row1); 1838 delete.addColumn(fam1, qf1); 1839 delete.addColumn(fam1, qf1); 1840 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(val2), 1841 delete); 1842 assertTrue(res); 1843 1844 delete = new Delete(row1); 1845 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 1846 new BinaryComparator(emptyVal), delete); 1847 assertTrue(res); 1848 1849 // checkAndPut looking for a null value 1850 put = new Put(row1); 1851 put.addColumn(fam1, qf1, val1); 1852 1853 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new NullComparator(), put); 1854 assertTrue(res); 1855 } 1856 1857 @Test 1858 @Deprecated 1859 public void testCheckAndMutate_WithWrongValue() throws IOException { 1860 byte[] row1 = Bytes.toBytes("row1"); 1861 byte[] fam1 = Bytes.toBytes("fam1"); 1862 byte[] qf1 = Bytes.toBytes("qualifier"); 1863 byte[] val1 = Bytes.toBytes("value1"); 1864 byte[] val2 = Bytes.toBytes("value2"); 1865 BigDecimal bd1 = new BigDecimal(Double.MAX_VALUE); 1866 BigDecimal bd2 = new BigDecimal(Double.MIN_VALUE); 1867 1868 // Setting up region 1869 this.region = initHRegion(tableName, method, CONF, fam1); 1870 // Putting data in key 1871 Put put = new Put(row1); 1872 put.addColumn(fam1, qf1, val1); 1873 region.put(put); 1874 1875 // checkAndPut with wrong value 1876 boolean res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 1877 new BinaryComparator(val2), put); 1878 assertEquals(false, res); 1879 1880 // checkAndDelete with wrong value 1881 Delete delete = new Delete(row1); 1882 delete.addFamily(fam1); 1883 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(val2), 1884 put); 1885 assertEquals(false, res); 1886 1887 // Putting data in key 1888 put = new Put(row1); 1889 put.addColumn(fam1, qf1, Bytes.toBytes(bd1)); 1890 region.put(put); 1891 1892 // checkAndPut with wrong value 1893 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 1894 new BigDecimalComparator(bd2), put); 1895 assertEquals(false, res); 1896 1897 // checkAndDelete with wrong value 1898 delete = new Delete(row1); 1899 delete.addFamily(fam1); 1900 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 1901 new BigDecimalComparator(bd2), put); 1902 assertEquals(false, res); 1903 } 1904 1905 @Test 1906 @Deprecated 1907 public void testCheckAndMutate_WithCorrectValue() throws IOException { 1908 byte[] row1 = Bytes.toBytes("row1"); 1909 byte[] fam1 = Bytes.toBytes("fam1"); 1910 byte[] qf1 = Bytes.toBytes("qualifier"); 1911 byte[] val1 = Bytes.toBytes("value1"); 1912 BigDecimal bd1 = new BigDecimal(Double.MIN_VALUE); 1913 1914 // Setting up region 1915 this.region = initHRegion(tableName, method, CONF, fam1); 1916 // Putting data in key 1917 long now = EnvironmentEdgeManager.currentTime(); 1918 Put put = new Put(row1); 1919 put.addColumn(fam1, qf1, now, val1); 1920 region.put(put); 1921 1922 // checkAndPut with correct value 1923 boolean res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 1924 new BinaryComparator(val1), put); 1925 assertEquals("First", true, res); 1926 1927 // checkAndDelete with correct value 1928 Delete delete = new Delete(row1, now + 1); 1929 delete.addColumn(fam1, qf1); 1930 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(val1), 1931 delete); 1932 assertEquals("Delete", true, res); 1933 1934 // Putting data in key 1935 put = new Put(row1); 1936 put.addColumn(fam1, qf1, now + 2, Bytes.toBytes(bd1)); 1937 region.put(put); 1938 1939 // checkAndPut with correct value 1940 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 1941 new BigDecimalComparator(bd1), put); 1942 assertEquals("Second put", true, res); 1943 1944 // checkAndDelete with correct value 1945 delete = new Delete(row1, now + 3); 1946 delete.addColumn(fam1, qf1); 1947 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 1948 new BigDecimalComparator(bd1), delete); 1949 assertEquals("Second delete", true, res); 1950 } 1951 1952 @Test 1953 @Deprecated 1954 public void testCheckAndMutate_WithNonEqualCompareOp() throws IOException { 1955 byte[] row1 = Bytes.toBytes("row1"); 1956 byte[] fam1 = Bytes.toBytes("fam1"); 1957 byte[] qf1 = Bytes.toBytes("qualifier"); 1958 byte[] val1 = Bytes.toBytes("value1"); 1959 byte[] val2 = Bytes.toBytes("value2"); 1960 byte[] val3 = Bytes.toBytes("value3"); 1961 byte[] val4 = Bytes.toBytes("value4"); 1962 1963 // Setting up region 1964 this.region = initHRegion(tableName, method, CONF, fam1); 1965 // Putting val3 in key 1966 Put put = new Put(row1); 1967 put.addColumn(fam1, qf1, val3); 1968 region.put(put); 1969 1970 // Test CompareOp.LESS: original = val3, compare with val3, fail 1971 boolean res = 1972 region.checkAndMutate(row1, fam1, qf1, CompareOperator.LESS, new BinaryComparator(val3), put); 1973 assertEquals(false, res); 1974 1975 // Test CompareOp.LESS: original = val3, compare with val4, fail 1976 res = 1977 region.checkAndMutate(row1, fam1, qf1, CompareOperator.LESS, new BinaryComparator(val4), put); 1978 assertEquals(false, res); 1979 1980 // Test CompareOp.LESS: original = val3, compare with val2, 1981 // succeed (now value = val2) 1982 put = new Put(row1); 1983 put.addColumn(fam1, qf1, val2); 1984 res = 1985 region.checkAndMutate(row1, fam1, qf1, CompareOperator.LESS, new BinaryComparator(val2), put); 1986 assertEquals(true, res); 1987 1988 // Test CompareOp.LESS_OR_EQUAL: original = val2, compare with val3, fail 1989 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.LESS_OR_EQUAL, 1990 new BinaryComparator(val3), put); 1991 assertEquals(false, res); 1992 1993 // Test CompareOp.LESS_OR_EQUAL: original = val2, compare with val2, 1994 // succeed (value still = val2) 1995 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.LESS_OR_EQUAL, 1996 new BinaryComparator(val2), put); 1997 assertEquals(true, res); 1998 1999 // Test CompareOp.LESS_OR_EQUAL: original = val2, compare with val1, 2000 // succeed (now value = val3) 2001 put = new Put(row1); 2002 put.addColumn(fam1, qf1, val3); 2003 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.LESS_OR_EQUAL, 2004 new BinaryComparator(val1), put); 2005 assertEquals(true, res); 2006 2007 // Test CompareOp.GREATER: original = val3, compare with val3, fail 2008 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.GREATER, 2009 new BinaryComparator(val3), put); 2010 assertEquals(false, res); 2011 2012 // Test CompareOp.GREATER: original = val3, compare with val2, fail 2013 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.GREATER, 2014 new BinaryComparator(val2), put); 2015 assertEquals(false, res); 2016 2017 // Test CompareOp.GREATER: original = val3, compare with val4, 2018 // succeed (now value = val2) 2019 put = new Put(row1); 2020 put.addColumn(fam1, qf1, val2); 2021 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.GREATER, 2022 new BinaryComparator(val4), put); 2023 assertEquals(true, res); 2024 2025 // Test CompareOp.GREATER_OR_EQUAL: original = val2, compare with val1, fail 2026 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.GREATER_OR_EQUAL, 2027 new BinaryComparator(val1), put); 2028 assertEquals(false, res); 2029 2030 // Test CompareOp.GREATER_OR_EQUAL: original = val2, compare with val2, 2031 // succeed (value still = val2) 2032 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.GREATER_OR_EQUAL, 2033 new BinaryComparator(val2), put); 2034 assertEquals(true, res); 2035 2036 // Test CompareOp.GREATER_OR_EQUAL: original = val2, compare with val3, succeed 2037 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.GREATER_OR_EQUAL, 2038 new BinaryComparator(val3), put); 2039 assertEquals(true, res); 2040 } 2041 2042 @Test 2043 @Deprecated 2044 public void testCheckAndPut_ThatPutWasWritten() throws IOException { 2045 byte[] row1 = Bytes.toBytes("row1"); 2046 byte[] fam1 = Bytes.toBytes("fam1"); 2047 byte[] fam2 = Bytes.toBytes("fam2"); 2048 byte[] qf1 = Bytes.toBytes("qualifier"); 2049 byte[] val1 = Bytes.toBytes("value1"); 2050 byte[] val2 = Bytes.toBytes("value2"); 2051 2052 byte[][] families = { fam1, fam2 }; 2053 2054 // Setting up region 2055 this.region = initHRegion(tableName, method, CONF, families); 2056 // Putting data in the key to check 2057 Put put = new Put(row1); 2058 put.addColumn(fam1, qf1, val1); 2059 region.put(put); 2060 2061 // Creating put to add 2062 long ts = EnvironmentEdgeManager.currentTime(); 2063 KeyValue kv = new KeyValue(row1, fam2, qf1, ts, KeyValue.Type.Put, val2); 2064 put = new Put(row1); 2065 put.add(kv); 2066 2067 // checkAndPut with wrong value 2068 boolean res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 2069 new BinaryComparator(val1), put); 2070 assertEquals(true, res); 2071 2072 Get get = new Get(row1); 2073 get.addColumn(fam2, qf1); 2074 Cell[] actual = region.get(get).rawCells(); 2075 2076 Cell[] expected = { kv }; 2077 2078 assertEquals(expected.length, actual.length); 2079 for (int i = 0; i < actual.length; i++) { 2080 assertEquals(expected[i], actual[i]); 2081 } 2082 } 2083 2084 @Test 2085 @Deprecated 2086 public void testCheckAndPut_wrongRowInPut() throws IOException { 2087 this.region = initHRegion(tableName, method, CONF, COLUMNS); 2088 Put put = new Put(row2); 2089 put.addColumn(fam1, qual1, value1); 2090 try { 2091 region.checkAndMutate(row, fam1, qual1, CompareOperator.EQUAL, new BinaryComparator(value2), 2092 put); 2093 fail(); 2094 } catch (org.apache.hadoop.hbase.DoNotRetryIOException expected) { 2095 // expected exception. 2096 } 2097 } 2098 2099 @Test 2100 @Deprecated 2101 public void testCheckAndDelete_ThatDeleteWasWritten() throws IOException { 2102 byte[] row1 = Bytes.toBytes("row1"); 2103 byte[] fam1 = Bytes.toBytes("fam1"); 2104 byte[] fam2 = Bytes.toBytes("fam2"); 2105 byte[] qf1 = Bytes.toBytes("qualifier1"); 2106 byte[] qf2 = Bytes.toBytes("qualifier2"); 2107 byte[] qf3 = Bytes.toBytes("qualifier3"); 2108 byte[] val1 = Bytes.toBytes("value1"); 2109 byte[] val2 = Bytes.toBytes("value2"); 2110 byte[] val3 = Bytes.toBytes("value3"); 2111 byte[] emptyVal = new byte[] {}; 2112 2113 byte[][] families = { fam1, fam2 }; 2114 2115 // Setting up region 2116 this.region = initHRegion(tableName, method, CONF, families); 2117 // Put content 2118 Put put = new Put(row1); 2119 put.addColumn(fam1, qf1, val1); 2120 region.put(put); 2121 Threads.sleep(2); 2122 2123 put = new Put(row1); 2124 put.addColumn(fam1, qf1, val2); 2125 put.addColumn(fam2, qf1, val3); 2126 put.addColumn(fam2, qf2, val2); 2127 put.addColumn(fam2, qf3, val1); 2128 put.addColumn(fam1, qf3, val1); 2129 region.put(put); 2130 2131 LOG.info("get={}", region.get(new Get(row1).addColumn(fam1, qf1)).toString()); 2132 2133 // Multi-column delete 2134 Delete delete = new Delete(row1); 2135 delete.addColumn(fam1, qf1); 2136 delete.addColumn(fam2, qf1); 2137 delete.addColumn(fam1, qf3); 2138 boolean res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, 2139 new BinaryComparator(val2), delete); 2140 assertEquals(true, res); 2141 2142 Get get = new Get(row1); 2143 get.addColumn(fam1, qf1); 2144 get.addColumn(fam1, qf3); 2145 get.addColumn(fam2, qf2); 2146 Result r = region.get(get); 2147 assertEquals(2, r.size()); 2148 assertArrayEquals(val1, r.getValue(fam1, qf1)); 2149 assertArrayEquals(val2, r.getValue(fam2, qf2)); 2150 2151 // Family delete 2152 delete = new Delete(row1); 2153 delete.addFamily(fam2); 2154 res = region.checkAndMutate(row1, fam2, qf1, CompareOperator.EQUAL, 2155 new BinaryComparator(emptyVal), delete); 2156 assertEquals(true, res); 2157 2158 get = new Get(row1); 2159 r = region.get(get); 2160 assertEquals(1, r.size()); 2161 assertArrayEquals(val1, r.getValue(fam1, qf1)); 2162 2163 // Row delete 2164 delete = new Delete(row1); 2165 res = region.checkAndMutate(row1, fam1, qf1, CompareOperator.EQUAL, new BinaryComparator(val1), 2166 delete); 2167 assertEquals(true, res); 2168 get = new Get(row1); 2169 r = region.get(get); 2170 assertEquals(0, r.size()); 2171 } 2172 2173 @Test 2174 @Deprecated 2175 public void testCheckAndMutate_WithFilters() throws Throwable { 2176 final byte[] FAMILY = Bytes.toBytes("fam"); 2177 2178 // Setting up region 2179 this.region = initHRegion(tableName, method, CONF, FAMILY); 2180 2181 // Put one row 2182 Put put = new Put(row); 2183 put.addColumn(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a")); 2184 put.addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b")); 2185 put.addColumn(FAMILY, Bytes.toBytes("C"), Bytes.toBytes("c")); 2186 region.put(put); 2187 2188 // Put with success 2189 boolean ok = region.checkAndMutate(row, 2190 new FilterList( 2191 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2192 Bytes.toBytes("a")), 2193 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"), CompareOperator.EQUAL, 2194 Bytes.toBytes("b"))), 2195 new Put(row).addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d"))); 2196 assertTrue(ok); 2197 2198 Result result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("D"))); 2199 assertEquals("d", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("D")))); 2200 2201 // Put with failure 2202 ok = region.checkAndMutate(row, 2203 new FilterList( 2204 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2205 Bytes.toBytes("a")), 2206 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"), CompareOperator.EQUAL, 2207 Bytes.toBytes("c"))), 2208 new Put(row).addColumn(FAMILY, Bytes.toBytes("E"), Bytes.toBytes("e"))); 2209 assertFalse(ok); 2210 2211 assertTrue(region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("E"))).isEmpty()); 2212 2213 // Delete with success 2214 ok = region.checkAndMutate(row, 2215 new FilterList( 2216 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2217 Bytes.toBytes("a")), 2218 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"), CompareOperator.EQUAL, 2219 Bytes.toBytes("b"))), 2220 new Delete(row).addColumns(FAMILY, Bytes.toBytes("D"))); 2221 assertTrue(ok); 2222 2223 assertTrue(region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("D"))).isEmpty()); 2224 2225 // Mutate with success 2226 ok = region.checkAndRowMutate(row, 2227 new FilterList( 2228 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2229 Bytes.toBytes("a")), 2230 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"), CompareOperator.EQUAL, 2231 Bytes.toBytes("b"))), 2232 new RowMutations(row) 2233 .add((Mutation) new Put(row).addColumn(FAMILY, Bytes.toBytes("E"), Bytes.toBytes("e"))) 2234 .add((Mutation) new Delete(row).addColumns(FAMILY, Bytes.toBytes("A")))); 2235 assertTrue(ok); 2236 2237 result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("E"))); 2238 assertEquals("e", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("E")))); 2239 2240 assertTrue(region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("A"))).isEmpty()); 2241 } 2242 2243 @Test 2244 @Deprecated 2245 public void testCheckAndMutate_WithFiltersAndTimeRange() throws Throwable { 2246 final byte[] FAMILY = Bytes.toBytes("fam"); 2247 2248 // Setting up region 2249 this.region = initHRegion(tableName, method, CONF, FAMILY); 2250 2251 // Put with specifying the timestamp 2252 region.put(new Put(row).addColumn(FAMILY, Bytes.toBytes("A"), 100, Bytes.toBytes("a"))); 2253 2254 // Put with success 2255 boolean ok = region.checkAndMutate(row, 2256 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2257 Bytes.toBytes("a")), 2258 TimeRange.between(0, 101), 2259 new Put(row).addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b"))); 2260 assertTrue(ok); 2261 2262 Result result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("B"))); 2263 assertEquals("b", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("B")))); 2264 2265 // Put with failure 2266 ok = region.checkAndMutate(row, 2267 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2268 Bytes.toBytes("a")), 2269 TimeRange.between(0, 100), 2270 new Put(row).addColumn(FAMILY, Bytes.toBytes("C"), Bytes.toBytes("c"))); 2271 assertFalse(ok); 2272 2273 assertTrue(region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("C"))).isEmpty()); 2274 2275 // Mutate with success 2276 ok = region.checkAndRowMutate(row, 2277 new SingleColumnValueFilter( 2278 FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, Bytes.toBytes("a")), 2279 TimeRange.between(0, 101), 2280 new RowMutations(row) 2281 .add((Mutation) new Put(row).addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d"))) 2282 .add((Mutation) new Delete(row).addColumns(FAMILY, Bytes.toBytes("A")))); 2283 assertTrue(ok); 2284 2285 result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("D"))); 2286 assertEquals("d", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("D")))); 2287 2288 assertTrue(region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("A"))).isEmpty()); 2289 } 2290 2291 @Test 2292 @Deprecated 2293 public void testCheckAndMutate_wrongMutationType() throws Throwable { 2294 // Setting up region 2295 this.region = initHRegion(tableName, method, CONF, fam1); 2296 2297 try { 2298 region.checkAndMutate(row, fam1, qual1, CompareOperator.EQUAL, new BinaryComparator(value1), 2299 new Increment(row).addColumn(fam1, qual1, 1)); 2300 fail("should throw DoNotRetryIOException"); 2301 } catch (DoNotRetryIOException e) { 2302 assertEquals("Unsupported mutate type: INCREMENT", e.getMessage()); 2303 } 2304 2305 try { 2306 region.checkAndMutate(row, 2307 new SingleColumnValueFilter(fam1, qual1, CompareOperator.EQUAL, value1), 2308 new Increment(row).addColumn(fam1, qual1, 1)); 2309 fail("should throw DoNotRetryIOException"); 2310 } catch (DoNotRetryIOException e) { 2311 assertEquals("Unsupported mutate type: INCREMENT", e.getMessage()); 2312 } 2313 } 2314 2315 @Test 2316 @Deprecated 2317 public void testCheckAndMutate_wrongRow() throws Throwable { 2318 final byte[] wrongRow = Bytes.toBytes("wrongRow"); 2319 2320 // Setting up region 2321 this.region = initHRegion(tableName, method, CONF, fam1); 2322 2323 try { 2324 region.checkAndMutate(row, fam1, qual1, CompareOperator.EQUAL, new BinaryComparator(value1), 2325 new Put(wrongRow).addColumn(fam1, qual1, value1)); 2326 fail("should throw DoNotRetryIOException"); 2327 } catch (DoNotRetryIOException e) { 2328 assertEquals("The row of the action <wrongRow> doesn't match the original one <rowA>", 2329 e.getMessage()); 2330 } 2331 2332 try { 2333 region.checkAndMutate(row, 2334 new SingleColumnValueFilter(fam1, qual1, CompareOperator.EQUAL, value1), 2335 new Put(wrongRow).addColumn(fam1, qual1, value1)); 2336 fail("should throw DoNotRetryIOException"); 2337 } catch (DoNotRetryIOException e) { 2338 assertEquals("The row of the action <wrongRow> doesn't match the original one <rowA>", 2339 e.getMessage()); 2340 } 2341 2342 try { 2343 region.checkAndRowMutate(row, fam1, qual1, CompareOperator.EQUAL, 2344 new BinaryComparator(value1), 2345 new RowMutations(wrongRow).add((Mutation) new Put(wrongRow).addColumn(fam1, qual1, value1)) 2346 .add((Mutation) new Delete(wrongRow).addColumns(fam1, qual2))); 2347 fail("should throw DoNotRetryIOException"); 2348 } catch (DoNotRetryIOException e) { 2349 assertEquals("The row of the action <wrongRow> doesn't match the original one <rowA>", 2350 e.getMessage()); 2351 } 2352 2353 try { 2354 region.checkAndRowMutate(row, 2355 new SingleColumnValueFilter(fam1, qual1, CompareOperator.EQUAL, value1), 2356 new RowMutations(wrongRow).add((Mutation) new Put(wrongRow).addColumn(fam1, qual1, value1)) 2357 .add((Mutation) new Delete(wrongRow).addColumns(fam1, qual2))); 2358 fail("should throw DoNotRetryIOException"); 2359 } catch (DoNotRetryIOException e) { 2360 assertEquals("The row of the action <wrongRow> doesn't match the original one <rowA>", 2361 e.getMessage()); 2362 } 2363 } 2364 2365 @Test 2366 public void testCheckAndMutateWithEmptyRowValue() throws IOException { 2367 byte[] row1 = Bytes.toBytes("row1"); 2368 byte[] fam1 = Bytes.toBytes("fam1"); 2369 byte[] qf1 = Bytes.toBytes("qualifier"); 2370 byte[] emptyVal = new byte[] {}; 2371 byte[] val1 = Bytes.toBytes("value1"); 2372 byte[] val2 = Bytes.toBytes("value2"); 2373 2374 // Setting up region 2375 this.region = initHRegion(tableName, method, CONF, fam1); 2376 // Putting empty data in key 2377 Put put = new Put(row1); 2378 put.addColumn(fam1, qf1, emptyVal); 2379 2380 // checkAndPut with empty value 2381 CheckAndMutateResult res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2382 .ifMatches(fam1, qf1, CompareOperator.EQUAL, emptyVal).build(put)); 2383 assertTrue(res.isSuccess()); 2384 assertNull(res.getResult()); 2385 2386 // Putting data in key 2387 put = new Put(row1); 2388 put.addColumn(fam1, qf1, val1); 2389 2390 // checkAndPut with correct value 2391 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2392 .ifMatches(fam1, qf1, CompareOperator.EQUAL, emptyVal).build(put)); 2393 assertTrue(res.isSuccess()); 2394 assertNull(res.getResult()); 2395 2396 // not empty anymore 2397 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2398 .ifMatches(fam1, qf1, CompareOperator.EQUAL, emptyVal).build(put)); 2399 assertFalse(res.isSuccess()); 2400 assertNull(res.getResult()); 2401 2402 Delete delete = new Delete(row1); 2403 delete.addColumn(fam1, qf1); 2404 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2405 .ifMatches(fam1, qf1, CompareOperator.EQUAL, emptyVal).build(delete)); 2406 assertFalse(res.isSuccess()); 2407 assertNull(res.getResult()); 2408 2409 put = new Put(row1); 2410 put.addColumn(fam1, qf1, val2); 2411 // checkAndPut with correct value 2412 res = region.checkAndMutate( 2413 CheckAndMutate.newBuilder(row1).ifMatches(fam1, qf1, CompareOperator.EQUAL, val1).build(put)); 2414 assertTrue(res.isSuccess()); 2415 assertNull(res.getResult()); 2416 2417 // checkAndDelete with correct value 2418 delete = new Delete(row1); 2419 delete.addColumn(fam1, qf1); 2420 delete.addColumn(fam1, qf1); 2421 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2422 .ifMatches(fam1, qf1, CompareOperator.EQUAL, val2).build(delete)); 2423 assertTrue(res.isSuccess()); 2424 assertNull(res.getResult()); 2425 2426 delete = new Delete(row1); 2427 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2428 .ifMatches(fam1, qf1, CompareOperator.EQUAL, emptyVal).build(delete)); 2429 assertTrue(res.isSuccess()); 2430 assertNull(res.getResult()); 2431 2432 // checkAndPut looking for a null value 2433 put = new Put(row1); 2434 put.addColumn(fam1, qf1, val1); 2435 2436 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1).ifNotExists(fam1, qf1).build(put)); 2437 assertTrue(res.isSuccess()); 2438 assertNull(res.getResult()); 2439 } 2440 2441 @Test 2442 public void testCheckAndMutateWithWrongValue() throws IOException { 2443 byte[] row1 = Bytes.toBytes("row1"); 2444 byte[] fam1 = Bytes.toBytes("fam1"); 2445 byte[] qf1 = Bytes.toBytes("qualifier"); 2446 byte[] val1 = Bytes.toBytes("value1"); 2447 byte[] val2 = Bytes.toBytes("value2"); 2448 BigDecimal bd1 = new BigDecimal(Double.MAX_VALUE); 2449 BigDecimal bd2 = new BigDecimal(Double.MIN_VALUE); 2450 2451 // Setting up region 2452 this.region = initHRegion(tableName, method, CONF, fam1); 2453 // Putting data in key 2454 Put put = new Put(row1); 2455 put.addColumn(fam1, qf1, val1); 2456 region.put(put); 2457 2458 // checkAndPut with wrong value 2459 CheckAndMutateResult res = region.checkAndMutate( 2460 CheckAndMutate.newBuilder(row1).ifMatches(fam1, qf1, CompareOperator.EQUAL, val2).build(put)); 2461 assertFalse(res.isSuccess()); 2462 assertNull(res.getResult()); 2463 2464 // checkAndDelete with wrong value 2465 Delete delete = new Delete(row1); 2466 delete.addFamily(fam1); 2467 res = region.checkAndMutate( 2468 CheckAndMutate.newBuilder(row1).ifMatches(fam1, qf1, CompareOperator.EQUAL, val2).build(put)); 2469 assertFalse(res.isSuccess()); 2470 assertNull(res.getResult()); 2471 2472 // Putting data in key 2473 put = new Put(row1); 2474 put.addColumn(fam1, qf1, Bytes.toBytes(bd1)); 2475 region.put(put); 2476 2477 // checkAndPut with wrong value 2478 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2479 .ifMatches(fam1, qf1, CompareOperator.EQUAL, Bytes.toBytes(bd2)).build(put)); 2480 assertFalse(res.isSuccess()); 2481 assertNull(res.getResult()); 2482 2483 // checkAndDelete with wrong value 2484 delete = new Delete(row1); 2485 delete.addFamily(fam1); 2486 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2487 .ifMatches(fam1, qf1, CompareOperator.EQUAL, Bytes.toBytes(bd2)).build(delete)); 2488 assertFalse(res.isSuccess()); 2489 assertNull(res.getResult()); 2490 } 2491 2492 @Test 2493 public void testCheckAndMutateWithCorrectValue() throws IOException { 2494 byte[] row1 = Bytes.toBytes("row1"); 2495 byte[] fam1 = Bytes.toBytes("fam1"); 2496 byte[] qf1 = Bytes.toBytes("qualifier"); 2497 byte[] val1 = Bytes.toBytes("value1"); 2498 BigDecimal bd1 = new BigDecimal(Double.MIN_VALUE); 2499 2500 // Setting up region 2501 this.region = initHRegion(tableName, method, CONF, fam1); 2502 // Putting data in key 2503 long now = EnvironmentEdgeManager.currentTime(); 2504 Put put = new Put(row1); 2505 put.addColumn(fam1, qf1, now, val1); 2506 region.put(put); 2507 2508 // checkAndPut with correct value 2509 CheckAndMutateResult res = region.checkAndMutate( 2510 CheckAndMutate.newBuilder(row1).ifMatches(fam1, qf1, CompareOperator.EQUAL, val1).build(put)); 2511 assertTrue("First", res.isSuccess()); 2512 2513 // checkAndDelete with correct value 2514 Delete delete = new Delete(row1, now + 1); 2515 delete.addColumn(fam1, qf1); 2516 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2517 .ifMatches(fam1, qf1, CompareOperator.EQUAL, val1).build(delete)); 2518 assertTrue("Delete", res.isSuccess()); 2519 assertNull(res.getResult()); 2520 2521 // Putting data in key 2522 put = new Put(row1); 2523 put.addColumn(fam1, qf1, now + 2, Bytes.toBytes(bd1)); 2524 region.put(put); 2525 2526 // checkAndPut with correct value 2527 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2528 .ifMatches(fam1, qf1, CompareOperator.EQUAL, Bytes.toBytes(bd1)).build(put)); 2529 assertTrue("Second put", res.isSuccess()); 2530 assertNull(res.getResult()); 2531 2532 // checkAndDelete with correct value 2533 delete = new Delete(row1, now + 3); 2534 delete.addColumn(fam1, qf1); 2535 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2536 .ifMatches(fam1, qf1, CompareOperator.EQUAL, Bytes.toBytes(bd1)).build(delete)); 2537 assertTrue("Second delete", res.isSuccess()); 2538 assertNull(res.getResult()); 2539 } 2540 2541 @Test 2542 public void testCheckAndMutateWithNonEqualCompareOp() throws IOException { 2543 byte[] row1 = Bytes.toBytes("row1"); 2544 byte[] fam1 = Bytes.toBytes("fam1"); 2545 byte[] qf1 = Bytes.toBytes("qualifier"); 2546 byte[] val1 = Bytes.toBytes("value1"); 2547 byte[] val2 = Bytes.toBytes("value2"); 2548 byte[] val3 = Bytes.toBytes("value3"); 2549 byte[] val4 = Bytes.toBytes("value4"); 2550 2551 // Setting up region 2552 this.region = initHRegion(tableName, method, CONF, fam1); 2553 // Putting val3 in key 2554 Put put = new Put(row1); 2555 put.addColumn(fam1, qf1, val3); 2556 region.put(put); 2557 2558 // Test CompareOp.LESS: original = val3, compare with val3, fail 2559 CheckAndMutateResult res = region.checkAndMutate( 2560 CheckAndMutate.newBuilder(row1).ifMatches(fam1, qf1, CompareOperator.LESS, val3).build(put)); 2561 assertFalse(res.isSuccess()); 2562 assertNull(res.getResult()); 2563 2564 // Test CompareOp.LESS: original = val3, compare with val4, fail 2565 res = region.checkAndMutate( 2566 CheckAndMutate.newBuilder(row1).ifMatches(fam1, qf1, CompareOperator.LESS, val4).build(put)); 2567 assertFalse(res.isSuccess()); 2568 assertNull(res.getResult()); 2569 2570 // Test CompareOp.LESS: original = val3, compare with val2, 2571 // succeed (now value = val2) 2572 put = new Put(row1); 2573 put.addColumn(fam1, qf1, val2); 2574 res = region.checkAndMutate( 2575 CheckAndMutate.newBuilder(row1).ifMatches(fam1, qf1, CompareOperator.LESS, val2).build(put)); 2576 assertTrue(res.isSuccess()); 2577 assertNull(res.getResult()); 2578 2579 // Test CompareOp.LESS_OR_EQUAL: original = val2, compare with val3, fail 2580 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2581 .ifMatches(fam1, qf1, CompareOperator.LESS_OR_EQUAL, val3).build(put)); 2582 assertFalse(res.isSuccess()); 2583 assertNull(res.getResult()); 2584 2585 // Test CompareOp.LESS_OR_EQUAL: original = val2, compare with val2, 2586 // succeed (value still = val2) 2587 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2588 .ifMatches(fam1, qf1, CompareOperator.LESS_OR_EQUAL, val2).build(put)); 2589 assertTrue(res.isSuccess()); 2590 assertNull(res.getResult()); 2591 2592 // Test CompareOp.LESS_OR_EQUAL: original = val2, compare with val1, 2593 // succeed (now value = val3) 2594 put = new Put(row1); 2595 put.addColumn(fam1, qf1, val3); 2596 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2597 .ifMatches(fam1, qf1, CompareOperator.LESS_OR_EQUAL, val1).build(put)); 2598 assertTrue(res.isSuccess()); 2599 assertNull(res.getResult()); 2600 2601 // Test CompareOp.GREATER: original = val3, compare with val3, fail 2602 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2603 .ifMatches(fam1, qf1, CompareOperator.GREATER, val3).build(put)); 2604 assertFalse(res.isSuccess()); 2605 assertNull(res.getResult()); 2606 2607 // Test CompareOp.GREATER: original = val3, compare with val2, fail 2608 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2609 .ifMatches(fam1, qf1, CompareOperator.GREATER, val2).build(put)); 2610 assertFalse(res.isSuccess()); 2611 assertNull(res.getResult()); 2612 2613 // Test CompareOp.GREATER: original = val3, compare with val4, 2614 // succeed (now value = val2) 2615 put = new Put(row1); 2616 put.addColumn(fam1, qf1, val2); 2617 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2618 .ifMatches(fam1, qf1, CompareOperator.GREATER, val4).build(put)); 2619 assertTrue(res.isSuccess()); 2620 assertNull(res.getResult()); 2621 2622 // Test CompareOp.GREATER_OR_EQUAL: original = val2, compare with val1, fail 2623 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2624 .ifMatches(fam1, qf1, CompareOperator.GREATER_OR_EQUAL, val1).build(put)); 2625 assertFalse(res.isSuccess()); 2626 assertNull(res.getResult()); 2627 2628 // Test CompareOp.GREATER_OR_EQUAL: original = val2, compare with val2, 2629 // succeed (value still = val2) 2630 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2631 .ifMatches(fam1, qf1, CompareOperator.GREATER_OR_EQUAL, val2).build(put)); 2632 assertTrue(res.isSuccess()); 2633 assertNull(res.getResult()); 2634 2635 // Test CompareOp.GREATER_OR_EQUAL: original = val2, compare with val3, succeed 2636 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2637 .ifMatches(fam1, qf1, CompareOperator.GREATER_OR_EQUAL, val3).build(put)); 2638 assertTrue(res.isSuccess()); 2639 assertNull(res.getResult()); 2640 } 2641 2642 @Test 2643 public void testCheckAndPutThatPutWasWritten() throws IOException { 2644 byte[] row1 = Bytes.toBytes("row1"); 2645 byte[] fam1 = Bytes.toBytes("fam1"); 2646 byte[] fam2 = Bytes.toBytes("fam2"); 2647 byte[] qf1 = Bytes.toBytes("qualifier"); 2648 byte[] val1 = Bytes.toBytes("value1"); 2649 byte[] val2 = Bytes.toBytes("value2"); 2650 2651 byte[][] families = { fam1, fam2 }; 2652 2653 // Setting up region 2654 this.region = initHRegion(tableName, method, CONF, families); 2655 // Putting data in the key to check 2656 Put put = new Put(row1); 2657 put.addColumn(fam1, qf1, val1); 2658 region.put(put); 2659 2660 // Creating put to add 2661 long ts = EnvironmentEdgeManager.currentTime(); 2662 KeyValue kv = new KeyValue(row1, fam2, qf1, ts, KeyValue.Type.Put, val2); 2663 put = new Put(row1); 2664 put.add(kv); 2665 2666 // checkAndPut with wrong value 2667 CheckAndMutateResult res = region.checkAndMutate( 2668 CheckAndMutate.newBuilder(row1).ifMatches(fam1, qf1, CompareOperator.EQUAL, val1).build(put)); 2669 assertTrue(res.isSuccess()); 2670 assertNull(res.getResult()); 2671 2672 Get get = new Get(row1); 2673 get.addColumn(fam2, qf1); 2674 Cell[] actual = region.get(get).rawCells(); 2675 2676 Cell[] expected = { kv }; 2677 2678 assertEquals(expected.length, actual.length); 2679 for (int i = 0; i < actual.length; i++) { 2680 assertEquals(expected[i], actual[i]); 2681 } 2682 } 2683 2684 @Test 2685 public void testCheckAndDeleteThatDeleteWasWritten() throws IOException { 2686 byte[] row1 = Bytes.toBytes("row1"); 2687 byte[] fam1 = Bytes.toBytes("fam1"); 2688 byte[] fam2 = Bytes.toBytes("fam2"); 2689 byte[] qf1 = Bytes.toBytes("qualifier1"); 2690 byte[] qf2 = Bytes.toBytes("qualifier2"); 2691 byte[] qf3 = Bytes.toBytes("qualifier3"); 2692 byte[] val1 = Bytes.toBytes("value1"); 2693 byte[] val2 = Bytes.toBytes("value2"); 2694 byte[] val3 = Bytes.toBytes("value3"); 2695 byte[] emptyVal = new byte[] {}; 2696 2697 byte[][] families = { fam1, fam2 }; 2698 2699 // Setting up region 2700 this.region = initHRegion(tableName, method, CONF, families); 2701 // Put content 2702 Put put = new Put(row1); 2703 put.addColumn(fam1, qf1, val1); 2704 region.put(put); 2705 Threads.sleep(2); 2706 2707 put = new Put(row1); 2708 put.addColumn(fam1, qf1, val2); 2709 put.addColumn(fam2, qf1, val3); 2710 put.addColumn(fam2, qf2, val2); 2711 put.addColumn(fam2, qf3, val1); 2712 put.addColumn(fam1, qf3, val1); 2713 region.put(put); 2714 2715 LOG.info("get={}", region.get(new Get(row1).addColumn(fam1, qf1)).toString()); 2716 2717 // Multi-column delete 2718 Delete delete = new Delete(row1); 2719 delete.addColumn(fam1, qf1); 2720 delete.addColumn(fam2, qf1); 2721 delete.addColumn(fam1, qf3); 2722 CheckAndMutateResult res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2723 .ifMatches(fam1, qf1, CompareOperator.EQUAL, val2).build(delete)); 2724 assertTrue(res.isSuccess()); 2725 assertNull(res.getResult()); 2726 2727 Get get = new Get(row1); 2728 get.addColumn(fam1, qf1); 2729 get.addColumn(fam1, qf3); 2730 get.addColumn(fam2, qf2); 2731 Result r = region.get(get); 2732 assertEquals(2, r.size()); 2733 assertArrayEquals(val1, r.getValue(fam1, qf1)); 2734 assertArrayEquals(val2, r.getValue(fam2, qf2)); 2735 2736 // Family delete 2737 delete = new Delete(row1); 2738 delete.addFamily(fam2); 2739 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2740 .ifMatches(fam2, qf1, CompareOperator.EQUAL, emptyVal).build(delete)); 2741 assertTrue(res.isSuccess()); 2742 assertNull(res.getResult()); 2743 2744 get = new Get(row1); 2745 r = region.get(get); 2746 assertEquals(1, r.size()); 2747 assertArrayEquals(val1, r.getValue(fam1, qf1)); 2748 2749 // Row delete 2750 delete = new Delete(row1); 2751 res = region.checkAndMutate(CheckAndMutate.newBuilder(row1) 2752 .ifMatches(fam1, qf1, CompareOperator.EQUAL, val1).build(delete)); 2753 assertTrue(res.isSuccess()); 2754 assertNull(res.getResult()); 2755 2756 get = new Get(row1); 2757 r = region.get(get); 2758 assertEquals(0, r.size()); 2759 } 2760 2761 @Test 2762 public void testCheckAndMutateWithFilters() throws Throwable { 2763 final byte[] FAMILY = Bytes.toBytes("fam"); 2764 2765 // Setting up region 2766 this.region = initHRegion(tableName, method, CONF, FAMILY); 2767 2768 // Put one row 2769 Put put = new Put(row); 2770 put.addColumn(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a")); 2771 put.addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b")); 2772 put.addColumn(FAMILY, Bytes.toBytes("C"), Bytes.toBytes("c")); 2773 region.put(put); 2774 2775 // Put with success 2776 CheckAndMutateResult res = region.checkAndMutate(CheckAndMutate.newBuilder(row) 2777 .ifMatches(new FilterList( 2778 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2779 Bytes.toBytes("a")), 2780 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"), CompareOperator.EQUAL, 2781 Bytes.toBytes("b")))) 2782 .build(new Put(row).addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d")))); 2783 assertTrue(res.isSuccess()); 2784 assertNull(res.getResult()); 2785 2786 Result result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("D"))); 2787 assertEquals("d", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("D")))); 2788 2789 // Put with failure 2790 res = region.checkAndMutate(CheckAndMutate.newBuilder(row) 2791 .ifMatches(new FilterList( 2792 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2793 Bytes.toBytes("a")), 2794 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"), CompareOperator.EQUAL, 2795 Bytes.toBytes("c")))) 2796 .build(new Put(row).addColumn(FAMILY, Bytes.toBytes("E"), Bytes.toBytes("e")))); 2797 assertFalse(res.isSuccess()); 2798 assertNull(res.getResult()); 2799 2800 assertTrue(region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("E"))).isEmpty()); 2801 2802 // Delete with success 2803 res = region.checkAndMutate(CheckAndMutate.newBuilder(row) 2804 .ifMatches(new FilterList( 2805 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2806 Bytes.toBytes("a")), 2807 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"), CompareOperator.EQUAL, 2808 Bytes.toBytes("b")))) 2809 .build(new Delete(row).addColumns(FAMILY, Bytes.toBytes("D")))); 2810 assertTrue(res.isSuccess()); 2811 assertNull(res.getResult()); 2812 2813 assertTrue(region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("D"))).isEmpty()); 2814 2815 // Mutate with success 2816 res = region.checkAndMutate(CheckAndMutate.newBuilder(row) 2817 .ifMatches(new FilterList( 2818 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2819 Bytes.toBytes("a")), 2820 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"), CompareOperator.EQUAL, 2821 Bytes.toBytes("b")))) 2822 .build(new RowMutations(row) 2823 .add((Mutation) new Put(row).addColumn(FAMILY, Bytes.toBytes("E"), Bytes.toBytes("e"))) 2824 .add((Mutation) new Delete(row).addColumns(FAMILY, Bytes.toBytes("A"))))); 2825 assertTrue(res.isSuccess()); 2826 assertNull(res.getResult()); 2827 2828 result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("E"))); 2829 assertEquals("e", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("E")))); 2830 2831 assertTrue(region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("A"))).isEmpty()); 2832 } 2833 2834 @Test 2835 public void testCheckAndMutateWithFiltersAndTimeRange() throws Throwable { 2836 final byte[] FAMILY = Bytes.toBytes("fam"); 2837 2838 // Setting up region 2839 this.region = initHRegion(tableName, method, CONF, FAMILY); 2840 2841 // Put with specifying the timestamp 2842 region.put(new Put(row).addColumn(FAMILY, Bytes.toBytes("A"), 100, Bytes.toBytes("a"))); 2843 2844 // Put with success 2845 CheckAndMutateResult res = region.checkAndMutate(CheckAndMutate.newBuilder(row) 2846 .ifMatches(new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2847 Bytes.toBytes("a"))) 2848 .timeRange(TimeRange.between(0, 101)) 2849 .build(new Put(row).addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b")))); 2850 assertTrue(res.isSuccess()); 2851 assertNull(res.getResult()); 2852 2853 Result result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("B"))); 2854 assertEquals("b", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("B")))); 2855 2856 // Put with failure 2857 res = region.checkAndMutate(CheckAndMutate.newBuilder(row) 2858 .ifMatches(new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2859 Bytes.toBytes("a"))) 2860 .timeRange(TimeRange.between(0, 100)) 2861 .build(new Put(row).addColumn(FAMILY, Bytes.toBytes("C"), Bytes.toBytes("c")))); 2862 assertFalse(res.isSuccess()); 2863 assertNull(res.getResult()); 2864 2865 assertTrue(region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("C"))).isEmpty()); 2866 2867 // RowMutations with success 2868 res = region.checkAndMutate(CheckAndMutate.newBuilder(row) 2869 .ifMatches(new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2870 Bytes.toBytes("a"))) 2871 .timeRange(TimeRange.between(0, 101)) 2872 .build(new RowMutations(row) 2873 .add((Mutation) new Put(row).addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d"))) 2874 .add((Mutation) new Delete(row).addColumns(FAMILY, Bytes.toBytes("A"))))); 2875 assertTrue(res.isSuccess()); 2876 assertNull(res.getResult()); 2877 2878 result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("D"))); 2879 assertEquals("d", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("D")))); 2880 2881 assertTrue(region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("A"))).isEmpty()); 2882 } 2883 2884 @Test 2885 public void testCheckAndIncrement() throws Throwable { 2886 final byte[] FAMILY = Bytes.toBytes("fam"); 2887 2888 // Setting up region 2889 this.region = initHRegion(tableName, method, CONF, FAMILY); 2890 2891 region.put(new Put(row).addColumn(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a"))); 2892 2893 // CheckAndIncrement with correct value 2894 CheckAndMutateResult res = region.checkAndMutate( 2895 CheckAndMutate.newBuilder(row).ifEquals(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a")) 2896 .build(new Increment(row).addColumn(FAMILY, Bytes.toBytes("B"), 1))); 2897 assertTrue(res.isSuccess()); 2898 assertEquals(1, Bytes.toLong(res.getResult().getValue(FAMILY, Bytes.toBytes("B")))); 2899 2900 Result result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("B"))); 2901 assertEquals(1, Bytes.toLong(result.getValue(FAMILY, Bytes.toBytes("B")))); 2902 2903 // CheckAndIncrement with wrong value 2904 res = region.checkAndMutate( 2905 CheckAndMutate.newBuilder(row).ifEquals(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("b")) 2906 .build(new Increment(row).addColumn(FAMILY, Bytes.toBytes("B"), 1))); 2907 assertFalse(res.isSuccess()); 2908 assertNull(res.getResult()); 2909 2910 result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("B"))); 2911 assertEquals(1, Bytes.toLong(result.getValue(FAMILY, Bytes.toBytes("B")))); 2912 2913 region.put(new Put(row).addColumn(FAMILY, Bytes.toBytes("C"), Bytes.toBytes("c"))); 2914 2915 // CheckAndIncrement with a filter and correct value 2916 res = region.checkAndMutate(CheckAndMutate.newBuilder(row) 2917 .ifMatches(new FilterList( 2918 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2919 Bytes.toBytes("a")), 2920 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("C"), CompareOperator.EQUAL, 2921 Bytes.toBytes("c")))) 2922 .build(new Increment(row).addColumn(FAMILY, Bytes.toBytes("B"), 2))); 2923 assertTrue(res.isSuccess()); 2924 assertEquals(3, Bytes.toLong(res.getResult().getValue(FAMILY, Bytes.toBytes("B")))); 2925 2926 result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("B"))); 2927 assertEquals(3, Bytes.toLong(result.getValue(FAMILY, Bytes.toBytes("B")))); 2928 2929 // CheckAndIncrement with a filter and correct value 2930 res = region.checkAndMutate(CheckAndMutate.newBuilder(row) 2931 .ifMatches(new FilterList( 2932 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2933 Bytes.toBytes("b")), 2934 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("C"), CompareOperator.EQUAL, 2935 Bytes.toBytes("d")))) 2936 .build(new Increment(row).addColumn(FAMILY, Bytes.toBytes("B"), 2))); 2937 assertFalse(res.isSuccess()); 2938 assertNull(res.getResult()); 2939 2940 result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("B"))); 2941 assertEquals(3, Bytes.toLong(result.getValue(FAMILY, Bytes.toBytes("B")))); 2942 } 2943 2944 @Test 2945 public void testCheckAndAppend() throws Throwable { 2946 final byte[] FAMILY = Bytes.toBytes("fam"); 2947 2948 // Setting up region 2949 this.region = initHRegion(tableName, method, CONF, FAMILY); 2950 2951 region.put(new Put(row).addColumn(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a"))); 2952 2953 // CheckAndAppend with correct value 2954 CheckAndMutateResult res = region.checkAndMutate( 2955 CheckAndMutate.newBuilder(row).ifEquals(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a")) 2956 .build(new Append(row).addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b")))); 2957 assertTrue(res.isSuccess()); 2958 assertEquals("b", Bytes.toString(res.getResult().getValue(FAMILY, Bytes.toBytes("B")))); 2959 2960 Result result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("B"))); 2961 assertEquals("b", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("B")))); 2962 2963 // CheckAndAppend with wrong value 2964 res = region.checkAndMutate( 2965 CheckAndMutate.newBuilder(row).ifEquals(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("b")) 2966 .build(new Append(row).addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b")))); 2967 assertFalse(res.isSuccess()); 2968 assertNull(res.getResult()); 2969 2970 result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("B"))); 2971 assertEquals("b", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("B")))); 2972 2973 region.put(new Put(row).addColumn(FAMILY, Bytes.toBytes("C"), Bytes.toBytes("c"))); 2974 2975 // CheckAndAppend with a filter and correct value 2976 res = region.checkAndMutate(CheckAndMutate.newBuilder(row) 2977 .ifMatches(new FilterList( 2978 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2979 Bytes.toBytes("a")), 2980 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("C"), CompareOperator.EQUAL, 2981 Bytes.toBytes("c")))) 2982 .build(new Append(row).addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("bb")))); 2983 assertTrue(res.isSuccess()); 2984 assertEquals("bbb", Bytes.toString(res.getResult().getValue(FAMILY, Bytes.toBytes("B")))); 2985 2986 result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("B"))); 2987 assertEquals("bbb", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("B")))); 2988 2989 // CheckAndAppend with a filter and wrong value 2990 res = region.checkAndMutate(CheckAndMutate.newBuilder(row) 2991 .ifMatches(new FilterList( 2992 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"), CompareOperator.EQUAL, 2993 Bytes.toBytes("b")), 2994 new SingleColumnValueFilter(FAMILY, Bytes.toBytes("C"), CompareOperator.EQUAL, 2995 Bytes.toBytes("d")))) 2996 .build(new Append(row).addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("bb")))); 2997 assertFalse(res.isSuccess()); 2998 assertNull(res.getResult()); 2999 3000 result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("B"))); 3001 assertEquals("bbb", Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("B")))); 3002 } 3003 3004 @Test 3005 public void testCheckAndIncrementAndAppend() throws Throwable { 3006 // Setting up region 3007 this.region = initHRegion(tableName, method, CONF, fam1); 3008 3009 // CheckAndMutate with Increment and Append 3010 CheckAndMutate checkAndMutate = CheckAndMutate.newBuilder(row).ifNotExists(fam1, qual) 3011 .build(new RowMutations(row).add((Mutation) new Increment(row).addColumn(fam1, qual1, 1L)) 3012 .add((Mutation) new Append(row).addColumn(fam1, qual2, Bytes.toBytes("a")))); 3013 3014 CheckAndMutateResult result = region.checkAndMutate(checkAndMutate); 3015 assertTrue(result.isSuccess()); 3016 assertEquals(1L, Bytes.toLong(result.getResult().getValue(fam1, qual1))); 3017 assertEquals("a", Bytes.toString(result.getResult().getValue(fam1, qual2))); 3018 3019 Result r = region.get(new Get(row)); 3020 assertEquals(1L, Bytes.toLong(r.getValue(fam1, qual1))); 3021 assertEquals("a", Bytes.toString(r.getValue(fam1, qual2))); 3022 3023 // Set return results to false 3024 checkAndMutate = CheckAndMutate.newBuilder(row).ifNotExists(fam1, qual) 3025 .build(new RowMutations(row) 3026 .add((Mutation) new Increment(row).addColumn(fam1, qual1, 1L).setReturnResults(false)) 3027 .add((Mutation) new Append(row).addColumn(fam1, qual2, Bytes.toBytes("a")) 3028 .setReturnResults(false))); 3029 3030 result = region.checkAndMutate(checkAndMutate); 3031 assertTrue(result.isSuccess()); 3032 assertNull(result.getResult().getValue(fam1, qual1)); 3033 assertNull(result.getResult().getValue(fam1, qual2)); 3034 3035 r = region.get(new Get(row)); 3036 assertEquals(2L, Bytes.toLong(r.getValue(fam1, qual1))); 3037 assertEquals("aa", Bytes.toString(r.getValue(fam1, qual2))); 3038 3039 checkAndMutate = CheckAndMutate.newBuilder(row).ifNotExists(fam1, qual) 3040 .build(new RowMutations(row).add((Mutation) new Increment(row).addColumn(fam1, qual1, 1L)) 3041 .add((Mutation) new Append(row).addColumn(fam1, qual2, Bytes.toBytes("a")) 3042 .setReturnResults(false))); 3043 3044 result = region.checkAndMutate(checkAndMutate); 3045 assertTrue(result.isSuccess()); 3046 assertEquals(3L, Bytes.toLong(result.getResult().getValue(fam1, qual1))); 3047 assertNull(result.getResult().getValue(fam1, qual2)); 3048 3049 r = region.get(new Get(row)); 3050 assertEquals(3L, Bytes.toLong(r.getValue(fam1, qual1))); 3051 assertEquals("aaa", Bytes.toString(r.getValue(fam1, qual2))); 3052 } 3053 3054 @Test 3055 public void testCheckAndRowMutations() throws Throwable { 3056 final byte[] row = Bytes.toBytes("row"); 3057 final byte[] q1 = Bytes.toBytes("q1"); 3058 final byte[] q2 = Bytes.toBytes("q2"); 3059 final byte[] q3 = Bytes.toBytes("q3"); 3060 final byte[] q4 = Bytes.toBytes("q4"); 3061 final String v1 = "v1"; 3062 3063 region = initHRegion(tableName, method, CONF, fam1); 3064 3065 // Initial values 3066 region 3067 .batchMutate(new Mutation[] { new Put(row).addColumn(fam1, q2, Bytes.toBytes("toBeDeleted")), 3068 new Put(row).addColumn(fam1, q3, Bytes.toBytes(5L)), 3069 new Put(row).addColumn(fam1, q4, Bytes.toBytes("a")), }); 3070 3071 // Do CheckAndRowMutations 3072 CheckAndMutate checkAndMutate = CheckAndMutate.newBuilder(row).ifNotExists(fam1, q1).build( 3073 new RowMutations(row).add(Arrays.asList(new Put(row).addColumn(fam1, q1, Bytes.toBytes(v1)), 3074 new Delete(row).addColumns(fam1, q2), new Increment(row).addColumn(fam1, q3, 1), 3075 new Append(row).addColumn(fam1, q4, Bytes.toBytes("b"))))); 3076 3077 CheckAndMutateResult result = region.checkAndMutate(checkAndMutate); 3078 assertTrue(result.isSuccess()); 3079 assertEquals(6L, Bytes.toLong(result.getResult().getValue(fam1, q3))); 3080 assertEquals("ab", Bytes.toString(result.getResult().getValue(fam1, q4))); 3081 3082 // Verify the value 3083 Result r = region.get(new Get(row)); 3084 assertEquals(v1, Bytes.toString(r.getValue(fam1, q1))); 3085 assertNull(r.getValue(fam1, q2)); 3086 assertEquals(6L, Bytes.toLong(r.getValue(fam1, q3))); 3087 assertEquals("ab", Bytes.toString(r.getValue(fam1, q4))); 3088 3089 // Do CheckAndRowMutations again 3090 checkAndMutate = CheckAndMutate.newBuilder(row).ifNotExists(fam1, q1) 3091 .build(new RowMutations(row).add(Arrays.asList(new Delete(row).addColumns(fam1, q1), 3092 new Put(row).addColumn(fam1, q2, Bytes.toBytes(v1)), 3093 new Increment(row).addColumn(fam1, q3, 1), 3094 new Append(row).addColumn(fam1, q4, Bytes.toBytes("b"))))); 3095 3096 result = region.checkAndMutate(checkAndMutate); 3097 assertFalse(result.isSuccess()); 3098 assertNull(result.getResult()); 3099 3100 // Verify the value 3101 r = region.get(new Get(row)); 3102 assertEquals(v1, Bytes.toString(r.getValue(fam1, q1))); 3103 assertNull(r.getValue(fam1, q2)); 3104 assertEquals(6L, Bytes.toLong(r.getValue(fam1, q3))); 3105 assertEquals("ab", Bytes.toString(r.getValue(fam1, q4))); 3106 } 3107 3108 // //////////////////////////////////////////////////////////////////////////// 3109 // Delete tests 3110 // //////////////////////////////////////////////////////////////////////////// 3111 @Test 3112 public void testDelete_multiDeleteColumn() throws IOException { 3113 byte[] row1 = Bytes.toBytes("row1"); 3114 byte[] fam1 = Bytes.toBytes("fam1"); 3115 byte[] qual = Bytes.toBytes("qualifier"); 3116 byte[] value = Bytes.toBytes("value"); 3117 3118 Put put = new Put(row1); 3119 put.addColumn(fam1, qual, 1, value); 3120 put.addColumn(fam1, qual, 2, value); 3121 3122 this.region = initHRegion(tableName, method, CONF, fam1); 3123 region.put(put); 3124 3125 // We do support deleting more than 1 'latest' version 3126 Delete delete = new Delete(row1); 3127 delete.addColumn(fam1, qual); 3128 delete.addColumn(fam1, qual); 3129 region.delete(delete); 3130 3131 Get get = new Get(row1); 3132 get.addFamily(fam1); 3133 Result r = region.get(get); 3134 assertEquals(0, r.size()); 3135 } 3136 3137 @Test 3138 public void testDelete_CheckFamily() throws IOException { 3139 byte[] row1 = Bytes.toBytes("row1"); 3140 byte[] fam1 = Bytes.toBytes("fam1"); 3141 byte[] fam2 = Bytes.toBytes("fam2"); 3142 byte[] fam3 = Bytes.toBytes("fam3"); 3143 byte[] fam4 = Bytes.toBytes("fam4"); 3144 3145 // Setting up region 3146 this.region = initHRegion(tableName, method, CONF, fam1, fam2, fam3); 3147 List<Cell> kvs = new ArrayList<>(); 3148 kvs.add(new KeyValue(row1, fam4, null, null)); 3149 3150 byte[] forUnitTestsOnly = Bytes.toBytes("ForUnitTestsOnly"); 3151 3152 // testing existing family 3153 NavigableMap<byte[], List<Cell>> deleteMap = new TreeMap<>(Bytes.BYTES_COMPARATOR); 3154 deleteMap.put(fam2, kvs); 3155 region.delete(new Delete(forUnitTestsOnly, HConstants.LATEST_TIMESTAMP, deleteMap)); 3156 3157 // testing non existing family 3158 NavigableMap<byte[], List<Cell>> deleteMap2 = new TreeMap<>(Bytes.BYTES_COMPARATOR); 3159 deleteMap2.put(fam4, kvs); 3160 assertThrows("Family " + Bytes.toString(fam4) + " does exist", 3161 NoSuchColumnFamilyException.class, 3162 () -> region.delete(new Delete(forUnitTestsOnly, HConstants.LATEST_TIMESTAMP, deleteMap2))); 3163 } 3164 3165 @Test 3166 public void testDelete_mixed() throws IOException, InterruptedException { 3167 byte[] fam = Bytes.toBytes("info"); 3168 byte[][] families = { fam }; 3169 this.region = initHRegion(tableName, method, CONF, families); 3170 EnvironmentEdgeManagerTestHelper.injectEdge(new IncrementingEnvironmentEdge()); 3171 3172 byte[] row = Bytes.toBytes("table_name"); 3173 // column names 3174 byte[] serverinfo = Bytes.toBytes("serverinfo"); 3175 byte[] splitA = Bytes.toBytes("splitA"); 3176 byte[] splitB = Bytes.toBytes("splitB"); 3177 3178 // add some data: 3179 Put put = new Put(row); 3180 put.addColumn(fam, splitA, Bytes.toBytes("reference_A")); 3181 region.put(put); 3182 3183 put = new Put(row); 3184 put.addColumn(fam, splitB, Bytes.toBytes("reference_B")); 3185 region.put(put); 3186 3187 put = new Put(row); 3188 put.addColumn(fam, serverinfo, Bytes.toBytes("ip_address")); 3189 region.put(put); 3190 3191 // ok now delete a split: 3192 Delete delete = new Delete(row); 3193 delete.addColumns(fam, splitA); 3194 region.delete(delete); 3195 3196 // assert some things: 3197 Get get = new Get(row).addColumn(fam, serverinfo); 3198 Result result = region.get(get); 3199 assertEquals(1, result.size()); 3200 3201 get = new Get(row).addColumn(fam, splitA); 3202 result = region.get(get); 3203 assertEquals(0, result.size()); 3204 3205 get = new Get(row).addColumn(fam, splitB); 3206 result = region.get(get); 3207 assertEquals(1, result.size()); 3208 3209 // Assert that after a delete, I can put. 3210 put = new Put(row); 3211 put.addColumn(fam, splitA, Bytes.toBytes("reference_A")); 3212 region.put(put); 3213 get = new Get(row); 3214 result = region.get(get); 3215 assertEquals(3, result.size()); 3216 3217 // Now delete all... then test I can add stuff back 3218 delete = new Delete(row); 3219 region.delete(delete); 3220 assertEquals(0, region.get(get).size()); 3221 3222 region.put(new Put(row).addColumn(fam, splitA, Bytes.toBytes("reference_A"))); 3223 result = region.get(get); 3224 assertEquals(1, result.size()); 3225 } 3226 3227 @Test 3228 public void testDeleteRowWithFutureTs() throws IOException { 3229 byte[] fam = Bytes.toBytes("info"); 3230 byte[][] families = { fam }; 3231 this.region = initHRegion(tableName, method, CONF, families); 3232 byte[] row = Bytes.toBytes("table_name"); 3233 // column names 3234 byte[] serverinfo = Bytes.toBytes("serverinfo"); 3235 3236 // add data in the far future 3237 Put put = new Put(row); 3238 put.addColumn(fam, serverinfo, HConstants.LATEST_TIMESTAMP - 5, Bytes.toBytes("value")); 3239 region.put(put); 3240 3241 // now delete something in the present 3242 Delete delete = new Delete(row); 3243 region.delete(delete); 3244 3245 // make sure we still see our data 3246 Get get = new Get(row).addColumn(fam, serverinfo); 3247 Result result = region.get(get); 3248 assertEquals(1, result.size()); 3249 3250 // delete the future row 3251 delete = new Delete(row, HConstants.LATEST_TIMESTAMP - 3); 3252 region.delete(delete); 3253 3254 // make sure it is gone 3255 get = new Get(row).addColumn(fam, serverinfo); 3256 result = region.get(get); 3257 assertEquals(0, result.size()); 3258 } 3259 3260 /** 3261 * Tests that the special LATEST_TIMESTAMP option for puts gets replaced by the actual timestamp 3262 */ 3263 @Test 3264 public void testPutWithLatestTS() throws IOException { 3265 byte[] fam = Bytes.toBytes("info"); 3266 byte[][] families = { fam }; 3267 this.region = initHRegion(tableName, method, CONF, families); 3268 byte[] row = Bytes.toBytes("row1"); 3269 // column names 3270 byte[] qual = Bytes.toBytes("qual"); 3271 3272 // add data with LATEST_TIMESTAMP, put without WAL 3273 Put put = new Put(row); 3274 put.addColumn(fam, qual, HConstants.LATEST_TIMESTAMP, Bytes.toBytes("value")); 3275 region.put(put); 3276 3277 // Make sure it shows up with an actual timestamp 3278 Get get = new Get(row).addColumn(fam, qual); 3279 Result result = region.get(get); 3280 assertEquals(1, result.size()); 3281 Cell kv = result.rawCells()[0]; 3282 LOG.info("Got: " + kv); 3283 assertTrue("LATEST_TIMESTAMP was not replaced with real timestamp", 3284 kv.getTimestamp() != HConstants.LATEST_TIMESTAMP); 3285 3286 // Check same with WAL enabled (historically these took different 3287 // code paths, so check both) 3288 row = Bytes.toBytes("row2"); 3289 put = new Put(row); 3290 put.addColumn(fam, qual, HConstants.LATEST_TIMESTAMP, Bytes.toBytes("value")); 3291 region.put(put); 3292 3293 // Make sure it shows up with an actual timestamp 3294 get = new Get(row).addColumn(fam, qual); 3295 result = region.get(get); 3296 assertEquals(1, result.size()); 3297 kv = result.rawCells()[0]; 3298 LOG.info("Got: " + kv); 3299 assertTrue("LATEST_TIMESTAMP was not replaced with real timestamp", 3300 kv.getTimestamp() != HConstants.LATEST_TIMESTAMP); 3301 } 3302 3303 /** 3304 * Tests that there is server-side filtering for invalid timestamp upper bound. Note that the 3305 * timestamp lower bound is automatically handled for us by the TTL field. 3306 */ 3307 @Test 3308 public void testPutWithTsSlop() throws IOException { 3309 byte[] fam = Bytes.toBytes("info"); 3310 byte[][] families = { fam }; 3311 3312 // add data with a timestamp that is too recent for range. Ensure assert 3313 CONF.setInt("hbase.hregion.keyvalue.timestamp.slop.millisecs", 1000); 3314 this.region = initHRegion(tableName, method, CONF, families); 3315 boolean caughtExcep = false; 3316 try { 3317 // no TS specified == use latest. should not error 3318 region.put(new Put(row).addColumn(fam, Bytes.toBytes("qual"), Bytes.toBytes("value"))); 3319 // TS out of range. should error 3320 region.put(new Put(row).addColumn(fam, Bytes.toBytes("qual"), 3321 EnvironmentEdgeManager.currentTime() + 2000, Bytes.toBytes("value"))); 3322 fail("Expected IOE for TS out of configured timerange"); 3323 } catch (FailedSanityCheckException ioe) { 3324 LOG.debug("Received expected exception", ioe); 3325 caughtExcep = true; 3326 } 3327 assertTrue("Should catch FailedSanityCheckException", caughtExcep); 3328 } 3329 3330 @Test 3331 public void testScanner_DeleteOneFamilyNotAnother() throws IOException { 3332 byte[] fam1 = Bytes.toBytes("columnA"); 3333 byte[] fam2 = Bytes.toBytes("columnB"); 3334 this.region = initHRegion(tableName, method, CONF, fam1, fam2); 3335 byte[] rowA = Bytes.toBytes("rowA"); 3336 byte[] rowB = Bytes.toBytes("rowB"); 3337 3338 byte[] value = Bytes.toBytes("value"); 3339 3340 Delete delete = new Delete(rowA); 3341 delete.addFamily(fam1); 3342 3343 region.delete(delete); 3344 3345 // now create data. 3346 Put put = new Put(rowA); 3347 put.addColumn(fam2, null, value); 3348 region.put(put); 3349 3350 put = new Put(rowB); 3351 put.addColumn(fam1, null, value); 3352 put.addColumn(fam2, null, value); 3353 region.put(put); 3354 3355 Scan scan = new Scan(); 3356 scan.addFamily(fam1).addFamily(fam2); 3357 try (InternalScanner s = region.getScanner(scan)) { 3358 List<Cell> results = new ArrayList<>(); 3359 s.next(results); 3360 assertTrue(CellUtil.matchingRows(results.get(0), rowA)); 3361 3362 results.clear(); 3363 s.next(results); 3364 assertTrue(CellUtil.matchingRows(results.get(0), rowB)); 3365 } 3366 } 3367 3368 @Test 3369 public void testDataInMemoryWithoutWAL() throws IOException { 3370 FileSystem fs = FileSystem.get(CONF); 3371 Path rootDir = new Path(dir + method); 3372 fs.mkdirs(new Path(rootDir, method)); 3373 FSHLog hLog = new FSHLog(fs, rootDir, method, CONF); 3374 hLog.init(); 3375 // This chunk creation is done throughout the code base. Do we want to move it into core? 3376 // It is missing from this test. W/o it we NPE. 3377 region = initHRegion(tableName, null, null, CONF, false, Durability.SYNC_WAL, hLog, 3378 COLUMN_FAMILY_BYTES); 3379 3380 Cell originalCell = ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row) 3381 .setFamily(COLUMN_FAMILY_BYTES).setQualifier(qual1) 3382 .setTimestamp(EnvironmentEdgeManager.currentTime()).setType(KeyValue.Type.Put.getCode()) 3383 .setValue(value1).build(); 3384 final long originalSize = originalCell.getSerializedSize(); 3385 3386 Cell addCell = ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row) 3387 .setFamily(COLUMN_FAMILY_BYTES).setQualifier(qual1) 3388 .setTimestamp(EnvironmentEdgeManager.currentTime()).setType(KeyValue.Type.Put.getCode()) 3389 .setValue(Bytes.toBytes("xxxxxxxxxx")).build(); 3390 final long addSize = addCell.getSerializedSize(); 3391 3392 LOG.info("originalSize:" + originalSize + ", addSize:" + addSize); 3393 // start test. We expect that the addPut's durability will be replaced 3394 // by originalPut's durability. 3395 3396 // case 1: 3397 testDataInMemoryWithoutWAL(region, 3398 new Put(row).add(originalCell).setDurability(Durability.SKIP_WAL), 3399 new Put(row).add(addCell).setDurability(Durability.SKIP_WAL), originalSize + addSize); 3400 3401 // case 2: 3402 testDataInMemoryWithoutWAL(region, 3403 new Put(row).add(originalCell).setDurability(Durability.SKIP_WAL), 3404 new Put(row).add(addCell).setDurability(Durability.SYNC_WAL), originalSize + addSize); 3405 3406 // case 3: 3407 testDataInMemoryWithoutWAL(region, 3408 new Put(row).add(originalCell).setDurability(Durability.SYNC_WAL), 3409 new Put(row).add(addCell).setDurability(Durability.SKIP_WAL), 0); 3410 3411 // case 4: 3412 testDataInMemoryWithoutWAL(region, 3413 new Put(row).add(originalCell).setDurability(Durability.SYNC_WAL), 3414 new Put(row).add(addCell).setDurability(Durability.SYNC_WAL), 0); 3415 } 3416 3417 private static void testDataInMemoryWithoutWAL(HRegion region, Put originalPut, final Put addPut, 3418 long delta) throws IOException { 3419 final long initSize = region.getDataInMemoryWithoutWAL(); 3420 // save normalCPHost and replaced by mockedCPHost 3421 RegionCoprocessorHost normalCPHost = region.getCoprocessorHost(); 3422 RegionCoprocessorHost mockedCPHost = mock(RegionCoprocessorHost.class); 3423 // Because the preBatchMutate returns void, we can't do usual Mockito when...then form. Must 3424 // do below format (from Mockito doc). 3425 doAnswer(new Answer<Void>() { 3426 @Override 3427 public Void answer(InvocationOnMock invocation) throws Throwable { 3428 MiniBatchOperationInProgress<Mutation> mb = invocation.getArgument(0); 3429 mb.addOperationsFromCP(0, new Mutation[] { addPut }); 3430 return null; 3431 } 3432 }).when(mockedCPHost).preBatchMutate(isA(MiniBatchOperationInProgress.class)); 3433 ColumnFamilyDescriptorBuilder builder = 3434 ColumnFamilyDescriptorBuilder.newBuilder(COLUMN_FAMILY_BYTES); 3435 ScanInfo info = new ScanInfo(CONF, builder.build(), Long.MAX_VALUE, Long.MAX_VALUE, 3436 region.getCellComparator()); 3437 when(mockedCPHost.preFlushScannerOpen(any(HStore.class), any())).thenReturn(info); 3438 3439 when(mockedCPHost.preFlush(any(), any(StoreScanner.class), any())) 3440 .thenAnswer(i -> i.getArgument(1)); 3441 region.setCoprocessorHost(mockedCPHost); 3442 3443 region.put(originalPut); 3444 region.setCoprocessorHost(normalCPHost); 3445 final long finalSize = region.getDataInMemoryWithoutWAL(); 3446 assertEquals("finalSize:" + finalSize + ", initSize:" + initSize + ", delta:" + delta, 3447 finalSize, initSize + delta); 3448 } 3449 3450 @Test 3451 public void testDeleteColumns_PostInsert() throws IOException, InterruptedException { 3452 Delete delete = new Delete(row); 3453 delete.addColumns(fam1, qual1); 3454 doTestDelete_AndPostInsert(delete); 3455 } 3456 3457 @Test 3458 public void testaddFamily_PostInsert() throws IOException, InterruptedException { 3459 Delete delete = new Delete(row); 3460 delete.addFamily(fam1); 3461 doTestDelete_AndPostInsert(delete); 3462 } 3463 3464 public void doTestDelete_AndPostInsert(Delete delete) throws IOException, InterruptedException { 3465 this.region = initHRegion(tableName, method, CONF, fam1); 3466 EnvironmentEdgeManagerTestHelper.injectEdge(new IncrementingEnvironmentEdge()); 3467 Put put = new Put(row); 3468 put.addColumn(fam1, qual1, value1); 3469 region.put(put); 3470 3471 // now delete the value: 3472 region.delete(delete); 3473 3474 // ok put data: 3475 put = new Put(row); 3476 put.addColumn(fam1, qual1, value2); 3477 region.put(put); 3478 3479 // ok get: 3480 Get get = new Get(row); 3481 get.addColumn(fam1, qual1); 3482 3483 Result r = region.get(get); 3484 assertEquals(1, r.size()); 3485 assertArrayEquals(value2, r.getValue(fam1, qual1)); 3486 3487 // next: 3488 Scan scan = new Scan().withStartRow(row); 3489 scan.addColumn(fam1, qual1); 3490 try (InternalScanner s = region.getScanner(scan)) { 3491 List<Cell> results = new ArrayList<>(); 3492 assertEquals(false, s.next(results)); 3493 assertEquals(1, results.size()); 3494 Cell kv = results.get(0); 3495 3496 assertArrayEquals(value2, CellUtil.cloneValue(kv)); 3497 assertArrayEquals(fam1, CellUtil.cloneFamily(kv)); 3498 assertArrayEquals(qual1, CellUtil.cloneQualifier(kv)); 3499 assertArrayEquals(row, CellUtil.cloneRow(kv)); 3500 } 3501 } 3502 3503 @Test 3504 public void testDelete_CheckTimestampUpdated() throws IOException { 3505 byte[] row1 = Bytes.toBytes("row1"); 3506 byte[] col1 = Bytes.toBytes("col1"); 3507 byte[] col2 = Bytes.toBytes("col2"); 3508 byte[] col3 = Bytes.toBytes("col3"); 3509 3510 byte[] forUnitTestsOnly = Bytes.toBytes("ForUnitTestsOnly"); 3511 3512 // Setting up region 3513 this.region = initHRegion(tableName, method, CONF, fam1); 3514 // Building checkerList 3515 List<Cell> kvs = new ArrayList<>(); 3516 kvs.add(new KeyValue(row1, fam1, col1, null)); 3517 kvs.add(new KeyValue(row1, fam1, col2, null)); 3518 kvs.add(new KeyValue(row1, fam1, col3, null)); 3519 3520 NavigableMap<byte[], List<Cell>> deleteMap = new TreeMap<>(Bytes.BYTES_COMPARATOR); 3521 deleteMap.put(fam1, kvs); 3522 region.delete(new Delete(forUnitTestsOnly, HConstants.LATEST_TIMESTAMP, deleteMap)); 3523 3524 // extract the key values out the memstore: 3525 // This is kinda hacky, but better than nothing... 3526 long now = EnvironmentEdgeManager.currentTime(); 3527 AbstractMemStore memstore = (AbstractMemStore) region.getStore(fam1).memstore; 3528 Cell firstCell = memstore.getActive().first(); 3529 assertTrue(firstCell.getTimestamp() <= now); 3530 now = firstCell.getTimestamp(); 3531 for (Cell cell : memstore.getActive().getCellSet()) { 3532 assertTrue(cell.getTimestamp() <= now); 3533 now = cell.getTimestamp(); 3534 } 3535 } 3536 3537 // //////////////////////////////////////////////////////////////////////////// 3538 // Get tests 3539 // //////////////////////////////////////////////////////////////////////////// 3540 @Test 3541 public void testGet_FamilyChecker() throws IOException { 3542 byte[] row1 = Bytes.toBytes("row1"); 3543 byte[] fam1 = Bytes.toBytes("fam1"); 3544 byte[] fam2 = Bytes.toBytes("False"); 3545 byte[] col1 = Bytes.toBytes("col1"); 3546 3547 // Setting up region 3548 this.region = initHRegion(tableName, method, CONF, fam1); 3549 Get get = new Get(row1); 3550 get.addColumn(fam2, col1); 3551 3552 // Test 3553 try { 3554 region.get(get); 3555 fail("Expecting DoNotRetryIOException in get but did not get any"); 3556 } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) { 3557 LOG.info("Got expected DoNotRetryIOException successfully"); 3558 } 3559 } 3560 3561 @Test 3562 public void testGet_Basic() throws IOException { 3563 byte[] row1 = Bytes.toBytes("row1"); 3564 byte[] fam1 = Bytes.toBytes("fam1"); 3565 byte[] col1 = Bytes.toBytes("col1"); 3566 byte[] col2 = Bytes.toBytes("col2"); 3567 byte[] col3 = Bytes.toBytes("col3"); 3568 byte[] col4 = Bytes.toBytes("col4"); 3569 byte[] col5 = Bytes.toBytes("col5"); 3570 3571 // Setting up region 3572 this.region = initHRegion(tableName, method, CONF, fam1); 3573 // Add to memstore 3574 Put put = new Put(row1); 3575 put.addColumn(fam1, col1, null); 3576 put.addColumn(fam1, col2, null); 3577 put.addColumn(fam1, col3, null); 3578 put.addColumn(fam1, col4, null); 3579 put.addColumn(fam1, col5, null); 3580 region.put(put); 3581 3582 Get get = new Get(row1); 3583 get.addColumn(fam1, col2); 3584 get.addColumn(fam1, col4); 3585 // Expected result 3586 KeyValue kv1 = new KeyValue(row1, fam1, col2); 3587 KeyValue kv2 = new KeyValue(row1, fam1, col4); 3588 KeyValue[] expected = { kv1, kv2 }; 3589 3590 // Test 3591 Result res = region.get(get); 3592 assertEquals(expected.length, res.size()); 3593 for (int i = 0; i < res.size(); i++) { 3594 assertTrue(CellUtil.matchingRows(expected[i], res.rawCells()[i])); 3595 assertTrue(CellUtil.matchingFamily(expected[i], res.rawCells()[i])); 3596 assertTrue(CellUtil.matchingQualifier(expected[i], res.rawCells()[i])); 3597 } 3598 3599 // Test using a filter on a Get 3600 Get g = new Get(row1); 3601 final int count = 2; 3602 g.setFilter(new ColumnCountGetFilter(count)); 3603 res = region.get(g); 3604 assertEquals(count, res.size()); 3605 } 3606 3607 @Test 3608 public void testGet_Empty() throws IOException { 3609 byte[] row = Bytes.toBytes("row"); 3610 byte[] fam = Bytes.toBytes("fam"); 3611 3612 this.region = initHRegion(tableName, method, CONF, fam); 3613 Get get = new Get(row); 3614 get.addFamily(fam); 3615 Result r = region.get(get); 3616 3617 assertTrue(r.isEmpty()); 3618 } 3619 3620 @Test 3621 public void testGetWithFilter() throws IOException, InterruptedException { 3622 byte[] row1 = Bytes.toBytes("row1"); 3623 byte[] fam1 = Bytes.toBytes("fam1"); 3624 byte[] col1 = Bytes.toBytes("col1"); 3625 byte[] value1 = Bytes.toBytes("value1"); 3626 byte[] value2 = Bytes.toBytes("value2"); 3627 3628 final int maxVersions = 3; 3629 TableDescriptor tableDescriptor = 3630 TableDescriptorBuilder.newBuilder(TableName.valueOf("testFilterAndColumnTracker")) 3631 .setColumnFamily( 3632 ColumnFamilyDescriptorBuilder.newBuilder(fam1).setMaxVersions(maxVersions).build()) 3633 .build(); 3634 ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null, 3635 MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT); 3636 RegionInfo info = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build(); 3637 Path logDir = TEST_UTIL.getDataTestDirOnTestFS(method + ".log"); 3638 final WAL wal = HBaseTestingUtil.createWal(TEST_UTIL.getConfiguration(), logDir, info); 3639 this.region = TEST_UTIL.createLocalHRegion(info, CONF, tableDescriptor, wal); 3640 3641 // Put 4 version to memstore 3642 long ts = 0; 3643 Put put = new Put(row1, ts); 3644 put.addColumn(fam1, col1, value1); 3645 region.put(put); 3646 put = new Put(row1, ts + 1); 3647 put.addColumn(fam1, col1, Bytes.toBytes("filter1")); 3648 region.put(put); 3649 put = new Put(row1, ts + 2); 3650 put.addColumn(fam1, col1, Bytes.toBytes("filter2")); 3651 region.put(put); 3652 put = new Put(row1, ts + 3); 3653 put.addColumn(fam1, col1, value2); 3654 region.put(put); 3655 3656 Get get = new Get(row1); 3657 get.readAllVersions(); 3658 Result res = region.get(get); 3659 // Get 3 versions, the oldest version has gone from user view 3660 assertEquals(maxVersions, res.size()); 3661 3662 get.setFilter(new ValueFilter(CompareOperator.EQUAL, new SubstringComparator("value"))); 3663 res = region.get(get); 3664 // When use value filter, the oldest version should still gone from user view and it 3665 // should only return one key vaule 3666 assertEquals(1, res.size()); 3667 assertTrue(CellUtil.matchingValue(new KeyValue(row1, fam1, col1, value2), res.rawCells()[0])); 3668 assertEquals(ts + 3, res.rawCells()[0].getTimestamp()); 3669 3670 region.flush(true); 3671 region.compact(true); 3672 Thread.sleep(1000); 3673 res = region.get(get); 3674 // After flush and compact, the result should be consistent with previous result 3675 assertEquals(1, res.size()); 3676 assertTrue(CellUtil.matchingValue(new KeyValue(row1, fam1, col1, value2), res.rawCells()[0])); 3677 } 3678 3679 // //////////////////////////////////////////////////////////////////////////// 3680 // Scanner tests 3681 // //////////////////////////////////////////////////////////////////////////// 3682 @Test 3683 public void testGetScanner_WithOkFamilies() throws IOException { 3684 byte[] fam1 = Bytes.toBytes("fam1"); 3685 byte[] fam2 = Bytes.toBytes("fam2"); 3686 3687 byte[][] families = { fam1, fam2 }; 3688 3689 // Setting up region 3690 this.region = initHRegion(tableName, method, CONF, families); 3691 Scan scan = new Scan(); 3692 scan.addFamily(fam1); 3693 scan.addFamily(fam2); 3694 region.getScanner(scan).close(); 3695 } 3696 3697 @Test 3698 public void testGetScanner_WithNotOkFamilies() throws IOException { 3699 byte[] fam1 = Bytes.toBytes("fam1"); 3700 byte[] fam2 = Bytes.toBytes("fam2"); 3701 3702 byte[][] families = { fam1 }; 3703 3704 // Setting up region 3705 this.region = initHRegion(tableName, method, CONF, families); 3706 Scan scan = new Scan(); 3707 scan.addFamily(fam2); 3708 assertThrows(NoSuchColumnFamilyException.class, () -> region.getScanner(scan)); 3709 } 3710 3711 @Test 3712 public void testGetScanner_WithNoFamilies() throws IOException { 3713 byte[] row1 = Bytes.toBytes("row1"); 3714 byte[] fam1 = Bytes.toBytes("fam1"); 3715 byte[] fam2 = Bytes.toBytes("fam2"); 3716 byte[] fam3 = Bytes.toBytes("fam3"); 3717 byte[] fam4 = Bytes.toBytes("fam4"); 3718 3719 byte[][] families = { fam1, fam2, fam3, fam4 }; 3720 3721 // Setting up region 3722 this.region = initHRegion(tableName, method, CONF, families); 3723 // Putting data in Region 3724 Put put = new Put(row1); 3725 put.addColumn(fam1, null, null); 3726 put.addColumn(fam2, null, null); 3727 put.addColumn(fam3, null, null); 3728 put.addColumn(fam4, null, null); 3729 region.put(put); 3730 3731 Scan scan = null; 3732 3733 // Testing to see how many scanners that is produced by getScanner, starting with known number, 3734 // 2 - current = 1 3735 scan = new Scan(); 3736 scan.addFamily(fam2); 3737 scan.addFamily(fam4); 3738 try (RegionScannerImpl is = region.getScanner(scan)) { 3739 assertEquals(1, is.storeHeap.getHeap().size()); 3740 } 3741 3742 scan = new Scan(); 3743 try (RegionScannerImpl is = region.getScanner(scan)) { 3744 assertEquals(families.length - 1, is.storeHeap.getHeap().size()); 3745 } 3746 } 3747 3748 /** 3749 * This method tests https://issues.apache.org/jira/browse/HBASE-2516. 3750 */ 3751 @Test 3752 public void testGetScanner_WithRegionClosed() throws IOException { 3753 byte[] fam1 = Bytes.toBytes("fam1"); 3754 byte[] fam2 = Bytes.toBytes("fam2"); 3755 3756 byte[][] families = { fam1, fam2 }; 3757 3758 // Setting up region 3759 region = initHRegion(tableName, method, CONF, families); 3760 region.closed.set(true); 3761 try { 3762 assertThrows(NotServingRegionException.class, () -> region.getScanner(null)); 3763 } finally { 3764 // so we can close the region in tearDown 3765 region.closed.set(false); 3766 } 3767 } 3768 3769 @Test 3770 public void testRegionScanner_getFilesRead() throws IOException { 3771 // Setup: init region with one family; put two rows and flush to create store files. 3772 byte[] family = Bytes.toBytes("fam1"); 3773 byte[][] families = { family }; 3774 this.region = initHRegion(tableName, method, CONF, families); 3775 Put put = new Put(Bytes.toBytes("row1")); 3776 put.addColumn(family, Bytes.toBytes("q1"), Bytes.toBytes("v1")); 3777 region.put(put); 3778 put = new Put(Bytes.toBytes("row2")); 3779 put.addColumn(family, Bytes.toBytes("q1"), Bytes.toBytes("v1")); 3780 region.put(put); 3781 region.flush(true); 3782 3783 // Collect expected store file paths from all stores (before opening the scanner). 3784 Set<Path> expectedFilePaths = new HashSet<>(); 3785 FileSystem fs = region.getFilesystem(); 3786 for (HStore store : region.getStores()) { 3787 for (HStoreFile storeFile : store.getStorefiles()) { 3788 expectedFilePaths.add(fs.makeQualified(storeFile.getPath())); 3789 } 3790 } 3791 assertTrue("Should have at least one store file after flush", expectedFilePaths.size() >= 1); 3792 3793 // Get region scanner; before close getFilesRead must be empty. 3794 RegionScannerImpl scanner = region.getScanner(new Scan()); 3795 3796 Set<Path> filesReadBeforeClose = scanner.getFilesRead(); 3797 assertTrue("Should return empty set before closing", filesReadBeforeClose.isEmpty()); 3798 3799 // Drain scanner (next up to two rows) to exercise store heap reads. 3800 List<Cell> cells = new ArrayList<>(); 3801 int count = 0; 3802 while (count < 2) { 3803 if (!scanner.next(cells)) { 3804 break; 3805 } 3806 cells.clear(); 3807 count++; 3808 } 3809 3810 // Still before close: set must remain empty until scanner is closed. 3811 filesReadBeforeClose = scanner.getFilesRead(); 3812 assertTrue("Should return empty set before closing even after scanning", 3813 filesReadBeforeClose.isEmpty()); 3814 scanner.close(); 3815 3816 // After close: set must contain exactly the expected store file paths. 3817 Set<Path> filesReadAfterClose = scanner.getFilesRead(); 3818 assertEquals("Should have exact file count after closing", expectedFilePaths.size(), 3819 filesReadAfterClose.size()); 3820 assertEquals("Should contain all expected file paths", expectedFilePaths, filesReadAfterClose); 3821 } 3822 3823 @Test 3824 public void testRegionScanner_Next() throws IOException { 3825 byte[] row1 = Bytes.toBytes("row1"); 3826 byte[] row2 = Bytes.toBytes("row2"); 3827 byte[] fam1 = Bytes.toBytes("fam1"); 3828 byte[] fam2 = Bytes.toBytes("fam2"); 3829 byte[] fam3 = Bytes.toBytes("fam3"); 3830 byte[] fam4 = Bytes.toBytes("fam4"); 3831 3832 byte[][] families = { fam1, fam2, fam3, fam4 }; 3833 long ts = EnvironmentEdgeManager.currentTime(); 3834 3835 // Setting up region 3836 this.region = initHRegion(tableName, method, CONF, families); 3837 // Putting data in Region 3838 Put put = null; 3839 put = new Put(row1); 3840 put.addColumn(fam1, (byte[]) null, ts, null); 3841 put.addColumn(fam2, (byte[]) null, ts, null); 3842 put.addColumn(fam3, (byte[]) null, ts, null); 3843 put.addColumn(fam4, (byte[]) null, ts, null); 3844 region.put(put); 3845 3846 put = new Put(row2); 3847 put.addColumn(fam1, (byte[]) null, ts, null); 3848 put.addColumn(fam2, (byte[]) null, ts, null); 3849 put.addColumn(fam3, (byte[]) null, ts, null); 3850 put.addColumn(fam4, (byte[]) null, ts, null); 3851 region.put(put); 3852 3853 Scan scan = new Scan(); 3854 scan.addFamily(fam2); 3855 scan.addFamily(fam4); 3856 try (InternalScanner is = region.getScanner(scan)) { 3857 List<ExtendedCell> res = null; 3858 3859 // Result 1 3860 List<ExtendedCell> expected1 = new ArrayList<>(); 3861 expected1.add(new KeyValue(row1, fam2, null, ts, KeyValue.Type.Put, null)); 3862 expected1.add(new KeyValue(row1, fam4, null, ts, KeyValue.Type.Put, null)); 3863 3864 res = new ArrayList<>(); 3865 is.next(res); 3866 for (int i = 0; i < res.size(); i++) { 3867 assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected1.get(i), res.get(i))); 3868 } 3869 3870 // Result 2 3871 List<ExtendedCell> expected2 = new ArrayList<>(); 3872 expected2.add(new KeyValue(row2, fam2, null, ts, KeyValue.Type.Put, null)); 3873 expected2.add(new KeyValue(row2, fam4, null, ts, KeyValue.Type.Put, null)); 3874 3875 res = new ArrayList<>(); 3876 is.next(res); 3877 for (int i = 0; i < res.size(); i++) { 3878 assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected2.get(i), res.get(i))); 3879 } 3880 } 3881 } 3882 3883 @Test 3884 public void testScanner_ExplicitColumns_FromMemStore_EnforceVersions() throws IOException { 3885 byte[] row1 = Bytes.toBytes("row1"); 3886 byte[] qf1 = Bytes.toBytes("qualifier1"); 3887 byte[] qf2 = Bytes.toBytes("qualifier2"); 3888 byte[] fam1 = Bytes.toBytes("fam1"); 3889 byte[][] families = { fam1 }; 3890 3891 long ts1 = EnvironmentEdgeManager.currentTime(); 3892 long ts2 = ts1 + 1; 3893 long ts3 = ts1 + 2; 3894 3895 // Setting up region 3896 this.region = initHRegion(tableName, method, CONF, families); 3897 // Putting data in Region 3898 Put put = null; 3899 KeyValue kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null); 3900 KeyValue kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null); 3901 KeyValue kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null); 3902 3903 KeyValue kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null); 3904 KeyValue kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null); 3905 KeyValue kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null); 3906 3907 put = new Put(row1); 3908 put.add(kv13); 3909 put.add(kv12); 3910 put.add(kv11); 3911 put.add(kv23); 3912 put.add(kv22); 3913 put.add(kv21); 3914 region.put(put); 3915 3916 // Expected 3917 List<Cell> expected = new ArrayList<>(); 3918 expected.add(kv13); 3919 expected.add(kv12); 3920 3921 Scan scan = new Scan().withStartRow(row1); 3922 scan.addColumn(fam1, qf1); 3923 scan.readVersions(MAX_VERSIONS); 3924 List<Cell> actual = new ArrayList<>(); 3925 try (InternalScanner scanner = region.getScanner(scan)) { 3926 boolean hasNext = scanner.next(actual); 3927 assertEquals(false, hasNext); 3928 3929 // Verify result 3930 for (int i = 0; i < expected.size(); i++) { 3931 assertEquals(expected.get(i), actual.get(i)); 3932 } 3933 } 3934 } 3935 3936 @Test 3937 public void testScanner_ExplicitColumns_FromFilesOnly_EnforceVersions() throws IOException { 3938 byte[] row1 = Bytes.toBytes("row1"); 3939 byte[] qf1 = Bytes.toBytes("qualifier1"); 3940 byte[] qf2 = Bytes.toBytes("qualifier2"); 3941 byte[] fam1 = Bytes.toBytes("fam1"); 3942 byte[][] families = { fam1 }; 3943 3944 long ts1 = 1; 3945 long ts2 = ts1 + 1; 3946 long ts3 = ts1 + 2; 3947 3948 // Setting up region 3949 this.region = initHRegion(tableName, method, CONF, families); 3950 // Putting data in Region 3951 Put put = null; 3952 KeyValue kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null); 3953 KeyValue kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null); 3954 KeyValue kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null); 3955 3956 KeyValue kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null); 3957 KeyValue kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null); 3958 KeyValue kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null); 3959 3960 put = new Put(row1); 3961 put.add(kv13); 3962 put.add(kv12); 3963 put.add(kv11); 3964 put.add(kv23); 3965 put.add(kv22); 3966 put.add(kv21); 3967 region.put(put); 3968 region.flush(true); 3969 3970 // Expected 3971 List<ExtendedCell> expected = new ArrayList<>(); 3972 expected.add(kv13); 3973 expected.add(kv12); 3974 expected.add(kv23); 3975 expected.add(kv22); 3976 3977 Scan scan = new Scan().withStartRow(row1); 3978 scan.addColumn(fam1, qf1); 3979 scan.addColumn(fam1, qf2); 3980 scan.readVersions(MAX_VERSIONS); 3981 List<ExtendedCell> actual = new ArrayList<>(); 3982 try (InternalScanner scanner = region.getScanner(scan)) { 3983 boolean hasNext = scanner.next(actual); 3984 assertEquals(false, hasNext); 3985 3986 // Verify result 3987 for (int i = 0; i < expected.size(); i++) { 3988 assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i))); 3989 } 3990 } 3991 } 3992 3993 @Test 3994 public void testScanner_ExplicitColumns_FromMemStoreAndFiles_EnforceVersions() 3995 throws IOException { 3996 byte[] row1 = Bytes.toBytes("row1"); 3997 byte[] fam1 = Bytes.toBytes("fam1"); 3998 byte[][] families = { fam1 }; 3999 byte[] qf1 = Bytes.toBytes("qualifier1"); 4000 byte[] qf2 = Bytes.toBytes("qualifier2"); 4001 4002 long ts1 = 1; 4003 long ts2 = ts1 + 1; 4004 long ts3 = ts1 + 2; 4005 long ts4 = ts1 + 3; 4006 4007 // Setting up region 4008 this.region = initHRegion(tableName, method, CONF, families); 4009 // Putting data in Region 4010 KeyValue kv14 = new KeyValue(row1, fam1, qf1, ts4, KeyValue.Type.Put, null); 4011 KeyValue kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null); 4012 KeyValue kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null); 4013 KeyValue kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null); 4014 4015 KeyValue kv24 = new KeyValue(row1, fam1, qf2, ts4, KeyValue.Type.Put, null); 4016 KeyValue kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null); 4017 KeyValue kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null); 4018 KeyValue kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null); 4019 4020 Put put = null; 4021 put = new Put(row1); 4022 put.add(kv14); 4023 put.add(kv24); 4024 region.put(put); 4025 region.flush(true); 4026 4027 put = new Put(row1); 4028 put.add(kv23); 4029 put.add(kv13); 4030 region.put(put); 4031 region.flush(true); 4032 4033 put = new Put(row1); 4034 put.add(kv22); 4035 put.add(kv12); 4036 region.put(put); 4037 region.flush(true); 4038 4039 put = new Put(row1); 4040 put.add(kv21); 4041 put.add(kv11); 4042 region.put(put); 4043 4044 // Expected 4045 List<ExtendedCell> expected = new ArrayList<>(); 4046 expected.add(kv14); 4047 expected.add(kv13); 4048 expected.add(kv12); 4049 expected.add(kv24); 4050 expected.add(kv23); 4051 expected.add(kv22); 4052 4053 Scan scan = new Scan().withStartRow(row1); 4054 scan.addColumn(fam1, qf1); 4055 scan.addColumn(fam1, qf2); 4056 int versions = 3; 4057 scan.readVersions(versions); 4058 List<ExtendedCell> actual = new ArrayList<>(); 4059 try (InternalScanner scanner = region.getScanner(scan)) { 4060 boolean hasNext = scanner.next(actual); 4061 assertEquals(false, hasNext); 4062 4063 // Verify result 4064 for (int i = 0; i < expected.size(); i++) { 4065 assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i))); 4066 } 4067 } 4068 } 4069 4070 @Test 4071 public void testScanner_Wildcard_FromMemStore_EnforceVersions() throws IOException { 4072 byte[] row1 = Bytes.toBytes("row1"); 4073 byte[] qf1 = Bytes.toBytes("qualifier1"); 4074 byte[] qf2 = Bytes.toBytes("qualifier2"); 4075 byte[] fam1 = Bytes.toBytes("fam1"); 4076 byte[][] families = { fam1 }; 4077 4078 long ts1 = EnvironmentEdgeManager.currentTime(); 4079 long ts2 = ts1 + 1; 4080 long ts3 = ts1 + 2; 4081 4082 // Setting up region 4083 this.region = initHRegion(tableName, method, CONF, families); 4084 // Putting data in Region 4085 Put put = null; 4086 KeyValue kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null); 4087 KeyValue kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null); 4088 KeyValue kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null); 4089 4090 KeyValue kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null); 4091 KeyValue kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null); 4092 KeyValue kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null); 4093 4094 put = new Put(row1); 4095 put.add(kv13); 4096 put.add(kv12); 4097 put.add(kv11); 4098 put.add(kv23); 4099 put.add(kv22); 4100 put.add(kv21); 4101 region.put(put); 4102 4103 // Expected 4104 List<Cell> expected = new ArrayList<>(); 4105 expected.add(kv13); 4106 expected.add(kv12); 4107 expected.add(kv23); 4108 expected.add(kv22); 4109 4110 Scan scan = new Scan().withStartRow(row1); 4111 scan.addFamily(fam1); 4112 scan.readVersions(MAX_VERSIONS); 4113 List<Cell> actual = new ArrayList<>(); 4114 try (InternalScanner scanner = region.getScanner(scan)) { 4115 boolean hasNext = scanner.next(actual); 4116 assertEquals(false, hasNext); 4117 4118 // Verify result 4119 for (int i = 0; i < expected.size(); i++) { 4120 assertEquals(expected.get(i), actual.get(i)); 4121 } 4122 } 4123 } 4124 4125 @Test 4126 public void testScanner_Wildcard_FromFilesOnly_EnforceVersions() throws IOException { 4127 byte[] row1 = Bytes.toBytes("row1"); 4128 byte[] qf1 = Bytes.toBytes("qualifier1"); 4129 byte[] qf2 = Bytes.toBytes("qualifier2"); 4130 byte[] fam1 = Bytes.toBytes("fam1"); 4131 4132 long ts1 = 1; 4133 long ts2 = ts1 + 1; 4134 long ts3 = ts1 + 2; 4135 4136 // Setting up region 4137 this.region = initHRegion(tableName, method, CONF, fam1); 4138 // Putting data in Region 4139 Put put = null; 4140 KeyValue kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null); 4141 KeyValue kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null); 4142 KeyValue kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null); 4143 4144 KeyValue kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null); 4145 KeyValue kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null); 4146 KeyValue kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null); 4147 4148 put = new Put(row1); 4149 put.add(kv13); 4150 put.add(kv12); 4151 put.add(kv11); 4152 put.add(kv23); 4153 put.add(kv22); 4154 put.add(kv21); 4155 region.put(put); 4156 region.flush(true); 4157 4158 // Expected 4159 List<ExtendedCell> expected = new ArrayList<>(); 4160 expected.add(kv13); 4161 expected.add(kv12); 4162 expected.add(kv23); 4163 expected.add(kv22); 4164 4165 Scan scan = new Scan().withStartRow(row1); 4166 scan.addFamily(fam1); 4167 scan.readVersions(MAX_VERSIONS); 4168 List<ExtendedCell> actual = new ArrayList<>(); 4169 try (InternalScanner scanner = region.getScanner(scan)) { 4170 boolean hasNext = scanner.next(actual); 4171 assertEquals(false, hasNext); 4172 4173 // Verify result 4174 for (int i = 0; i < expected.size(); i++) { 4175 assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i))); 4176 } 4177 } 4178 } 4179 4180 @Test 4181 public void testScanner_StopRow1542() throws IOException { 4182 byte[] family = Bytes.toBytes("testFamily"); 4183 this.region = initHRegion(tableName, method, CONF, family); 4184 byte[] row1 = Bytes.toBytes("row111"); 4185 byte[] row2 = Bytes.toBytes("row222"); 4186 byte[] row3 = Bytes.toBytes("row333"); 4187 byte[] row4 = Bytes.toBytes("row444"); 4188 byte[] row5 = Bytes.toBytes("row555"); 4189 4190 byte[] col1 = Bytes.toBytes("Pub111"); 4191 byte[] col2 = Bytes.toBytes("Pub222"); 4192 4193 Put put = new Put(row1); 4194 put.addColumn(family, col1, Bytes.toBytes(10L)); 4195 region.put(put); 4196 4197 put = new Put(row2); 4198 put.addColumn(family, col1, Bytes.toBytes(15L)); 4199 region.put(put); 4200 4201 put = new Put(row3); 4202 put.addColumn(family, col2, Bytes.toBytes(20L)); 4203 region.put(put); 4204 4205 put = new Put(row4); 4206 put.addColumn(family, col2, Bytes.toBytes(30L)); 4207 region.put(put); 4208 4209 put = new Put(row5); 4210 put.addColumn(family, col1, Bytes.toBytes(40L)); 4211 region.put(put); 4212 4213 Scan scan = new Scan().withStartRow(row3).withStopRow(row4); 4214 scan.readAllVersions(); 4215 scan.addColumn(family, col1); 4216 try (InternalScanner s = region.getScanner(scan)) { 4217 List<Cell> results = new ArrayList<>(); 4218 assertEquals(false, s.next(results)); 4219 assertEquals(0, results.size()); 4220 } 4221 } 4222 4223 @Test 4224 public void testScanner_Wildcard_FromMemStoreAndFiles_EnforceVersions() throws IOException { 4225 byte[] row1 = Bytes.toBytes("row1"); 4226 byte[] fam1 = Bytes.toBytes("fam1"); 4227 byte[] qf1 = Bytes.toBytes("qualifier1"); 4228 byte[] qf2 = Bytes.toBytes("quateslifier2"); 4229 4230 long ts1 = 1; 4231 long ts2 = ts1 + 1; 4232 long ts3 = ts1 + 2; 4233 long ts4 = ts1 + 3; 4234 4235 // Setting up region 4236 this.region = initHRegion(tableName, method, CONF, fam1); 4237 // Putting data in Region 4238 KeyValue kv14 = new KeyValue(row1, fam1, qf1, ts4, KeyValue.Type.Put, null); 4239 KeyValue kv13 = new KeyValue(row1, fam1, qf1, ts3, KeyValue.Type.Put, null); 4240 KeyValue kv12 = new KeyValue(row1, fam1, qf1, ts2, KeyValue.Type.Put, null); 4241 KeyValue kv11 = new KeyValue(row1, fam1, qf1, ts1, KeyValue.Type.Put, null); 4242 4243 KeyValue kv24 = new KeyValue(row1, fam1, qf2, ts4, KeyValue.Type.Put, null); 4244 KeyValue kv23 = new KeyValue(row1, fam1, qf2, ts3, KeyValue.Type.Put, null); 4245 KeyValue kv22 = new KeyValue(row1, fam1, qf2, ts2, KeyValue.Type.Put, null); 4246 KeyValue kv21 = new KeyValue(row1, fam1, qf2, ts1, KeyValue.Type.Put, null); 4247 4248 Put put = null; 4249 put = new Put(row1); 4250 put.add(kv14); 4251 put.add(kv24); 4252 region.put(put); 4253 region.flush(true); 4254 4255 put = new Put(row1); 4256 put.add(kv23); 4257 put.add(kv13); 4258 region.put(put); 4259 region.flush(true); 4260 4261 put = new Put(row1); 4262 put.add(kv22); 4263 put.add(kv12); 4264 region.put(put); 4265 region.flush(true); 4266 4267 put = new Put(row1); 4268 put.add(kv21); 4269 put.add(kv11); 4270 region.put(put); 4271 4272 // Expected 4273 List<KeyValue> expected = new ArrayList<>(); 4274 expected.add(kv14); 4275 expected.add(kv13); 4276 expected.add(kv12); 4277 expected.add(kv24); 4278 expected.add(kv23); 4279 expected.add(kv22); 4280 4281 Scan scan = new Scan().withStartRow(row1); 4282 int versions = 3; 4283 scan.readVersions(versions); 4284 List<ExtendedCell> actual = new ArrayList<>(); 4285 try (InternalScanner scanner = region.getScanner(scan)) { 4286 boolean hasNext = scanner.next(actual); 4287 assertEquals(false, hasNext); 4288 4289 // Verify result 4290 for (int i = 0; i < expected.size(); i++) { 4291 assertTrue(PrivateCellUtil.equalsIgnoreMvccVersion(expected.get(i), actual.get(i))); 4292 } 4293 } 4294 } 4295 4296 /** 4297 * Added for HBASE-5416 Here we test scan optimization when only subset of CFs are used in filter 4298 * conditions. 4299 */ 4300 @Test 4301 public void testScanner_JoinedScanners() throws IOException { 4302 byte[] cf_essential = Bytes.toBytes("essential"); 4303 byte[] cf_joined = Bytes.toBytes("joined"); 4304 byte[] cf_alpha = Bytes.toBytes("alpha"); 4305 this.region = initHRegion(tableName, method, CONF, cf_essential, cf_joined, cf_alpha); 4306 byte[] row1 = Bytes.toBytes("row1"); 4307 byte[] row2 = Bytes.toBytes("row2"); 4308 byte[] row3 = Bytes.toBytes("row3"); 4309 4310 byte[] col_normal = Bytes.toBytes("d"); 4311 byte[] col_alpha = Bytes.toBytes("a"); 4312 4313 byte[] filtered_val = Bytes.toBytes(3); 4314 4315 Put put = new Put(row1); 4316 put.addColumn(cf_essential, col_normal, Bytes.toBytes(1)); 4317 put.addColumn(cf_joined, col_alpha, Bytes.toBytes(1)); 4318 region.put(put); 4319 4320 put = new Put(row2); 4321 put.addColumn(cf_essential, col_alpha, Bytes.toBytes(2)); 4322 put.addColumn(cf_joined, col_normal, Bytes.toBytes(2)); 4323 put.addColumn(cf_alpha, col_alpha, Bytes.toBytes(2)); 4324 region.put(put); 4325 4326 put = new Put(row3); 4327 put.addColumn(cf_essential, col_normal, filtered_val); 4328 put.addColumn(cf_joined, col_normal, filtered_val); 4329 region.put(put); 4330 4331 // Check two things: 4332 // 1. result list contains expected values 4333 // 2. result list is sorted properly 4334 4335 Scan scan = new Scan(); 4336 Filter filter = new SingleColumnValueExcludeFilter(cf_essential, col_normal, 4337 CompareOperator.NOT_EQUAL, filtered_val); 4338 scan.setFilter(filter); 4339 scan.setLoadColumnFamiliesOnDemand(true); 4340 try (InternalScanner s = region.getScanner(scan)) { 4341 List<Cell> results = new ArrayList<>(); 4342 assertTrue(s.next(results)); 4343 assertEquals(1, results.size()); 4344 results.clear(); 4345 4346 assertTrue(s.next(results)); 4347 assertEquals(3, results.size()); 4348 assertTrue("orderCheck", CellUtil.matchingFamily(results.get(0), cf_alpha)); 4349 assertTrue("orderCheck", CellUtil.matchingFamily(results.get(1), cf_essential)); 4350 assertTrue("orderCheck", CellUtil.matchingFamily(results.get(2), cf_joined)); 4351 results.clear(); 4352 4353 assertFalse(s.next(results)); 4354 assertEquals(0, results.size()); 4355 } 4356 } 4357 4358 /** 4359 * HBASE-5416 Test case when scan limits amount of KVs returned on each next() call. 4360 */ 4361 @Test 4362 public void testScanner_JoinedScannersWithLimits() throws IOException { 4363 final byte[] cf_first = Bytes.toBytes("first"); 4364 final byte[] cf_second = Bytes.toBytes("second"); 4365 4366 this.region = initHRegion(tableName, method, CONF, cf_first, cf_second); 4367 final byte[] col_a = Bytes.toBytes("a"); 4368 final byte[] col_b = Bytes.toBytes("b"); 4369 4370 Put put; 4371 4372 for (int i = 0; i < 10; i++) { 4373 put = new Put(Bytes.toBytes("r" + Integer.toString(i))); 4374 put.addColumn(cf_first, col_a, Bytes.toBytes(i)); 4375 if (i < 5) { 4376 put.addColumn(cf_first, col_b, Bytes.toBytes(i)); 4377 put.addColumn(cf_second, col_a, Bytes.toBytes(i)); 4378 put.addColumn(cf_second, col_b, Bytes.toBytes(i)); 4379 } 4380 region.put(put); 4381 } 4382 4383 Scan scan = new Scan(); 4384 scan.setLoadColumnFamiliesOnDemand(true); 4385 Filter bogusFilter = new FilterBase() { 4386 @Override 4387 public ReturnCode filterCell(final Cell ignored) throws IOException { 4388 return ReturnCode.INCLUDE; 4389 } 4390 4391 @Override 4392 public boolean isFamilyEssential(byte[] name) { 4393 return Bytes.equals(name, cf_first); 4394 } 4395 }; 4396 4397 scan.setFilter(bogusFilter); 4398 try (InternalScanner s = region.getScanner(scan)) { 4399 // Our data looks like this: 4400 // r0: first:a, first:b, second:a, second:b 4401 // r1: first:a, first:b, second:a, second:b 4402 // r2: first:a, first:b, second:a, second:b 4403 // r3: first:a, first:b, second:a, second:b 4404 // r4: first:a, first:b, second:a, second:b 4405 // r5: first:a 4406 // r6: first:a 4407 // r7: first:a 4408 // r8: first:a 4409 // r9: first:a 4410 4411 // But due to next's limit set to 3, we should get this: 4412 // r0: first:a, first:b, second:a 4413 // r0: second:b 4414 // r1: first:a, first:b, second:a 4415 // r1: second:b 4416 // r2: first:a, first:b, second:a 4417 // r2: second:b 4418 // r3: first:a, first:b, second:a 4419 // r3: second:b 4420 // r4: first:a, first:b, second:a 4421 // r4: second:b 4422 // r5: first:a 4423 // r6: first:a 4424 // r7: first:a 4425 // r8: first:a 4426 // r9: first:a 4427 4428 List<Cell> results = new ArrayList<>(); 4429 int index = 0; 4430 ScannerContext scannerContext = ScannerContext.newBuilder().setBatchLimit(3).build(); 4431 while (true) { 4432 boolean more = s.next(results, scannerContext); 4433 if ((index >> 1) < 5) { 4434 if (index % 2 == 0) { 4435 assertEquals(3, results.size()); 4436 } else { 4437 assertEquals(1, results.size()); 4438 } 4439 } else { 4440 assertEquals(1, results.size()); 4441 } 4442 results.clear(); 4443 index++; 4444 if (!more) { 4445 break; 4446 } 4447 } 4448 } 4449 } 4450 4451 @Test 4452 public void testScannerOperationId() throws IOException { 4453 region = initHRegion(tableName, method, CONF, COLUMN_FAMILY_BYTES); 4454 Scan scan = new Scan(); 4455 try (RegionScanner scanner = region.getScanner(scan)) { 4456 assertNull(scanner.getOperationId()); 4457 } 4458 4459 String operationId = "test_operation_id_0101"; 4460 scan = new Scan().setId(operationId); 4461 try (RegionScanner scanner = region.getScanner(scan)) { 4462 assertEquals(operationId, scanner.getOperationId()); 4463 } 4464 4465 HBaseTestingUtil.closeRegionAndWAL(this.region); 4466 } 4467 4468 /** 4469 * Write an HFile block full with Cells whose qualifier that are identical between 0 and 4470 * Short.MAX_VALUE. See HBASE-13329. 4471 */ 4472 @Test 4473 public void testLongQualifier() throws Exception { 4474 byte[] family = Bytes.toBytes("family"); 4475 this.region = initHRegion(tableName, method, CONF, family); 4476 byte[] q = new byte[Short.MAX_VALUE + 2]; 4477 Arrays.fill(q, 0, q.length - 1, (byte) 42); 4478 for (byte i = 0; i < 10; i++) { 4479 Put p = new Put(Bytes.toBytes("row")); 4480 // qualifiers that differ past Short.MAX_VALUE 4481 q[q.length - 1] = i; 4482 p.addColumn(family, q, q); 4483 region.put(p); 4484 } 4485 region.flush(false); 4486 } 4487 4488 /** 4489 * Flushes the cache in a thread while scanning. The tests verify that the scan is coherent - e.g. 4490 * the returned results are always of the same or later update as the previous results. scan / 4491 * compact thread join 4492 */ 4493 @Test 4494 public void testFlushCacheWhileScanning() throws IOException, InterruptedException { 4495 byte[] family = Bytes.toBytes("family"); 4496 int numRows = 1000; 4497 int flushAndScanInterval = 10; 4498 int compactInterval = 10 * flushAndScanInterval; 4499 4500 this.region = initHRegion(tableName, method, CONF, family); 4501 FlushThread flushThread = new FlushThread(); 4502 try { 4503 flushThread.start(); 4504 4505 Scan scan = new Scan(); 4506 scan.addFamily(family); 4507 scan.setFilter(new SingleColumnValueFilter(family, qual1, CompareOperator.EQUAL, 4508 new BinaryComparator(Bytes.toBytes(5L)))); 4509 4510 int expectedCount = 0; 4511 List<Cell> res = new ArrayList<>(); 4512 4513 boolean toggle = true; 4514 for (long i = 0; i < numRows; i++) { 4515 Put put = new Put(Bytes.toBytes(i)); 4516 put.setDurability(Durability.SKIP_WAL); 4517 put.addColumn(family, qual1, Bytes.toBytes(i % 10)); 4518 region.put(put); 4519 4520 if (i != 0 && i % compactInterval == 0) { 4521 LOG.debug("iteration = " + i + " ts=" + EnvironmentEdgeManager.currentTime()); 4522 region.compact(true); 4523 } 4524 4525 if (i % 10 == 5L) { 4526 expectedCount++; 4527 } 4528 4529 if (i != 0 && i % flushAndScanInterval == 0) { 4530 res.clear(); 4531 try (InternalScanner scanner = region.getScanner(scan)) { 4532 if (toggle) { 4533 flushThread.flush(); 4534 } 4535 while (scanner.next(res)) { 4536 // ignore 4537 } 4538 } 4539 if (!toggle) { 4540 flushThread.flush(); 4541 } 4542 assertEquals( 4543 "toggle=" + toggle + "i=" + i + " ts=" + EnvironmentEdgeManager.currentTime(), 4544 expectedCount, res.size()); 4545 toggle = !toggle; 4546 } 4547 } 4548 4549 } finally { 4550 try { 4551 flushThread.done(); 4552 flushThread.join(); 4553 flushThread.checkNoError(); 4554 } catch (InterruptedException ie) { 4555 LOG.warn("Caught exception when joining with flushThread", ie); 4556 } 4557 HBaseTestingUtil.closeRegionAndWAL(this.region); 4558 this.region = null; 4559 } 4560 } 4561 4562 protected class FlushThread extends Thread { 4563 private volatile boolean done; 4564 private Throwable error = null; 4565 4566 FlushThread() { 4567 super("FlushThread"); 4568 } 4569 4570 public void done() { 4571 done = true; 4572 synchronized (this) { 4573 interrupt(); 4574 } 4575 } 4576 4577 public void checkNoError() { 4578 if (error != null) { 4579 assertNull(error); 4580 } 4581 } 4582 4583 @Override 4584 public void run() { 4585 done = false; 4586 while (!done && !Thread.currentThread().isInterrupted()) { 4587 synchronized (this) { 4588 try { 4589 wait(); 4590 } catch (InterruptedException ignored) { 4591 if (done) { 4592 break; 4593 } 4594 } 4595 } 4596 try { 4597 region.flush(true); 4598 } catch (IOException e) { 4599 if (!done) { 4600 LOG.error("Error while flushing cache", e); 4601 error = e; 4602 } 4603 break; 4604 } catch (Throwable t) { 4605 LOG.error("Uncaught exception", t); 4606 throw t; 4607 } 4608 } 4609 } 4610 4611 public void flush() { 4612 synchronized (this) { 4613 notify(); 4614 } 4615 } 4616 } 4617 4618 /** 4619 * So can be overridden in subclasses. 4620 */ 4621 protected int getNumQualifiersForTestWritesWhileScanning() { 4622 return 100; 4623 } 4624 4625 /** 4626 * So can be overridden in subclasses. 4627 */ 4628 protected int getTestCountForTestWritesWhileScanning() { 4629 return 100; 4630 } 4631 4632 /** 4633 * Writes very wide records and scans for the latest every time.. Flushes and compacts the region 4634 * every now and then to keep things realistic. by flush / scan / compaction when joining threads 4635 */ 4636 @Test 4637 public void testWritesWhileScanning() throws IOException, InterruptedException { 4638 int testCount = getTestCountForTestWritesWhileScanning(); 4639 int numRows = 1; 4640 int numFamilies = 10; 4641 int numQualifiers = getNumQualifiersForTestWritesWhileScanning(); 4642 int flushInterval = 7; 4643 int compactInterval = 5 * flushInterval; 4644 byte[][] families = new byte[numFamilies][]; 4645 for (int i = 0; i < numFamilies; i++) { 4646 families[i] = Bytes.toBytes("family" + i); 4647 } 4648 byte[][] qualifiers = new byte[numQualifiers][]; 4649 for (int i = 0; i < numQualifiers; i++) { 4650 qualifiers[i] = Bytes.toBytes("qual" + i); 4651 } 4652 4653 this.region = initHRegion(tableName, method, CONF, families); 4654 FlushThread flushThread = new FlushThread(); 4655 PutThread putThread = new PutThread(numRows, families, qualifiers); 4656 try { 4657 putThread.start(); 4658 putThread.waitForFirstPut(); 4659 4660 flushThread.start(); 4661 4662 Scan scan = new Scan().withStartRow(Bytes.toBytes("row0")).withStopRow(Bytes.toBytes("row1")); 4663 4664 int expectedCount = numFamilies * numQualifiers; 4665 List<Cell> res = new ArrayList<>(); 4666 4667 long prevTimestamp = 0L; 4668 for (int i = 0; i < testCount; i++) { 4669 4670 if (i != 0 && i % compactInterval == 0) { 4671 region.compact(true); 4672 for (HStore store : region.getStores()) { 4673 store.closeAndArchiveCompactedFiles(); 4674 } 4675 } 4676 4677 if (i != 0 && i % flushInterval == 0) { 4678 flushThread.flush(); 4679 } 4680 4681 boolean previousEmpty = res.isEmpty(); 4682 res.clear(); 4683 try (InternalScanner scanner = region.getScanner(scan)) { 4684 boolean moreRows; 4685 do { 4686 moreRows = scanner.next(res); 4687 } while (moreRows); 4688 } 4689 if (!res.isEmpty() || !previousEmpty || i > compactInterval) { 4690 assertEquals("i=" + i, expectedCount, res.size()); 4691 long timestamp = res.get(0).getTimestamp(); 4692 assertTrue("Timestamps were broke: " + timestamp + " prev: " + prevTimestamp, 4693 timestamp >= prevTimestamp); 4694 prevTimestamp = timestamp; 4695 } 4696 } 4697 4698 putThread.done(); 4699 4700 region.flush(true); 4701 4702 } finally { 4703 try { 4704 flushThread.done(); 4705 flushThread.join(); 4706 flushThread.checkNoError(); 4707 4708 putThread.join(); 4709 putThread.checkNoError(); 4710 } catch (InterruptedException ie) { 4711 LOG.warn("Caught exception when joining with flushThread", ie); 4712 } 4713 4714 try { 4715 HBaseTestingUtil.closeRegionAndWAL(this.region); 4716 } catch (DroppedSnapshotException dse) { 4717 // We could get this on way out because we interrupt the background flusher and it could 4718 // fail anywhere causing a DSE over in the background flusher... only it is not properly 4719 // dealt with so could still be memory hanging out when we get to here -- memory we can't 4720 // flush because the accounting is 'off' since original DSE. 4721 } 4722 this.region = null; 4723 } 4724 } 4725 4726 @Test 4727 public void testCloseAndArchiveCompactedFiles() throws IOException { 4728 byte[] CF1 = Bytes.toBytes("CF1"); 4729 byte[] CF2 = Bytes.toBytes("CF2"); 4730 this.region = initHRegion(tableName, method, CONF, CF1, CF2); 4731 for (int i = 0; i < 2; i++) { 4732 int index = i; 4733 Put put = 4734 new Put(Bytes.toBytes(index)).addColumn(CF1, Bytes.toBytes("q"), Bytes.toBytes(index)); 4735 region.put(put); 4736 region.flush(true); 4737 } 4738 4739 region.compact(true); 4740 4741 HStore store1 = region.getStore(CF1); 4742 HStore store2 = region.getStore(CF2); 4743 store1.closeAndArchiveCompactedFiles(); 4744 store2.closeAndArchiveCompactedFiles(); 4745 4746 int storefilesCount = region.getStores().stream().mapToInt(Store::getStorefilesCount).sum(); 4747 assertTrue(storefilesCount == 1); 4748 4749 FileSystem fs = region.getRegionFileSystem().getFileSystem(); 4750 Configuration conf = region.getReadOnlyConfiguration(); 4751 RegionInfo regionInfo = region.getRegionInfo(); 4752 Path store1ArchiveDir = HFileArchiveUtil.getStoreArchivePath(conf, regionInfo, CF1); 4753 assertTrue(fs.exists(store1ArchiveDir)); 4754 // The archived dir of CF2 does not exist because this column family has no data at all 4755 Path store2ArchiveDir = HFileArchiveUtil.getStoreArchivePath(conf, regionInfo, CF2); 4756 assertFalse(fs.exists(store2ArchiveDir)); 4757 } 4758 4759 protected class PutThread extends Thread { 4760 private volatile boolean done; 4761 private volatile int numPutsFinished = 0; 4762 4763 private Throwable error = null; 4764 private int numRows; 4765 private byte[][] families; 4766 private byte[][] qualifiers; 4767 4768 private PutThread(int numRows, byte[][] families, byte[][] qualifiers) { 4769 super("PutThread"); 4770 this.numRows = numRows; 4771 this.families = families; 4772 this.qualifiers = qualifiers; 4773 } 4774 4775 /** 4776 * Block calling thread until this instance of PutThread has put at least one row. 4777 */ 4778 public void waitForFirstPut() throws InterruptedException { 4779 // wait until put thread actually puts some data 4780 while (isAlive() && numPutsFinished == 0) { 4781 checkNoError(); 4782 Thread.sleep(50); 4783 } 4784 } 4785 4786 public void done() { 4787 done = true; 4788 synchronized (this) { 4789 interrupt(); 4790 } 4791 } 4792 4793 public void checkNoError() { 4794 if (error != null) { 4795 assertNull(error); 4796 } 4797 } 4798 4799 @Override 4800 public void run() { 4801 done = false; 4802 while (!done && !Thread.currentThread().isInterrupted()) { 4803 try { 4804 for (int r = 0; r < numRows; r++) { 4805 byte[] row = Bytes.toBytes("row" + r); 4806 Put put = new Put(row); 4807 put.setDurability(Durability.SKIP_WAL); 4808 byte[] value = Bytes.toBytes(String.valueOf(numPutsFinished)); 4809 for (byte[] family : families) { 4810 for (byte[] qualifier : qualifiers) { 4811 put.addColumn(family, qualifier, numPutsFinished, value); 4812 } 4813 } 4814 region.put(put); 4815 numPutsFinished++; 4816 if (numPutsFinished > 0 && numPutsFinished % 47 == 0) { 4817 LOG.debug("put iteration = {}", numPutsFinished); 4818 Delete delete = new Delete(row, (long) numPutsFinished - 30); 4819 region.delete(delete); 4820 } 4821 numPutsFinished++; 4822 } 4823 } catch (InterruptedIOException e) { 4824 // This is fine. It means we are done, or didn't get the lock on time 4825 LOG.info("Interrupted", e); 4826 } catch (IOException e) { 4827 LOG.error("Error while putting records", e); 4828 error = e; 4829 break; 4830 } 4831 } 4832 4833 } 4834 4835 } 4836 4837 /** 4838 * Writes very wide records and gets the latest row every time.. Flushes and compacts the region 4839 * aggressivly to catch issues. by flush / scan / compaction when joining threads 4840 */ 4841 @Test 4842 public void testWritesWhileGetting() throws Exception { 4843 int testCount = 50; 4844 int numRows = 1; 4845 int numFamilies = 10; 4846 int numQualifiers = 100; 4847 int compactInterval = 100; 4848 byte[][] families = new byte[numFamilies][]; 4849 for (int i = 0; i < numFamilies; i++) { 4850 families[i] = Bytes.toBytes("family" + i); 4851 } 4852 byte[][] qualifiers = new byte[numQualifiers][]; 4853 for (int i = 0; i < numQualifiers; i++) { 4854 qualifiers[i] = Bytes.toBytes("qual" + i); 4855 } 4856 4857 // This test flushes constantly and can cause many files to be created, 4858 // possibly 4859 // extending over the ulimit. Make sure compactions are aggressive in 4860 // reducing 4861 // the number of HFiles created. 4862 Configuration conf = HBaseConfiguration.create(CONF); 4863 conf.setInt("hbase.hstore.compaction.min", 1); 4864 conf.setInt("hbase.hstore.compaction.max", 1000); 4865 this.region = initHRegion(tableName, method, conf, families); 4866 PutThread putThread = null; 4867 MultithreadedTestUtil.TestContext ctx = new MultithreadedTestUtil.TestContext(conf); 4868 try { 4869 putThread = new PutThread(numRows, families, qualifiers); 4870 putThread.start(); 4871 putThread.waitForFirstPut(); 4872 4873 // Add a thread that flushes as fast as possible 4874 ctx.addThread(new RepeatingTestThread(ctx) { 4875 4876 @Override 4877 public void doAnAction() throws Exception { 4878 region.flush(true); 4879 // Compact regularly to avoid creating too many files and exceeding 4880 // the ulimit. 4881 region.compact(false); 4882 for (HStore store : region.getStores()) { 4883 store.closeAndArchiveCompactedFiles(); 4884 } 4885 } 4886 }); 4887 ctx.startThreads(); 4888 4889 Get get = new Get(Bytes.toBytes("row0")); 4890 Result result = null; 4891 4892 int expectedCount = numFamilies * numQualifiers; 4893 4894 long prevTimestamp = 0L; 4895 for (int i = 0; i < testCount; i++) { 4896 LOG.info("testWritesWhileGetting verify turn " + i); 4897 boolean previousEmpty = result == null || result.isEmpty(); 4898 result = region.get(get); 4899 if (!result.isEmpty() || !previousEmpty || i > compactInterval) { 4900 assertEquals("i=" + i, expectedCount, result.size()); 4901 // TODO this was removed, now what dangit?! 4902 // search looking for the qualifier in question? 4903 long timestamp = 0; 4904 for (Cell kv : result.rawCells()) { 4905 if ( 4906 CellUtil.matchingFamily(kv, families[0]) 4907 && CellUtil.matchingQualifier(kv, qualifiers[0]) 4908 ) { 4909 timestamp = kv.getTimestamp(); 4910 } 4911 } 4912 assertTrue(timestamp >= prevTimestamp); 4913 prevTimestamp = timestamp; 4914 ExtendedCell previousKV = null; 4915 4916 for (ExtendedCell kv : ClientInternalHelper.getExtendedRawCells(result)) { 4917 byte[] thisValue = CellUtil.cloneValue(kv); 4918 if (previousKV != null) { 4919 if (Bytes.compareTo(CellUtil.cloneValue(previousKV), thisValue) != 0) { 4920 LOG.warn("These two KV should have the same value." + " Previous KV:" + previousKV 4921 + "(memStoreTS:" + previousKV.getSequenceId() + ")" + ", New KV: " + kv 4922 + "(memStoreTS:" + kv.getSequenceId() + ")"); 4923 assertEquals(0, Bytes.compareTo(CellUtil.cloneValue(previousKV), thisValue)); 4924 } 4925 } 4926 previousKV = kv; 4927 } 4928 } 4929 } 4930 } finally { 4931 if (putThread != null) putThread.done(); 4932 4933 region.flush(true); 4934 4935 if (putThread != null) { 4936 putThread.join(); 4937 putThread.checkNoError(); 4938 } 4939 4940 ctx.stop(); 4941 HBaseTestingUtil.closeRegionAndWAL(this.region); 4942 this.region = null; 4943 } 4944 } 4945 4946 @Test 4947 public void testHolesInMeta() throws Exception { 4948 byte[] family = Bytes.toBytes("family"); 4949 this.region = 4950 initHRegion(tableName, Bytes.toBytes("x"), Bytes.toBytes("z"), method, CONF, false, family); 4951 byte[] rowNotServed = Bytes.toBytes("a"); 4952 Get g = new Get(rowNotServed); 4953 try { 4954 region.get(g); 4955 fail(); 4956 } catch (WrongRegionException x) { 4957 // OK 4958 } 4959 byte[] row = Bytes.toBytes("y"); 4960 g = new Get(row); 4961 region.get(g); 4962 } 4963 4964 @Test 4965 public void testIndexesScanWithOneDeletedRow() throws IOException { 4966 byte[] family = Bytes.toBytes("family"); 4967 4968 // Setting up region 4969 this.region = initHRegion(tableName, method, CONF, family); 4970 Put put = new Put(Bytes.toBytes(1L)); 4971 put.addColumn(family, qual1, 1L, Bytes.toBytes(1L)); 4972 region.put(put); 4973 4974 region.flush(true); 4975 4976 Delete delete = new Delete(Bytes.toBytes(1L), 1L); 4977 region.delete(delete); 4978 4979 put = new Put(Bytes.toBytes(2L)); 4980 put.addColumn(family, qual1, 2L, Bytes.toBytes(2L)); 4981 region.put(put); 4982 4983 Scan idxScan = new Scan(); 4984 idxScan.addFamily(family); 4985 idxScan.setFilter(new FilterList(FilterList.Operator.MUST_PASS_ALL, 4986 Arrays.<Filter> asList( 4987 new SingleColumnValueFilter(family, qual1, CompareOperator.GREATER_OR_EQUAL, 4988 new BinaryComparator(Bytes.toBytes(0L))), 4989 new SingleColumnValueFilter(family, qual1, CompareOperator.LESS_OR_EQUAL, 4990 new BinaryComparator(Bytes.toBytes(3L)))))); 4991 try (InternalScanner scanner = region.getScanner(idxScan)) { 4992 List<Cell> res = new ArrayList<>(); 4993 4994 while (scanner.next(res)) { 4995 // Ignore res value. 4996 } 4997 assertEquals(1L, res.size()); 4998 } 4999 } 5000 5001 // //////////////////////////////////////////////////////////////////////////// 5002 // Bloom filter test 5003 // //////////////////////////////////////////////////////////////////////////// 5004 @Test 5005 public void testBloomFilterSize() throws IOException { 5006 byte[] fam1 = Bytes.toBytes("fam1"); 5007 byte[] qf1 = Bytes.toBytes("col"); 5008 byte[] val1 = Bytes.toBytes("value1"); 5009 // Create Table 5010 TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName) 5011 .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(fam1) 5012 .setMaxVersions(Integer.MAX_VALUE).setBloomFilterType(BloomType.ROWCOL).build()) 5013 .build(); 5014 RegionInfo info = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build(); 5015 this.region = TEST_UTIL.createLocalHRegion(info, tableDescriptor); 5016 int num_unique_rows = 10; 5017 int duplicate_multiplier = 2; 5018 int num_storefiles = 4; 5019 5020 int version = 0; 5021 for (int f = 0; f < num_storefiles; f++) { 5022 for (int i = 0; i < duplicate_multiplier; i++) { 5023 for (int j = 0; j < num_unique_rows; j++) { 5024 Put put = new Put(Bytes.toBytes("row" + j)); 5025 put.setDurability(Durability.SKIP_WAL); 5026 long ts = version++; 5027 put.addColumn(fam1, qf1, ts, val1); 5028 region.put(put); 5029 } 5030 } 5031 region.flush(true); 5032 } 5033 // before compaction 5034 HStore store = region.getStore(fam1); 5035 Collection<HStoreFile> storeFiles = store.getStorefiles(); 5036 for (HStoreFile storefile : storeFiles) { 5037 StoreFileReader reader = storefile.getReader(); 5038 reader.loadFileInfo(); 5039 reader.loadBloomfilter(); 5040 assertEquals(num_unique_rows * duplicate_multiplier, reader.getEntries()); 5041 assertEquals(num_unique_rows, reader.getFilterEntries()); 5042 } 5043 5044 region.compact(true); 5045 5046 // after compaction 5047 storeFiles = store.getStorefiles(); 5048 for (HStoreFile storefile : storeFiles) { 5049 StoreFileReader reader = storefile.getReader(); 5050 reader.loadFileInfo(); 5051 reader.loadBloomfilter(); 5052 assertEquals(num_unique_rows * duplicate_multiplier * num_storefiles, reader.getEntries()); 5053 assertEquals(num_unique_rows, reader.getFilterEntries()); 5054 } 5055 } 5056 5057 @Test 5058 public void testAllColumnsWithBloomFilter() throws IOException { 5059 byte[] TABLE = Bytes.toBytes(name.getMethodName()); 5060 byte[] FAMILY = Bytes.toBytes("family"); 5061 5062 // Create table 5063 TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(TableName.valueOf(TABLE)) 5064 .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(FAMILY) 5065 .setMaxVersions(Integer.MAX_VALUE).setBloomFilterType(BloomType.ROWCOL).build()) 5066 .build(); 5067 RegionInfo info = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build(); 5068 this.region = TEST_UTIL.createLocalHRegion(info, tableDescriptor); 5069 // For row:0, col:0: insert versions 1 through 5. 5070 byte[] row = Bytes.toBytes("row:" + 0); 5071 byte[] column = Bytes.toBytes("column:" + 0); 5072 Put put = new Put(row); 5073 put.setDurability(Durability.SKIP_WAL); 5074 for (long idx = 1; idx <= 4; idx++) { 5075 put.addColumn(FAMILY, column, idx, Bytes.toBytes("value-version-" + idx)); 5076 } 5077 region.put(put); 5078 5079 // Flush 5080 region.flush(true); 5081 5082 // Get rows 5083 Get get = new Get(row); 5084 get.readAllVersions(); 5085 Cell[] kvs = region.get(get).rawCells(); 5086 5087 // Check if rows are correct 5088 assertEquals(4, kvs.length); 5089 checkOneCell(kvs[0], FAMILY, 0, 0, 4); 5090 checkOneCell(kvs[1], FAMILY, 0, 0, 3); 5091 checkOneCell(kvs[2], FAMILY, 0, 0, 2); 5092 checkOneCell(kvs[3], FAMILY, 0, 0, 1); 5093 } 5094 5095 /** 5096 * Testcase to cover bug-fix for HBASE-2823 Ensures correct delete when issuing delete row on 5097 * columns with bloom filter set to row+col (BloomType.ROWCOL) 5098 */ 5099 @Test 5100 public void testDeleteRowWithBloomFilter() throws IOException { 5101 byte[] familyName = Bytes.toBytes("familyName"); 5102 5103 // Create Table 5104 TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName) 5105 .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(familyName) 5106 .setMaxVersions(Integer.MAX_VALUE).setBloomFilterType(BloomType.ROWCOL).build()) 5107 .build(); 5108 RegionInfo info = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build(); 5109 this.region = TEST_UTIL.createLocalHRegion(info, tableDescriptor); 5110 // Insert some data 5111 byte[] row = Bytes.toBytes("row1"); 5112 byte[] col = Bytes.toBytes("col1"); 5113 5114 Put put = new Put(row); 5115 put.addColumn(familyName, col, 1, Bytes.toBytes("SomeRandomValue")); 5116 region.put(put); 5117 region.flush(true); 5118 5119 Delete del = new Delete(row); 5120 region.delete(del); 5121 region.flush(true); 5122 5123 // Get remaining rows (should have none) 5124 Get get = new Get(row); 5125 get.addColumn(familyName, col); 5126 5127 Cell[] keyValues = region.get(get).rawCells(); 5128 assertEquals(0, keyValues.length); 5129 } 5130 5131 @Test 5132 public void testgetHDFSBlocksDistribution() throws Exception { 5133 HBaseTestingUtil htu = new HBaseTestingUtil(); 5134 // Why do we set the block size in this test? If we set it smaller than the kvs, then we'll 5135 // break up the file in to more pieces that can be distributed across the three nodes and we 5136 // won't be able to have the condition this test asserts; that at least one node has 5137 // a copy of all replicas -- if small block size, then blocks are spread evenly across the 5138 // the three nodes. hfilev3 with tags seems to put us over the block size. St.Ack. 5139 // final int DEFAULT_BLOCK_SIZE = 1024; 5140 // htu.getConfiguration().setLong("dfs.blocksize", DEFAULT_BLOCK_SIZE); 5141 htu.getConfiguration().setInt("dfs.replication", 2); 5142 5143 // set up a cluster with 3 nodes 5144 SingleProcessHBaseCluster cluster = null; 5145 String dataNodeHosts[] = new String[] { "host1", "host2", "host3" }; 5146 int regionServersCount = 3; 5147 5148 try { 5149 StartTestingClusterOption option = StartTestingClusterOption.builder() 5150 .numRegionServers(regionServersCount).dataNodeHosts(dataNodeHosts).build(); 5151 cluster = htu.startMiniCluster(option); 5152 byte[][] families = { fam1, fam2 }; 5153 Table ht = htu.createTable(tableName, families); 5154 5155 // Setting up region 5156 byte row[] = Bytes.toBytes("row1"); 5157 byte col[] = Bytes.toBytes("col1"); 5158 5159 Put put = new Put(row); 5160 put.addColumn(fam1, col, 1, Bytes.toBytes("test1")); 5161 put.addColumn(fam2, col, 1, Bytes.toBytes("test2")); 5162 ht.put(put); 5163 5164 HRegion firstRegion = htu.getHBaseCluster().getRegions(tableName).get(0); 5165 firstRegion.flush(true); 5166 HDFSBlocksDistribution blocksDistribution1 = firstRegion.getHDFSBlocksDistribution(); 5167 5168 // Given the default replication factor is 2 and we have 2 HFiles, 5169 // we will have total of 4 replica of blocks on 3 datanodes; thus there 5170 // must be at least one host that have replica for 2 HFiles. That host's 5171 // weight will be equal to the unique block weight. 5172 long uniqueBlocksWeight1 = blocksDistribution1.getUniqueBlocksTotalWeight(); 5173 StringBuilder sb = new StringBuilder(); 5174 for (String host : blocksDistribution1.getTopHosts()) { 5175 if (sb.length() > 0) sb.append(", "); 5176 sb.append(host); 5177 sb.append("="); 5178 sb.append(blocksDistribution1.getWeight(host)); 5179 } 5180 5181 String topHost = blocksDistribution1.getTopHosts().get(0); 5182 long topHostWeight = blocksDistribution1.getWeight(topHost); 5183 String msg = "uniqueBlocksWeight=" + uniqueBlocksWeight1 + ", topHostWeight=" + topHostWeight 5184 + ", topHost=" + topHost + "; " + sb.toString(); 5185 LOG.info(msg); 5186 assertTrue(msg, uniqueBlocksWeight1 == topHostWeight); 5187 5188 // use the static method to compute the value, it should be the same. 5189 // static method is used by load balancer or other components 5190 HDFSBlocksDistribution blocksDistribution2 = HRegion.computeHDFSBlocksDistribution( 5191 htu.getConfiguration(), firstRegion.getTableDescriptor(), firstRegion.getRegionInfo()); 5192 long uniqueBlocksWeight2 = blocksDistribution2.getUniqueBlocksTotalWeight(); 5193 5194 assertTrue(uniqueBlocksWeight1 == uniqueBlocksWeight2); 5195 5196 ht.close(); 5197 } finally { 5198 if (cluster != null) { 5199 htu.shutdownMiniCluster(); 5200 } 5201 } 5202 } 5203 5204 /** 5205 * Testcase to check state of region initialization task set to ABORTED or not if any exceptions 5206 * during initialization 5207 */ 5208 @Test 5209 public void testStatusSettingToAbortIfAnyExceptionDuringRegionInitilization() throws Exception { 5210 RegionInfo info; 5211 try { 5212 FileSystem fs = mock(FileSystem.class); 5213 when(fs.exists(any())).thenThrow(new IOException()); 5214 TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(tableName); 5215 ColumnFamilyDescriptor columnFamilyDescriptor = 5216 ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf")).build(); 5217 tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor); 5218 info = RegionInfoBuilder.newBuilder(tableName).build(); 5219 Path path = new Path(dir + "testStatusSettingToAbortIfAnyExceptionDuringRegionInitilization"); 5220 region = HRegion.newHRegion(path, null, fs, CONF, info, tableDescriptorBuilder.build(), null); 5221 // region initialization throws IOException and set task state to ABORTED. 5222 region.initialize(); 5223 fail("Region initialization should fail due to IOException"); 5224 } catch (IOException io) { 5225 List<MonitoredTask> tasks = TaskMonitor.get().getTasks(); 5226 for (MonitoredTask monitoredTask : tasks) { 5227 if ( 5228 !(monitoredTask instanceof MonitoredRPCHandler) 5229 && monitoredTask.getDescription().contains(region.toString()) 5230 ) { 5231 assertTrue("Region state should be ABORTED.", 5232 monitoredTask.getState().equals(MonitoredTask.State.ABORTED)); 5233 break; 5234 } 5235 } 5236 } 5237 } 5238 5239 /** 5240 * Verifies that the .regioninfo file is written on region creation and that is recreated if 5241 * missing during region opening. 5242 */ 5243 @Test 5244 public void testRegionInfoFileCreation() throws IOException { 5245 Path rootDir = new Path(dir + "testRegionInfoFileCreation"); 5246 5247 TableDescriptorBuilder tableDescriptorBuilder = 5248 TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())); 5249 ColumnFamilyDescriptor columnFamilyDescriptor = 5250 ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf")).build(); 5251 tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor); 5252 TableDescriptor tableDescriptor = tableDescriptorBuilder.build(); 5253 5254 RegionInfo hri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build(); 5255 5256 // Create a region and skip the initialization (like CreateTableHandler) 5257 region = HBaseTestingUtil.createRegionAndWAL(hri, rootDir, CONF, tableDescriptor, false); 5258 Path regionDir = region.getRegionFileSystem().getRegionDir(); 5259 FileSystem fs = region.getRegionFileSystem().getFileSystem(); 5260 HBaseTestingUtil.closeRegionAndWAL(region); 5261 5262 Path regionInfoFile = new Path(regionDir, HRegionFileSystem.REGION_INFO_FILE); 5263 5264 // Verify that the .regioninfo file is present 5265 assertTrue(HRegionFileSystem.REGION_INFO_FILE + " should be present in the region dir", 5266 fs.exists(regionInfoFile)); 5267 5268 // Try to open the region 5269 region = HRegion.openHRegion(rootDir, hri, tableDescriptor, null, CONF); 5270 assertEquals(regionDir, region.getRegionFileSystem().getRegionDir()); 5271 HBaseTestingUtil.closeRegionAndWAL(region); 5272 5273 // Verify that the .regioninfo file is still there 5274 assertTrue(HRegionFileSystem.REGION_INFO_FILE + " should be present in the region dir", 5275 fs.exists(regionInfoFile)); 5276 5277 // Remove the .regioninfo file and verify is recreated on region open 5278 fs.delete(regionInfoFile, true); 5279 assertFalse(HRegionFileSystem.REGION_INFO_FILE + " should be removed from the region dir", 5280 fs.exists(regionInfoFile)); 5281 5282 region = HRegion.openHRegion(rootDir, hri, tableDescriptor, null, CONF); 5283 // region = TEST_UTIL.openHRegion(hri, htd); 5284 assertEquals(regionDir, region.getRegionFileSystem().getRegionDir()); 5285 HBaseTestingUtil.closeRegionAndWAL(region); 5286 5287 // Verify that the .regioninfo file is still there 5288 assertTrue(HRegionFileSystem.REGION_INFO_FILE + " should be present in the region dir", 5289 fs.exists(new Path(regionDir, HRegionFileSystem.REGION_INFO_FILE))); 5290 5291 region = null; 5292 } 5293 5294 /** 5295 * TestCase for increment 5296 */ 5297 private static class Incrementer implements Runnable { 5298 private HRegion region; 5299 private final static byte[] incRow = Bytes.toBytes("incRow"); 5300 private final static byte[] family = Bytes.toBytes("family"); 5301 private final static byte[] qualifier = Bytes.toBytes("qualifier"); 5302 private final static long ONE = 1L; 5303 private int incCounter; 5304 5305 public Incrementer(HRegion region, int incCounter) { 5306 this.region = region; 5307 this.incCounter = incCounter; 5308 } 5309 5310 @Override 5311 public void run() { 5312 int count = 0; 5313 while (count < incCounter) { 5314 Increment inc = new Increment(incRow); 5315 inc.addColumn(family, qualifier, ONE); 5316 count++; 5317 try { 5318 region.increment(inc); 5319 } catch (IOException e) { 5320 LOG.info("Count=" + count + ", " + e); 5321 break; 5322 } 5323 } 5324 } 5325 } 5326 5327 /** 5328 * Test case to check increment function with memstore flushing 5329 */ 5330 @Test 5331 public void testParallelIncrementWithMemStoreFlush() throws Exception { 5332 byte[] family = Incrementer.family; 5333 this.region = initHRegion(tableName, method, CONF, family); 5334 final HRegion region = this.region; 5335 final AtomicBoolean incrementDone = new AtomicBoolean(false); 5336 Runnable flusher = new Runnable() { 5337 @Override 5338 public void run() { 5339 while (!incrementDone.get() && !Thread.currentThread().isInterrupted()) { 5340 try { 5341 region.flush(true); 5342 } catch (Exception e) { 5343 e.printStackTrace(); 5344 } 5345 } 5346 } 5347 }; 5348 5349 // after all increment finished, the row will increment to 20*100 = 2000 5350 int threadNum = 20; 5351 int incCounter = 100; 5352 long expected = (long) threadNum * incCounter; 5353 Thread[] incrementers = new Thread[threadNum]; 5354 Thread flushThread = new Thread(flusher); 5355 flushThread.setName("FlushThread-" + method); 5356 for (int i = 0; i < threadNum; i++) { 5357 incrementers[i] = new Thread(new Incrementer(this.region, incCounter)); 5358 incrementers[i].start(); 5359 } 5360 flushThread.start(); 5361 try { 5362 for (int i = 0; i < threadNum; i++) { 5363 incrementers[i].join(); 5364 } 5365 5366 incrementDone.set(true); 5367 flushThread.join(); 5368 5369 Get get = new Get(Incrementer.incRow); 5370 get.addColumn(Incrementer.family, Incrementer.qualifier); 5371 get.readVersions(1); 5372 Result res = this.region.get(get); 5373 List<Cell> kvs = res.getColumnCells(Incrementer.family, Incrementer.qualifier); 5374 5375 // we just got the latest version 5376 assertEquals(1, kvs.size()); 5377 Cell kv = kvs.get(0); 5378 assertEquals(expected, Bytes.toLong(kv.getValueArray(), kv.getValueOffset())); 5379 } finally { 5380 // Ensure flush thread is stopped even if test fails or times out 5381 incrementDone.set(true); 5382 flushThread.interrupt(); 5383 flushThread.join(5000); // Wait up to 5 seconds for thread to stop 5384 if (flushThread.isAlive()) { 5385 LOG.warn("Flush thread did not stop within timeout for test " + method); 5386 } 5387 } 5388 } 5389 5390 /** 5391 * TestCase for append 5392 */ 5393 private static class Appender implements Runnable { 5394 private HRegion region; 5395 private final static byte[] appendRow = Bytes.toBytes("appendRow"); 5396 private final static byte[] family = Bytes.toBytes("family"); 5397 private final static byte[] qualifier = Bytes.toBytes("qualifier"); 5398 private final static byte[] CHAR = Bytes.toBytes("a"); 5399 private int appendCounter; 5400 5401 public Appender(HRegion region, int appendCounter) { 5402 this.region = region; 5403 this.appendCounter = appendCounter; 5404 } 5405 5406 @Override 5407 public void run() { 5408 int count = 0; 5409 while (count < appendCounter) { 5410 Append app = new Append(appendRow); 5411 app.addColumn(family, qualifier, CHAR); 5412 count++; 5413 try { 5414 region.append(app); 5415 } catch (IOException e) { 5416 LOG.info("Count=" + count + ", max=" + appendCounter + ", " + e); 5417 break; 5418 } 5419 } 5420 } 5421 } 5422 5423 /** 5424 * Test case to check append function with memstore flushing 5425 */ 5426 @Test 5427 public void testParallelAppendWithMemStoreFlush() throws Exception { 5428 byte[] family = Appender.family; 5429 this.region = initHRegion(tableName, method, CONF, family); 5430 final HRegion region = this.region; 5431 final AtomicBoolean appendDone = new AtomicBoolean(false); 5432 Runnable flusher = new Runnable() { 5433 @Override 5434 public void run() { 5435 while (!appendDone.get() && !Thread.currentThread().isInterrupted()) { 5436 try { 5437 region.flush(true); 5438 } catch (Exception e) { 5439 e.printStackTrace(); 5440 } 5441 } 5442 } 5443 }; 5444 5445 // After all append finished, the value will append to threadNum * 5446 // appendCounter Appender.CHAR 5447 int threadNum = 20; 5448 int appendCounter = 100; 5449 byte[] expected = new byte[threadNum * appendCounter]; 5450 for (int i = 0; i < threadNum * appendCounter; i++) { 5451 System.arraycopy(Appender.CHAR, 0, expected, i, 1); 5452 } 5453 Thread[] appenders = new Thread[threadNum]; 5454 Thread flushThread = new Thread(flusher); 5455 flushThread.setName("FlushThread-" + method); 5456 for (int i = 0; i < threadNum; i++) { 5457 appenders[i] = new Thread(new Appender(this.region, appendCounter)); 5458 appenders[i].start(); 5459 } 5460 flushThread.start(); 5461 try { 5462 for (int i = 0; i < threadNum; i++) { 5463 appenders[i].join(); 5464 } 5465 5466 appendDone.set(true); 5467 flushThread.join(); 5468 5469 Get get = new Get(Appender.appendRow); 5470 get.addColumn(Appender.family, Appender.qualifier); 5471 get.readVersions(1); 5472 Result res = this.region.get(get); 5473 List<Cell> kvs = res.getColumnCells(Appender.family, Appender.qualifier); 5474 5475 // we just got the latest version 5476 assertEquals(1, kvs.size()); 5477 Cell kv = kvs.get(0); 5478 byte[] appendResult = new byte[kv.getValueLength()]; 5479 System.arraycopy(kv.getValueArray(), kv.getValueOffset(), appendResult, 0, 5480 kv.getValueLength()); 5481 assertArrayEquals(expected, appendResult); 5482 } finally { 5483 // Ensure flush thread is stopped even if test fails or times out 5484 appendDone.set(true); 5485 flushThread.interrupt(); 5486 flushThread.join(5000); // Wait up to 5 seconds for thread to stop 5487 if (flushThread.isAlive()) { 5488 LOG.warn("Flush thread did not stop within timeout for test " + method); 5489 } 5490 } 5491 } 5492 5493 /** 5494 * Test case to check put function with memstore flushing for same row, same ts 5495 */ 5496 @Test 5497 public void testPutWithMemStoreFlush() throws Exception { 5498 byte[] family = Bytes.toBytes("family"); 5499 byte[] qualifier = Bytes.toBytes("qualifier"); 5500 byte[] row = Bytes.toBytes("putRow"); 5501 byte[] value = null; 5502 this.region = initHRegion(tableName, method, CONF, family); 5503 Put put = null; 5504 Get get = null; 5505 List<Cell> kvs = null; 5506 Result res = null; 5507 5508 put = new Put(row); 5509 value = Bytes.toBytes("value0"); 5510 put.addColumn(family, qualifier, 1234567L, value); 5511 region.put(put); 5512 get = new Get(row); 5513 get.addColumn(family, qualifier); 5514 get.readAllVersions(); 5515 res = this.region.get(get); 5516 kvs = res.getColumnCells(family, qualifier); 5517 assertEquals(1, kvs.size()); 5518 assertArrayEquals(Bytes.toBytes("value0"), CellUtil.cloneValue(kvs.get(0))); 5519 5520 region.flush(true); 5521 get = new Get(row); 5522 get.addColumn(family, qualifier); 5523 get.readAllVersions(); 5524 res = this.region.get(get); 5525 kvs = res.getColumnCells(family, qualifier); 5526 assertEquals(1, kvs.size()); 5527 assertArrayEquals(Bytes.toBytes("value0"), CellUtil.cloneValue(kvs.get(0))); 5528 5529 put = new Put(row); 5530 value = Bytes.toBytes("value1"); 5531 put.addColumn(family, qualifier, 1234567L, value); 5532 region.put(put); 5533 get = new Get(row); 5534 get.addColumn(family, qualifier); 5535 get.readAllVersions(); 5536 res = this.region.get(get); 5537 kvs = res.getColumnCells(family, qualifier); 5538 assertEquals(1, kvs.size()); 5539 assertArrayEquals(Bytes.toBytes("value1"), CellUtil.cloneValue(kvs.get(0))); 5540 5541 region.flush(true); 5542 get = new Get(row); 5543 get.addColumn(family, qualifier); 5544 get.readAllVersions(); 5545 res = this.region.get(get); 5546 kvs = res.getColumnCells(family, qualifier); 5547 assertEquals(1, kvs.size()); 5548 assertArrayEquals(Bytes.toBytes("value1"), CellUtil.cloneValue(kvs.get(0))); 5549 } 5550 5551 /** 5552 * For this test,the spied {@link AsyncFSWAL} can not work properly because of a Mockito defect 5553 * that can not deal with classes which have a field of an inner class. See discussions in 5554 * HBASE-15536.When we reuse the code of {@link AsyncFSWAL} for {@link FSHLog}, this test could 5555 * not work for {@link FSHLog} also. 5556 */ 5557 @Test 5558 @Ignore 5559 public void testDurability() throws Exception { 5560 // there are 5 x 5 cases: 5561 // table durability(SYNC,FSYNC,ASYC,SKIP,USE_DEFAULT) x mutation 5562 // durability(SYNC,FSYNC,ASYC,SKIP,USE_DEFAULT) 5563 5564 // expected cases for append and sync wal 5565 durabilityTest(method, Durability.SYNC_WAL, Durability.SYNC_WAL, 0, true, true, false); 5566 durabilityTest(method, Durability.SYNC_WAL, Durability.FSYNC_WAL, 0, true, true, false); 5567 durabilityTest(method, Durability.SYNC_WAL, Durability.USE_DEFAULT, 0, true, true, false); 5568 5569 durabilityTest(method, Durability.FSYNC_WAL, Durability.SYNC_WAL, 0, true, true, false); 5570 durabilityTest(method, Durability.FSYNC_WAL, Durability.FSYNC_WAL, 0, true, true, false); 5571 durabilityTest(method, Durability.FSYNC_WAL, Durability.USE_DEFAULT, 0, true, true, false); 5572 5573 durabilityTest(method, Durability.ASYNC_WAL, Durability.SYNC_WAL, 0, true, true, false); 5574 durabilityTest(method, Durability.ASYNC_WAL, Durability.FSYNC_WAL, 0, true, true, false); 5575 5576 durabilityTest(method, Durability.SKIP_WAL, Durability.SYNC_WAL, 0, true, true, false); 5577 durabilityTest(method, Durability.SKIP_WAL, Durability.FSYNC_WAL, 0, true, true, false); 5578 5579 durabilityTest(method, Durability.USE_DEFAULT, Durability.SYNC_WAL, 0, true, true, false); 5580 durabilityTest(method, Durability.USE_DEFAULT, Durability.FSYNC_WAL, 0, true, true, false); 5581 durabilityTest(method, Durability.USE_DEFAULT, Durability.USE_DEFAULT, 0, true, true, false); 5582 5583 // expected cases for async wal 5584 durabilityTest(method, Durability.SYNC_WAL, Durability.ASYNC_WAL, 0, true, false, false); 5585 durabilityTest(method, Durability.FSYNC_WAL, Durability.ASYNC_WAL, 0, true, false, false); 5586 durabilityTest(method, Durability.ASYNC_WAL, Durability.ASYNC_WAL, 0, true, false, false); 5587 durabilityTest(method, Durability.SKIP_WAL, Durability.ASYNC_WAL, 0, true, false, false); 5588 durabilityTest(method, Durability.USE_DEFAULT, Durability.ASYNC_WAL, 0, true, false, false); 5589 durabilityTest(method, Durability.ASYNC_WAL, Durability.USE_DEFAULT, 0, true, false, false); 5590 5591 durabilityTest(method, Durability.SYNC_WAL, Durability.ASYNC_WAL, 5000, true, false, true); 5592 durabilityTest(method, Durability.FSYNC_WAL, Durability.ASYNC_WAL, 5000, true, false, true); 5593 durabilityTest(method, Durability.ASYNC_WAL, Durability.ASYNC_WAL, 5000, true, false, true); 5594 durabilityTest(method, Durability.SKIP_WAL, Durability.ASYNC_WAL, 5000, true, false, true); 5595 durabilityTest(method, Durability.USE_DEFAULT, Durability.ASYNC_WAL, 5000, true, false, true); 5596 durabilityTest(method, Durability.ASYNC_WAL, Durability.USE_DEFAULT, 5000, true, false, true); 5597 5598 // expect skip wal cases 5599 durabilityTest(method, Durability.SYNC_WAL, Durability.SKIP_WAL, 0, false, false, false); 5600 durabilityTest(method, Durability.FSYNC_WAL, Durability.SKIP_WAL, 0, false, false, false); 5601 durabilityTest(method, Durability.ASYNC_WAL, Durability.SKIP_WAL, 0, false, false, false); 5602 durabilityTest(method, Durability.SKIP_WAL, Durability.SKIP_WAL, 0, false, false, false); 5603 durabilityTest(method, Durability.USE_DEFAULT, Durability.SKIP_WAL, 0, false, false, false); 5604 durabilityTest(method, Durability.SKIP_WAL, Durability.USE_DEFAULT, 0, false, false, false); 5605 5606 } 5607 5608 private void durabilityTest(String method, Durability tableDurability, 5609 Durability mutationDurability, long timeout, boolean expectAppend, final boolean expectSync, 5610 final boolean expectSyncFromLogSyncer) throws Exception { 5611 Configuration conf = HBaseConfiguration.create(CONF); 5612 conf.setLong(AbstractFSWAL.WAL_SHUTDOWN_WAIT_TIMEOUT_MS, 60 * 60 * 1000); 5613 method = method + "_" + tableDurability.name() + "_" + mutationDurability.name(); 5614 byte[] family = Bytes.toBytes("family"); 5615 Path logDir = new Path(new Path(dir + method), "log"); 5616 final Configuration walConf = new Configuration(conf); 5617 CommonFSUtils.setRootDir(walConf, logDir); 5618 // XXX: The spied AsyncFSWAL can not work properly because of a Mockito defect that can not 5619 // deal with classes which have a field of an inner class. See discussions in HBASE-15536. 5620 walConf.set(WALFactory.WAL_PROVIDER, "filesystem"); 5621 final WALFactory wals = new WALFactory(walConf, HBaseTestingUtil.getRandomUUID().toString()); 5622 final WAL wal = spy(wals.getWAL(RegionInfoBuilder.newBuilder(tableName).build())); 5623 this.region = initHRegion(tableName, HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW, CONF, 5624 false, tableDurability, wal, new byte[][] { family }); 5625 5626 Put put = new Put(Bytes.toBytes("r1")); 5627 put.addColumn(family, Bytes.toBytes("q1"), Bytes.toBytes("v1")); 5628 put.setDurability(mutationDurability); 5629 region.put(put); 5630 5631 // verify append called or not 5632 verify(wal, expectAppend ? times(1) : never()).appendData((RegionInfo) any(), 5633 (WALKeyImpl) any(), (WALEdit) any()); 5634 5635 // verify sync called or not 5636 if (expectSync || expectSyncFromLogSyncer) { 5637 TEST_UTIL.waitFor(timeout, new Waiter.Predicate<Exception>() { 5638 @Override 5639 public boolean evaluate() throws Exception { 5640 try { 5641 if (expectSync) { 5642 verify(wal, times(1)).sync(anyLong()); // Hregion calls this one 5643 } else if (expectSyncFromLogSyncer) { 5644 verify(wal, times(1)).sync(); // wal syncer calls this one 5645 } 5646 } catch (Throwable ignore) { 5647 } 5648 return true; 5649 } 5650 }); 5651 } else { 5652 // verify(wal, never()).sync(anyLong()); 5653 verify(wal, never()).sync(); 5654 } 5655 5656 HBaseTestingUtil.closeRegionAndWAL(this.region); 5657 wals.close(); 5658 this.region = null; 5659 } 5660 5661 @Test 5662 public void testRegionReplicaSecondary() throws IOException { 5663 // create a primary region, load some data and flush 5664 // create a secondary region, and do a get against that 5665 Path rootDir = new Path(dir + name.getMethodName()); 5666 CommonFSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootDir); 5667 5668 byte[][] families = 5669 new byte[][] { Bytes.toBytes("cf1"), Bytes.toBytes("cf2"), Bytes.toBytes("cf3") }; 5670 byte[] cq = Bytes.toBytes("cq"); 5671 TableDescriptorBuilder builder = 5672 TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())); 5673 for (byte[] family : families) { 5674 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)); 5675 } 5676 TableDescriptor tableDescriptor = builder.build(); 5677 long time = EnvironmentEdgeManager.currentTime(); 5678 RegionInfo primaryHri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()) 5679 .setRegionId(time).setReplicaId(0).build(); 5680 RegionInfo secondaryHri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()) 5681 .setRegionId(time).setReplicaId(1).build(); 5682 5683 HRegion primaryRegion = null, secondaryRegion = null; 5684 5685 try { 5686 primaryRegion = HBaseTestingUtil.createRegionAndWAL(primaryHri, rootDir, 5687 TEST_UTIL.getConfiguration(), tableDescriptor); 5688 5689 // load some data 5690 putData(primaryRegion, 0, 1000, cq, families); 5691 5692 // flush region 5693 primaryRegion.flush(true); 5694 5695 // open secondary region 5696 secondaryRegion = HRegion.openHRegion(rootDir, secondaryHri, tableDescriptor, null, CONF); 5697 5698 verifyData(secondaryRegion, 0, 1000, cq, families); 5699 } finally { 5700 if (primaryRegion != null) { 5701 HBaseTestingUtil.closeRegionAndWAL(primaryRegion); 5702 } 5703 if (secondaryRegion != null) { 5704 HBaseTestingUtil.closeRegionAndWAL(secondaryRegion); 5705 } 5706 } 5707 } 5708 5709 @Test 5710 public void testRegionReplicaSecondaryIsReadOnly() throws IOException { 5711 // create a primary region, load some data and flush 5712 // create a secondary region, and do a put against that 5713 Path rootDir = new Path(dir + name.getMethodName()); 5714 CommonFSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootDir); 5715 5716 byte[][] families = 5717 new byte[][] { Bytes.toBytes("cf1"), Bytes.toBytes("cf2"), Bytes.toBytes("cf3") }; 5718 byte[] cq = Bytes.toBytes("cq"); 5719 TableDescriptorBuilder builder = 5720 TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())); 5721 for (byte[] family : families) { 5722 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)); 5723 } 5724 TableDescriptor tableDescriptor = builder.build(); 5725 long time = EnvironmentEdgeManager.currentTime(); 5726 RegionInfo primaryHri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()) 5727 .setRegionId(time).setReplicaId(0).build(); 5728 RegionInfo secondaryHri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()) 5729 .setRegionId(time).setReplicaId(1).build(); 5730 5731 HRegion primaryRegion = null, secondaryRegion = null; 5732 5733 try { 5734 primaryRegion = HBaseTestingUtil.createRegionAndWAL(primaryHri, rootDir, 5735 TEST_UTIL.getConfiguration(), tableDescriptor); 5736 5737 // load some data 5738 putData(primaryRegion, 0, 1000, cq, families); 5739 5740 // flush region 5741 primaryRegion.flush(true); 5742 5743 // open secondary region 5744 secondaryRegion = HRegion.openHRegion(rootDir, secondaryHri, tableDescriptor, null, CONF); 5745 5746 try { 5747 putData(secondaryRegion, 0, 1000, cq, families); 5748 fail("Should have thrown exception"); 5749 } catch (IOException ex) { 5750 // expected 5751 } 5752 } finally { 5753 if (primaryRegion != null) { 5754 HBaseTestingUtil.closeRegionAndWAL(primaryRegion); 5755 } 5756 if (secondaryRegion != null) { 5757 HBaseTestingUtil.closeRegionAndWAL(secondaryRegion); 5758 } 5759 } 5760 } 5761 5762 static WALFactory createWALFactory(Configuration conf, Path rootDir) throws IOException { 5763 Configuration confForWAL = new Configuration(conf); 5764 confForWAL.set(HConstants.HBASE_DIR, rootDir.toString()); 5765 return new WALFactory(confForWAL, "hregion-" + RandomStringUtils.randomNumeric(8)); 5766 } 5767 5768 @Test 5769 public void testCompactionFromPrimary() throws IOException { 5770 Path rootDir = new Path(dir + name.getMethodName()); 5771 CommonFSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootDir); 5772 5773 byte[][] families = 5774 new byte[][] { Bytes.toBytes("cf1"), Bytes.toBytes("cf2"), Bytes.toBytes("cf3") }; 5775 byte[] cq = Bytes.toBytes("cq"); 5776 TableDescriptorBuilder builder = 5777 TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())); 5778 for (byte[] family : families) { 5779 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)); 5780 } 5781 TableDescriptor tableDescriptor = builder.build(); 5782 long time = EnvironmentEdgeManager.currentTime(); 5783 RegionInfo primaryHri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()) 5784 .setRegionId(time).setReplicaId(0).build(); 5785 RegionInfo secondaryHri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()) 5786 .setRegionId(time).setReplicaId(1).build(); 5787 5788 HRegion primaryRegion = null, secondaryRegion = null; 5789 5790 try { 5791 primaryRegion = HBaseTestingUtil.createRegionAndWAL(primaryHri, rootDir, 5792 TEST_UTIL.getConfiguration(), tableDescriptor); 5793 5794 // load some data 5795 putData(primaryRegion, 0, 1000, cq, families); 5796 5797 // flush region 5798 primaryRegion.flush(true); 5799 5800 // open secondary region 5801 secondaryRegion = HRegion.openHRegion(rootDir, secondaryHri, tableDescriptor, null, CONF); 5802 5803 // move the file of the primary region to the archive, simulating a compaction 5804 Collection<HStoreFile> storeFiles = primaryRegion.getStore(families[0]).getStorefiles(); 5805 HRegionFileSystem regionFs = primaryRegion.getRegionFileSystem(); 5806 StoreFileTracker sft = StoreFileTrackerFactory.create(primaryRegion.getBaseConf(), false, 5807 StoreContext.getBuilder() 5808 .withColumnFamilyDescriptor(ColumnFamilyDescriptorBuilder.newBuilder(families[0]).build()) 5809 .withFamilyStoreDirectoryPath( 5810 new Path(regionFs.getRegionDir(), Bytes.toString(families[0]))) 5811 .withRegionFileSystem(regionFs).build()); 5812 sft.removeStoreFiles(storeFiles.stream().collect(Collectors.toList())); 5813 Collection<StoreFileInfo> storeFileInfos = sft.load(); 5814 Assert.assertTrue(storeFileInfos == null || storeFileInfos.isEmpty()); 5815 5816 verifyData(secondaryRegion, 0, 1000, cq, families); 5817 } finally { 5818 if (primaryRegion != null) { 5819 HBaseTestingUtil.closeRegionAndWAL(primaryRegion); 5820 } 5821 if (secondaryRegion != null) { 5822 HBaseTestingUtil.closeRegionAndWAL(secondaryRegion); 5823 } 5824 } 5825 } 5826 5827 private void putData(int startRow, int numRows, byte[] qf, byte[]... families) 5828 throws IOException { 5829 putData(this.region, startRow, numRows, qf, families); 5830 } 5831 5832 private void putData(HRegion region, int startRow, int numRows, byte[] qf, byte[]... families) 5833 throws IOException { 5834 putData(region, Durability.SKIP_WAL, startRow, numRows, qf, families); 5835 } 5836 5837 static void putData(HRegion region, Durability durability, int startRow, int numRows, byte[] qf, 5838 byte[]... families) throws IOException { 5839 for (int i = startRow; i < startRow + numRows; i++) { 5840 Put put = new Put(Bytes.toBytes("" + i)); 5841 put.setDurability(durability); 5842 for (byte[] family : families) { 5843 put.addColumn(family, qf, null); 5844 } 5845 region.put(put); 5846 LOG.info(put.toString()); 5847 } 5848 } 5849 5850 static void verifyData(HRegion newReg, int startRow, int numRows, byte[] qf, byte[]... families) 5851 throws IOException { 5852 for (int i = startRow; i < startRow + numRows; i++) { 5853 byte[] row = Bytes.toBytes("" + i); 5854 Get get = new Get(row); 5855 for (byte[] family : families) { 5856 get.addColumn(family, qf); 5857 } 5858 Result result = newReg.get(get); 5859 Cell[] raw = result.rawCells(); 5860 assertEquals(families.length, result.size()); 5861 for (int j = 0; j < families.length; j++) { 5862 assertTrue(CellUtil.matchingRows(raw[j], row)); 5863 assertTrue(CellUtil.matchingFamily(raw[j], families[j])); 5864 assertTrue(CellUtil.matchingQualifier(raw[j], qf)); 5865 } 5866 } 5867 } 5868 5869 static void assertGet(final HRegion r, final byte[] family, final byte[] k) throws IOException { 5870 // Now I have k, get values out and assert they are as expected. 5871 Get get = new Get(k).addFamily(family).readAllVersions(); 5872 Cell[] results = r.get(get).rawCells(); 5873 for (int j = 0; j < results.length; j++) { 5874 byte[] tmp = CellUtil.cloneValue(results[j]); 5875 // Row should be equal to value every time. 5876 assertTrue(Bytes.equals(k, tmp)); 5877 } 5878 } 5879 5880 /* 5881 * Assert first value in the passed region is <code>firstValue</code>. 5882 */ 5883 protected void assertScan(final HRegion r, final byte[] fs, final byte[] firstValue) 5884 throws IOException { 5885 byte[][] families = { fs }; 5886 Scan scan = new Scan(); 5887 for (int i = 0; i < families.length; i++) 5888 scan.addFamily(families[i]); 5889 try (InternalScanner s = r.getScanner(scan)) { 5890 List<Cell> curVals = new ArrayList<>(); 5891 boolean first = true; 5892 OUTER_LOOP: while (s.next(curVals)) { 5893 for (Cell kv : curVals) { 5894 byte[] val = CellUtil.cloneValue(kv); 5895 byte[] curval = val; 5896 if (first) { 5897 first = false; 5898 assertTrue(Bytes.compareTo(curval, firstValue) == 0); 5899 } else { 5900 // Not asserting anything. Might as well break. 5901 break OUTER_LOOP; 5902 } 5903 } 5904 } 5905 } 5906 } 5907 5908 /** 5909 * Test that we get the expected flush results back 5910 */ 5911 @Test 5912 public void testFlushResult() throws IOException { 5913 byte[] family = Bytes.toBytes("family"); 5914 5915 this.region = initHRegion(tableName, method, family); 5916 5917 // empty memstore, flush doesn't run 5918 HRegion.FlushResult fr = region.flush(true); 5919 assertFalse(fr.isFlushSucceeded()); 5920 assertFalse(fr.isCompactionNeeded()); 5921 5922 // Flush enough files to get up to the threshold, doesn't need compactions 5923 for (int i = 0; i < 2; i++) { 5924 Put put = new Put(tableName.toBytes()).addColumn(family, family, tableName.toBytes()); 5925 region.put(put); 5926 fr = region.flush(true); 5927 assertTrue(fr.isFlushSucceeded()); 5928 assertFalse(fr.isCompactionNeeded()); 5929 } 5930 5931 // Two flushes after the threshold, compactions are needed 5932 for (int i = 0; i < 2; i++) { 5933 Put put = new Put(tableName.toBytes()).addColumn(family, family, tableName.toBytes()); 5934 region.put(put); 5935 fr = region.flush(true); 5936 assertTrue(fr.isFlushSucceeded()); 5937 assertTrue(fr.isCompactionNeeded()); 5938 } 5939 } 5940 5941 protected Configuration initSplit() { 5942 // Always compact if there is more than one store file. 5943 CONF.setInt("hbase.hstore.compactionThreshold", 2); 5944 5945 CONF.setInt(HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD, 10 * 1000); 5946 5947 // Increase the amount of time between client retries 5948 CONF.setLong("hbase.client.pause", 15 * 1000); 5949 5950 // This size should make it so we always split using the addContent 5951 // below. After adding all data, the first region is 1.3M 5952 CONF.setLong(HConstants.HREGION_MAX_FILESIZE, 1024 * 128); 5953 return CONF; 5954 } 5955 5956 /** 5957 * @return A region on which you must call {@link HBaseTestingUtil#closeRegionAndWAL(HRegion)} 5958 * when done. 5959 */ 5960 protected HRegion initHRegion(TableName tableName, String callingMethod, Configuration conf, 5961 byte[]... families) throws IOException { 5962 return initHRegion(tableName, callingMethod, conf, false, families); 5963 } 5964 5965 /** 5966 * @return A region on which you must call {@link HBaseTestingUtil#closeRegionAndWAL(HRegion)} 5967 * when done. 5968 */ 5969 private HRegion initHRegion(TableName tableName, String callingMethod, Configuration conf, 5970 boolean isReadOnly, byte[]... families) throws IOException { 5971 return initHRegion(tableName, null, null, callingMethod, conf, isReadOnly, families); 5972 } 5973 5974 private HRegion initHRegion(TableName tableName, byte[] startKey, byte[] stopKey, 5975 String callingMethod, Configuration conf, boolean isReadOnly, byte[]... families) 5976 throws IOException { 5977 Path logDir = TEST_UTIL.getDataTestDirOnTestFS(callingMethod + ".log"); 5978 RegionInfo hri = 5979 RegionInfoBuilder.newBuilder(tableName).setStartKey(startKey).setEndKey(stopKey).build(); 5980 final WAL wal = HBaseTestingUtil.createWal(conf, logDir, hri); 5981 return initHRegion(tableName, startKey, stopKey, conf, isReadOnly, Durability.SYNC_WAL, wal, 5982 families); 5983 } 5984 5985 /** 5986 * @return A region on which you must call {@link HBaseTestingUtil#closeRegionAndWAL(HRegion)} 5987 * when done. 5988 */ 5989 protected HRegion initHRegion(TableName tableName, byte[] startKey, byte[] stopKey, 5990 Configuration conf, boolean isReadOnly, Durability durability, WAL wal, byte[]... families) 5991 throws IOException { 5992 ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null, 5993 MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT); 5994 return TEST_UTIL.createLocalHRegion(tableName, startKey, stopKey, conf, isReadOnly, durability, 5995 wal, families); 5996 } 5997 5998 /** 5999 * Assert that the passed in Cell has expected contents for the specified row, column & timestamp. 6000 */ 6001 private void checkOneCell(Cell kv, byte[] cf, int rowIdx, int colIdx, long ts) { 6002 String ctx = "rowIdx=" + rowIdx + "; colIdx=" + colIdx + "; ts=" + ts; 6003 assertEquals("Row mismatch which checking: " + ctx, "row:" + rowIdx, 6004 Bytes.toString(CellUtil.cloneRow(kv))); 6005 assertEquals("ColumnFamily mismatch while checking: " + ctx, Bytes.toString(cf), 6006 Bytes.toString(CellUtil.cloneFamily(kv))); 6007 assertEquals("Column qualifier mismatch while checking: " + ctx, "column:" + colIdx, 6008 Bytes.toString(CellUtil.cloneQualifier(kv))); 6009 assertEquals("Timestamp mismatch while checking: " + ctx, ts, kv.getTimestamp()); 6010 assertEquals("Value mismatch while checking: " + ctx, "value-version-" + ts, 6011 Bytes.toString(CellUtil.cloneValue(kv))); 6012 } 6013 6014 @Test 6015 public void testReverseScanner_FromMemStore_SingleCF_Normal() throws IOException { 6016 byte[] rowC = Bytes.toBytes("rowC"); 6017 byte[] rowA = Bytes.toBytes("rowA"); 6018 byte[] rowB = Bytes.toBytes("rowB"); 6019 byte[] cf = Bytes.toBytes("CF"); 6020 byte[][] families = { cf }; 6021 byte[] col = Bytes.toBytes("C"); 6022 long ts = 1; 6023 this.region = initHRegion(tableName, method, families); 6024 KeyValue kv1 = new KeyValue(rowC, cf, col, ts, KeyValue.Type.Put, null); 6025 KeyValue kv11 = new KeyValue(rowC, cf, col, ts + 1, KeyValue.Type.Put, null); 6026 KeyValue kv2 = new KeyValue(rowA, cf, col, ts, KeyValue.Type.Put, null); 6027 KeyValue kv3 = new KeyValue(rowB, cf, col, ts, KeyValue.Type.Put, null); 6028 Put put = null; 6029 put = new Put(rowC); 6030 put.add(kv1); 6031 put.add(kv11); 6032 region.put(put); 6033 put = new Put(rowA); 6034 put.add(kv2); 6035 region.put(put); 6036 put = new Put(rowB); 6037 put.add(kv3); 6038 region.put(put); 6039 6040 Scan scan = new Scan().withStartRow(rowC); 6041 scan.readVersions(5); 6042 scan.setReversed(true); 6043 try (InternalScanner scanner = region.getScanner(scan)) { 6044 List<Cell> currRow = new ArrayList<>(); 6045 boolean hasNext = scanner.next(currRow); 6046 assertEquals(2, currRow.size()); 6047 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6048 currRow.get(0).getRowLength(), rowC, 0, rowC.length)); 6049 assertTrue(hasNext); 6050 currRow.clear(); 6051 hasNext = scanner.next(currRow); 6052 assertEquals(1, currRow.size()); 6053 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6054 currRow.get(0).getRowLength(), rowB, 0, rowB.length)); 6055 assertTrue(hasNext); 6056 currRow.clear(); 6057 hasNext = scanner.next(currRow); 6058 assertEquals(1, currRow.size()); 6059 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6060 currRow.get(0).getRowLength(), rowA, 0, rowA.length)); 6061 assertFalse(hasNext); 6062 } 6063 } 6064 6065 @Test 6066 public void testReverseScanner_FromMemStore_SingleCF_LargerKey() throws IOException { 6067 byte[] rowC = Bytes.toBytes("rowC"); 6068 byte[] rowA = Bytes.toBytes("rowA"); 6069 byte[] rowB = Bytes.toBytes("rowB"); 6070 byte[] rowD = Bytes.toBytes("rowD"); 6071 byte[] cf = Bytes.toBytes("CF"); 6072 byte[][] families = { cf }; 6073 byte[] col = Bytes.toBytes("C"); 6074 long ts = 1; 6075 this.region = initHRegion(tableName, method, families); 6076 KeyValue kv1 = new KeyValue(rowC, cf, col, ts, KeyValue.Type.Put, null); 6077 KeyValue kv11 = new KeyValue(rowC, cf, col, ts + 1, KeyValue.Type.Put, null); 6078 KeyValue kv2 = new KeyValue(rowA, cf, col, ts, KeyValue.Type.Put, null); 6079 KeyValue kv3 = new KeyValue(rowB, cf, col, ts, KeyValue.Type.Put, null); 6080 Put put = null; 6081 put = new Put(rowC); 6082 put.add(kv1); 6083 put.add(kv11); 6084 region.put(put); 6085 put = new Put(rowA); 6086 put.add(kv2); 6087 region.put(put); 6088 put = new Put(rowB); 6089 put.add(kv3); 6090 region.put(put); 6091 6092 Scan scan = new Scan().withStartRow(rowD); 6093 List<Cell> currRow = new ArrayList<>(); 6094 scan.setReversed(true); 6095 scan.readVersions(5); 6096 try (InternalScanner scanner = region.getScanner(scan)) { 6097 boolean hasNext = scanner.next(currRow); 6098 assertEquals(2, currRow.size()); 6099 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6100 currRow.get(0).getRowLength(), rowC, 0, rowC.length)); 6101 assertTrue(hasNext); 6102 currRow.clear(); 6103 hasNext = scanner.next(currRow); 6104 assertEquals(1, currRow.size()); 6105 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6106 currRow.get(0).getRowLength(), rowB, 0, rowB.length)); 6107 assertTrue(hasNext); 6108 currRow.clear(); 6109 hasNext = scanner.next(currRow); 6110 assertEquals(1, currRow.size()); 6111 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6112 currRow.get(0).getRowLength(), rowA, 0, rowA.length)); 6113 assertFalse(hasNext); 6114 } 6115 } 6116 6117 @Test 6118 public void testReverseScanner_FromMemStore_SingleCF_FullScan() throws IOException { 6119 byte[] rowC = Bytes.toBytes("rowC"); 6120 byte[] rowA = Bytes.toBytes("rowA"); 6121 byte[] rowB = Bytes.toBytes("rowB"); 6122 byte[] cf = Bytes.toBytes("CF"); 6123 byte[][] families = { cf }; 6124 byte[] col = Bytes.toBytes("C"); 6125 long ts = 1; 6126 this.region = initHRegion(tableName, method, families); 6127 KeyValue kv1 = new KeyValue(rowC, cf, col, ts, KeyValue.Type.Put, null); 6128 KeyValue kv11 = new KeyValue(rowC, cf, col, ts + 1, KeyValue.Type.Put, null); 6129 KeyValue kv2 = new KeyValue(rowA, cf, col, ts, KeyValue.Type.Put, null); 6130 KeyValue kv3 = new KeyValue(rowB, cf, col, ts, KeyValue.Type.Put, null); 6131 Put put = null; 6132 put = new Put(rowC); 6133 put.add(kv1); 6134 put.add(kv11); 6135 region.put(put); 6136 put = new Put(rowA); 6137 put.add(kv2); 6138 region.put(put); 6139 put = new Put(rowB); 6140 put.add(kv3); 6141 region.put(put); 6142 Scan scan = new Scan(); 6143 List<Cell> currRow = new ArrayList<>(); 6144 scan.setReversed(true); 6145 try (InternalScanner scanner = region.getScanner(scan)) { 6146 boolean hasNext = scanner.next(currRow); 6147 assertEquals(1, currRow.size()); 6148 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6149 currRow.get(0).getRowLength(), rowC, 0, rowC.length)); 6150 assertTrue(hasNext); 6151 currRow.clear(); 6152 hasNext = scanner.next(currRow); 6153 assertEquals(1, currRow.size()); 6154 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6155 currRow.get(0).getRowLength(), rowB, 0, rowB.length)); 6156 assertTrue(hasNext); 6157 currRow.clear(); 6158 hasNext = scanner.next(currRow); 6159 assertEquals(1, currRow.size()); 6160 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6161 currRow.get(0).getRowLength(), rowA, 0, rowA.length)); 6162 assertFalse(hasNext); 6163 } 6164 } 6165 6166 @Test 6167 public void testReverseScanner_moreRowsMayExistAfter() throws IOException { 6168 // case for "INCLUDE_AND_SEEK_NEXT_ROW & SEEK_NEXT_ROW" endless loop 6169 byte[] rowA = Bytes.toBytes("rowA"); 6170 byte[] rowB = Bytes.toBytes("rowB"); 6171 byte[] rowC = Bytes.toBytes("rowC"); 6172 byte[] rowD = Bytes.toBytes("rowD"); 6173 byte[] rowE = Bytes.toBytes("rowE"); 6174 byte[] cf = Bytes.toBytes("CF"); 6175 byte[][] families = { cf }; 6176 byte[] col1 = Bytes.toBytes("col1"); 6177 byte[] col2 = Bytes.toBytes("col2"); 6178 long ts = 1; 6179 this.region = initHRegion(tableName, method, families); 6180 KeyValue kv1 = new KeyValue(rowA, cf, col1, ts, KeyValue.Type.Put, null); 6181 KeyValue kv2 = new KeyValue(rowB, cf, col1, ts, KeyValue.Type.Put, null); 6182 KeyValue kv3 = new KeyValue(rowC, cf, col1, ts, KeyValue.Type.Put, null); 6183 KeyValue kv4_1 = new KeyValue(rowD, cf, col1, ts, KeyValue.Type.Put, null); 6184 KeyValue kv4_2 = new KeyValue(rowD, cf, col2, ts, KeyValue.Type.Put, null); 6185 KeyValue kv5 = new KeyValue(rowE, cf, col1, ts, KeyValue.Type.Put, null); 6186 Put put = null; 6187 put = new Put(rowA); 6188 put.add(kv1); 6189 region.put(put); 6190 put = new Put(rowB); 6191 put.add(kv2); 6192 region.put(put); 6193 put = new Put(rowC); 6194 put.add(kv3); 6195 region.put(put); 6196 put = new Put(rowD); 6197 put.add(kv4_1); 6198 region.put(put); 6199 put = new Put(rowD); 6200 put.add(kv4_2); 6201 region.put(put); 6202 put = new Put(rowE); 6203 put.add(kv5); 6204 region.put(put); 6205 region.flush(true); 6206 Scan scan = new Scan().withStartRow(rowD).withStopRow(rowA); 6207 scan.addColumn(families[0], col1); 6208 scan.setReversed(true); 6209 List<Cell> currRow = new ArrayList<>(); 6210 try (InternalScanner scanner = region.getScanner(scan)) { 6211 boolean hasNext = scanner.next(currRow); 6212 assertEquals(1, currRow.size()); 6213 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6214 currRow.get(0).getRowLength(), rowD, 0, rowD.length)); 6215 assertTrue(hasNext); 6216 currRow.clear(); 6217 hasNext = scanner.next(currRow); 6218 assertEquals(1, currRow.size()); 6219 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6220 currRow.get(0).getRowLength(), rowC, 0, rowC.length)); 6221 assertTrue(hasNext); 6222 currRow.clear(); 6223 hasNext = scanner.next(currRow); 6224 assertEquals(1, currRow.size()); 6225 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6226 currRow.get(0).getRowLength(), rowB, 0, rowB.length)); 6227 assertFalse(hasNext); 6228 } 6229 6230 scan = new Scan().withStartRow(rowD).withStopRow(rowA); 6231 scan.addColumn(families[0], col2); 6232 scan.setReversed(true); 6233 currRow.clear(); 6234 try (InternalScanner scanner = region.getScanner(scan)) { 6235 boolean hasNext = scanner.next(currRow); 6236 assertEquals(1, currRow.size()); 6237 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6238 currRow.get(0).getRowLength(), rowD, 0, rowD.length)); 6239 assertTrue(hasNext); 6240 } 6241 } 6242 6243 @Test 6244 public void testReverseScanner_smaller_blocksize() throws IOException { 6245 // case to ensure no conflict with HFile index optimization 6246 byte[] rowA = Bytes.toBytes("rowA"); 6247 byte[] rowB = Bytes.toBytes("rowB"); 6248 byte[] rowC = Bytes.toBytes("rowC"); 6249 byte[] rowD = Bytes.toBytes("rowD"); 6250 byte[] rowE = Bytes.toBytes("rowE"); 6251 byte[] cf = Bytes.toBytes("CF"); 6252 byte[][] families = { cf }; 6253 byte[] col1 = Bytes.toBytes("col1"); 6254 byte[] col2 = Bytes.toBytes("col2"); 6255 long ts = 1; 6256 Configuration conf = new Configuration(CONF); 6257 conf.setInt("test.block.size", 1); 6258 this.region = initHRegion(tableName, method, conf, families); 6259 KeyValue kv1 = new KeyValue(rowA, cf, col1, ts, KeyValue.Type.Put, null); 6260 KeyValue kv2 = new KeyValue(rowB, cf, col1, ts, KeyValue.Type.Put, null); 6261 KeyValue kv3 = new KeyValue(rowC, cf, col1, ts, KeyValue.Type.Put, null); 6262 KeyValue kv4_1 = new KeyValue(rowD, cf, col1, ts, KeyValue.Type.Put, null); 6263 KeyValue kv4_2 = new KeyValue(rowD, cf, col2, ts, KeyValue.Type.Put, null); 6264 KeyValue kv5 = new KeyValue(rowE, cf, col1, ts, KeyValue.Type.Put, null); 6265 Put put = null; 6266 put = new Put(rowA); 6267 put.add(kv1); 6268 region.put(put); 6269 put = new Put(rowB); 6270 put.add(kv2); 6271 region.put(put); 6272 put = new Put(rowC); 6273 put.add(kv3); 6274 region.put(put); 6275 put = new Put(rowD); 6276 put.add(kv4_1); 6277 region.put(put); 6278 put = new Put(rowD); 6279 put.add(kv4_2); 6280 region.put(put); 6281 put = new Put(rowE); 6282 put.add(kv5); 6283 region.put(put); 6284 region.flush(true); 6285 Scan scan = new Scan().withStartRow(rowD).withStopRow(rowA); 6286 scan.addColumn(families[0], col1); 6287 scan.setReversed(true); 6288 List<Cell> currRow = new ArrayList<>(); 6289 try (InternalScanner scanner = region.getScanner(scan)) { 6290 boolean hasNext = scanner.next(currRow); 6291 assertEquals(1, currRow.size()); 6292 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6293 currRow.get(0).getRowLength(), rowD, 0, rowD.length)); 6294 assertTrue(hasNext); 6295 currRow.clear(); 6296 hasNext = scanner.next(currRow); 6297 assertEquals(1, currRow.size()); 6298 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6299 currRow.get(0).getRowLength(), rowC, 0, rowC.length)); 6300 assertTrue(hasNext); 6301 currRow.clear(); 6302 hasNext = scanner.next(currRow); 6303 assertEquals(1, currRow.size()); 6304 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6305 currRow.get(0).getRowLength(), rowB, 0, rowB.length)); 6306 assertFalse(hasNext); 6307 } 6308 6309 scan = new Scan().withStartRow(rowD).withStopRow(rowA); 6310 scan.addColumn(families[0], col2); 6311 scan.setReversed(true); 6312 currRow.clear(); 6313 try (InternalScanner scanner = region.getScanner(scan)) { 6314 boolean hasNext = scanner.next(currRow); 6315 assertEquals(1, currRow.size()); 6316 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6317 currRow.get(0).getRowLength(), rowD, 0, rowD.length)); 6318 assertTrue(hasNext); 6319 } 6320 } 6321 6322 @Test 6323 public void testReverseScanner_FromMemStoreAndHFiles_MultiCFs1() throws IOException { 6324 byte[] row0 = Bytes.toBytes("row0"); // 1 kv 6325 byte[] row1 = Bytes.toBytes("row1"); // 2 kv 6326 byte[] row2 = Bytes.toBytes("row2"); // 4 kv 6327 byte[] row3 = Bytes.toBytes("row3"); // 2 kv 6328 byte[] row4 = Bytes.toBytes("row4"); // 5 kv 6329 byte[] row5 = Bytes.toBytes("row5"); // 2 kv 6330 byte[] cf1 = Bytes.toBytes("CF1"); 6331 byte[] cf2 = Bytes.toBytes("CF2"); 6332 byte[] cf3 = Bytes.toBytes("CF3"); 6333 byte[][] families = { cf1, cf2, cf3 }; 6334 byte[] col = Bytes.toBytes("C"); 6335 long ts = 1; 6336 Configuration conf = new Configuration(CONF); 6337 // disable compactions in this test. 6338 conf.setInt("hbase.hstore.compactionThreshold", 10000); 6339 this.region = initHRegion(tableName, method, conf, families); 6340 // kv naming style: kv(row number) totalKvCountInThisRow seq no 6341 KeyValue kv0_1_1 = new KeyValue(row0, cf1, col, ts, KeyValue.Type.Put, null); 6342 KeyValue kv1_2_1 = new KeyValue(row1, cf2, col, ts, KeyValue.Type.Put, null); 6343 KeyValue kv1_2_2 = new KeyValue(row1, cf1, col, ts + 1, KeyValue.Type.Put, null); 6344 KeyValue kv2_4_1 = new KeyValue(row2, cf2, col, ts, KeyValue.Type.Put, null); 6345 KeyValue kv2_4_2 = new KeyValue(row2, cf1, col, ts, KeyValue.Type.Put, null); 6346 KeyValue kv2_4_3 = new KeyValue(row2, cf3, col, ts, KeyValue.Type.Put, null); 6347 KeyValue kv2_4_4 = new KeyValue(row2, cf1, col, ts + 4, KeyValue.Type.Put, null); 6348 KeyValue kv3_2_1 = new KeyValue(row3, cf2, col, ts, KeyValue.Type.Put, null); 6349 KeyValue kv3_2_2 = new KeyValue(row3, cf1, col, ts + 4, KeyValue.Type.Put, null); 6350 KeyValue kv4_5_1 = new KeyValue(row4, cf1, col, ts, KeyValue.Type.Put, null); 6351 KeyValue kv4_5_2 = new KeyValue(row4, cf3, col, ts, KeyValue.Type.Put, null); 6352 KeyValue kv4_5_3 = new KeyValue(row4, cf3, col, ts + 5, KeyValue.Type.Put, null); 6353 KeyValue kv4_5_4 = new KeyValue(row4, cf2, col, ts, KeyValue.Type.Put, null); 6354 KeyValue kv4_5_5 = new KeyValue(row4, cf1, col, ts + 3, KeyValue.Type.Put, null); 6355 KeyValue kv5_2_1 = new KeyValue(row5, cf2, col, ts, KeyValue.Type.Put, null); 6356 KeyValue kv5_2_2 = new KeyValue(row5, cf3, col, ts, KeyValue.Type.Put, null); 6357 // hfiles(cf1/cf2) :"row1"(1 kv) / "row2"(1 kv) / "row4"(2 kv) 6358 Put put = null; 6359 put = new Put(row1); 6360 put.add(kv1_2_1); 6361 region.put(put); 6362 put = new Put(row2); 6363 put.add(kv2_4_1); 6364 region.put(put); 6365 put = new Put(row4); 6366 put.add(kv4_5_4); 6367 put.add(kv4_5_5); 6368 region.put(put); 6369 region.flush(true); 6370 // hfiles(cf1/cf3) : "row1" (1 kvs) / "row2" (1 kv) / "row4" (2 kv) 6371 put = new Put(row4); 6372 put.add(kv4_5_1); 6373 put.add(kv4_5_3); 6374 region.put(put); 6375 put = new Put(row1); 6376 put.add(kv1_2_2); 6377 region.put(put); 6378 put = new Put(row2); 6379 put.add(kv2_4_4); 6380 region.put(put); 6381 region.flush(true); 6382 // hfiles(cf1/cf3) : "row2"(2 kv) / "row3"(1 kvs) / "row4" (1 kv) 6383 put = new Put(row4); 6384 put.add(kv4_5_2); 6385 region.put(put); 6386 put = new Put(row2); 6387 put.add(kv2_4_2); 6388 put.add(kv2_4_3); 6389 region.put(put); 6390 put = new Put(row3); 6391 put.add(kv3_2_2); 6392 region.put(put); 6393 region.flush(true); 6394 // memstore(cf1/cf2/cf3) : "row0" (1 kvs) / "row3" ( 1 kv) / "row5" (max) 6395 // ( 2 kv) 6396 put = new Put(row0); 6397 put.add(kv0_1_1); 6398 region.put(put); 6399 put = new Put(row3); 6400 put.add(kv3_2_1); 6401 region.put(put); 6402 put = new Put(row5); 6403 put.add(kv5_2_1); 6404 put.add(kv5_2_2); 6405 region.put(put); 6406 // scan range = ["row4", min), skip the max "row5" 6407 Scan scan = new Scan().withStartRow(row4); 6408 scan.readVersions(5); 6409 scan.setBatch(3); 6410 scan.setReversed(true); 6411 try (InternalScanner scanner = region.getScanner(scan)) { 6412 List<Cell> currRow = new ArrayList<>(); 6413 boolean hasNext = false; 6414 // 1. scan out "row4" (5 kvs), "row5" can't be scanned out since not 6415 // included in scan range 6416 // "row4" takes 2 next() calls since batch=3 6417 hasNext = scanner.next(currRow); 6418 assertEquals(3, currRow.size()); 6419 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6420 currRow.get(0).getRowLength(), row4, 0, row4.length)); 6421 assertTrue(hasNext); 6422 currRow.clear(); 6423 hasNext = scanner.next(currRow); 6424 assertEquals(2, currRow.size()); 6425 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6426 currRow.get(0).getRowLength(), row4, 0, row4.length)); 6427 assertTrue(hasNext); 6428 // 2. scan out "row3" (2 kv) 6429 currRow.clear(); 6430 hasNext = scanner.next(currRow); 6431 assertEquals(2, currRow.size()); 6432 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6433 currRow.get(0).getRowLength(), row3, 0, row3.length)); 6434 assertTrue(hasNext); 6435 // 3. scan out "row2" (4 kvs) 6436 // "row2" takes 2 next() calls since batch=3 6437 currRow.clear(); 6438 hasNext = scanner.next(currRow); 6439 assertEquals(3, currRow.size()); 6440 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6441 currRow.get(0).getRowLength(), row2, 0, row2.length)); 6442 assertTrue(hasNext); 6443 currRow.clear(); 6444 hasNext = scanner.next(currRow); 6445 assertEquals(1, currRow.size()); 6446 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6447 currRow.get(0).getRowLength(), row2, 0, row2.length)); 6448 assertTrue(hasNext); 6449 // 4. scan out "row1" (2 kv) 6450 currRow.clear(); 6451 hasNext = scanner.next(currRow); 6452 assertEquals(2, currRow.size()); 6453 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6454 currRow.get(0).getRowLength(), row1, 0, row1.length)); 6455 assertTrue(hasNext); 6456 // 5. scan out "row0" (1 kv) 6457 currRow.clear(); 6458 hasNext = scanner.next(currRow); 6459 assertEquals(1, currRow.size()); 6460 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6461 currRow.get(0).getRowLength(), row0, 0, row0.length)); 6462 assertFalse(hasNext); 6463 } 6464 } 6465 6466 @Test 6467 public void testReverseScanner_FromMemStoreAndHFiles_MultiCFs2() throws IOException { 6468 byte[] row1 = Bytes.toBytes("row1"); 6469 byte[] row2 = Bytes.toBytes("row2"); 6470 byte[] row3 = Bytes.toBytes("row3"); 6471 byte[] row4 = Bytes.toBytes("row4"); 6472 byte[] cf1 = Bytes.toBytes("CF1"); 6473 byte[] cf2 = Bytes.toBytes("CF2"); 6474 byte[] cf3 = Bytes.toBytes("CF3"); 6475 byte[] cf4 = Bytes.toBytes("CF4"); 6476 byte[][] families = { cf1, cf2, cf3, cf4 }; 6477 byte[] col = Bytes.toBytes("C"); 6478 long ts = 1; 6479 Configuration conf = new Configuration(CONF); 6480 // disable compactions in this test. 6481 conf.setInt("hbase.hstore.compactionThreshold", 10000); 6482 this.region = initHRegion(tableName, method, conf, families); 6483 KeyValue kv1 = new KeyValue(row1, cf1, col, ts, KeyValue.Type.Put, null); 6484 KeyValue kv2 = new KeyValue(row2, cf2, col, ts, KeyValue.Type.Put, null); 6485 KeyValue kv3 = new KeyValue(row3, cf3, col, ts, KeyValue.Type.Put, null); 6486 KeyValue kv4 = new KeyValue(row4, cf4, col, ts, KeyValue.Type.Put, null); 6487 // storefile1 6488 Put put = new Put(row1); 6489 put.add(kv1); 6490 region.put(put); 6491 region.flush(true); 6492 // storefile2 6493 put = new Put(row2); 6494 put.add(kv2); 6495 region.put(put); 6496 region.flush(true); 6497 // storefile3 6498 put = new Put(row3); 6499 put.add(kv3); 6500 region.put(put); 6501 region.flush(true); 6502 // memstore 6503 put = new Put(row4); 6504 put.add(kv4); 6505 region.put(put); 6506 // scan range = ["row4", min) 6507 Scan scan = new Scan().withStartRow(row4); 6508 scan.setReversed(true); 6509 scan.setBatch(10); 6510 try (InternalScanner scanner = region.getScanner(scan)) { 6511 List<Cell> currRow = new ArrayList<>(); 6512 boolean hasNext = scanner.next(currRow); 6513 assertEquals(1, currRow.size()); 6514 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6515 currRow.get(0).getRowLength(), row4, 0, row4.length)); 6516 assertTrue(hasNext); 6517 currRow.clear(); 6518 hasNext = scanner.next(currRow); 6519 assertEquals(1, currRow.size()); 6520 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6521 currRow.get(0).getRowLength(), row3, 0, row3.length)); 6522 assertTrue(hasNext); 6523 currRow.clear(); 6524 hasNext = scanner.next(currRow); 6525 assertEquals(1, currRow.size()); 6526 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6527 currRow.get(0).getRowLength(), row2, 0, row2.length)); 6528 assertTrue(hasNext); 6529 currRow.clear(); 6530 hasNext = scanner.next(currRow); 6531 assertEquals(1, currRow.size()); 6532 assertTrue(Bytes.equals(currRow.get(0).getRowArray(), currRow.get(0).getRowOffset(), 6533 currRow.get(0).getRowLength(), row1, 0, row1.length)); 6534 assertFalse(hasNext); 6535 } 6536 } 6537 6538 /** 6539 * Test for HBASE-14497: Reverse Scan threw StackOverflow caused by readPt checking 6540 */ 6541 @Test 6542 public void testReverseScanner_StackOverflow() throws IOException { 6543 byte[] cf1 = Bytes.toBytes("CF1"); 6544 byte[][] families = { cf1 }; 6545 byte[] col = Bytes.toBytes("C"); 6546 Configuration conf = new Configuration(CONF); 6547 this.region = initHRegion(tableName, method, conf, families); 6548 // setup with one storefile and one memstore, to create scanner and get an earlier readPt 6549 Put put = new Put(Bytes.toBytes("19998")); 6550 put.addColumn(cf1, col, Bytes.toBytes("val")); 6551 region.put(put); 6552 region.flushcache(true, true, FlushLifeCycleTracker.DUMMY); 6553 Put put2 = new Put(Bytes.toBytes("19997")); 6554 put2.addColumn(cf1, col, Bytes.toBytes("val")); 6555 region.put(put2); 6556 6557 Scan scan = new Scan().withStartRow(Bytes.toBytes("19998")); 6558 scan.setReversed(true); 6559 try (InternalScanner scanner = region.getScanner(scan)) { 6560 // create one storefile contains many rows will be skipped 6561 // to check StoreFileScanner.seekToPreviousRow 6562 for (int i = 10000; i < 20000; i++) { 6563 Put p = new Put(Bytes.toBytes("" + i)); 6564 p.addColumn(cf1, col, Bytes.toBytes("" + i)); 6565 region.put(p); 6566 } 6567 region.flushcache(true, true, FlushLifeCycleTracker.DUMMY); 6568 6569 // create one memstore contains many rows will be skipped 6570 // to check MemStoreScanner.seekToPreviousRow 6571 for (int i = 10000; i < 20000; i++) { 6572 Put p = new Put(Bytes.toBytes("" + i)); 6573 p.addColumn(cf1, col, Bytes.toBytes("" + i)); 6574 region.put(p); 6575 } 6576 6577 List<Cell> currRow = new ArrayList<>(); 6578 boolean hasNext; 6579 do { 6580 hasNext = scanner.next(currRow); 6581 } while (hasNext); 6582 assertEquals(2, currRow.size()); 6583 assertEquals("19998", Bytes.toString(currRow.get(0).getRowArray(), 6584 currRow.get(0).getRowOffset(), currRow.get(0).getRowLength())); 6585 assertEquals("19997", Bytes.toString(currRow.get(1).getRowArray(), 6586 currRow.get(1).getRowOffset(), currRow.get(1).getRowLength())); 6587 } 6588 } 6589 6590 @Test 6591 public void testReverseScanShouldNotScanMemstoreIfReadPtLesser() throws Exception { 6592 byte[] cf1 = Bytes.toBytes("CF1"); 6593 byte[][] families = { cf1 }; 6594 byte[] col = Bytes.toBytes("C"); 6595 this.region = initHRegion(tableName, method, CONF, families); 6596 // setup with one storefile and one memstore, to create scanner and get an earlier readPt 6597 Put put = new Put(Bytes.toBytes("19996")); 6598 put.addColumn(cf1, col, Bytes.toBytes("val")); 6599 region.put(put); 6600 Put put2 = new Put(Bytes.toBytes("19995")); 6601 put2.addColumn(cf1, col, Bytes.toBytes("val")); 6602 region.put(put2); 6603 // create a reverse scan 6604 Scan scan = new Scan().withStartRow(Bytes.toBytes("19996")); 6605 scan.setReversed(true); 6606 try (RegionScannerImpl scanner = region.getScanner(scan)) { 6607 // flush the cache. This will reset the store scanner 6608 region.flushcache(true, true, FlushLifeCycleTracker.DUMMY); 6609 6610 // create one memstore contains many rows will be skipped 6611 // to check MemStoreScanner.seekToPreviousRow 6612 for (int i = 10000; i < 20000; i++) { 6613 Put p = new Put(Bytes.toBytes("" + i)); 6614 p.addColumn(cf1, col, Bytes.toBytes("" + i)); 6615 region.put(p); 6616 } 6617 List<Cell> currRow = new ArrayList<>(); 6618 boolean hasNext; 6619 boolean assertDone = false; 6620 do { 6621 hasNext = scanner.next(currRow); 6622 // With HBASE-15871, after the scanner is reset the memstore scanner should not be 6623 // added here 6624 if (!assertDone) { 6625 StoreScanner current = (StoreScanner) (scanner.storeHeap).getCurrentForTesting(); 6626 List<KeyValueScanner> scanners = current.getAllScannersForTesting(); 6627 assertEquals("There should be only one scanner the store file scanner", 1, 6628 scanners.size()); 6629 assertDone = true; 6630 } 6631 } while (hasNext); 6632 assertEquals(2, currRow.size()); 6633 assertEquals("19996", Bytes.toString(currRow.get(0).getRowArray(), 6634 currRow.get(0).getRowOffset(), currRow.get(0).getRowLength())); 6635 assertEquals("19995", Bytes.toString(currRow.get(1).getRowArray(), 6636 currRow.get(1).getRowOffset(), currRow.get(1).getRowLength())); 6637 } 6638 } 6639 6640 @Test 6641 public void testReverseScanWhenPutCellsAfterOpenReverseScan() throws Exception { 6642 byte[] cf1 = Bytes.toBytes("CF1"); 6643 byte[][] families = { cf1 }; 6644 byte[] col = Bytes.toBytes("C"); 6645 6646 this.region = initHRegion(tableName, method, CONF, families); 6647 6648 Put put = new Put(Bytes.toBytes("199996")); 6649 put.addColumn(cf1, col, Bytes.toBytes("val")); 6650 region.put(put); 6651 Put put2 = new Put(Bytes.toBytes("199995")); 6652 put2.addColumn(cf1, col, Bytes.toBytes("val")); 6653 region.put(put2); 6654 6655 // Create a reverse scan 6656 Scan scan = new Scan().withStartRow(Bytes.toBytes("199996")); 6657 scan.setReversed(true); 6658 try (RegionScannerImpl scanner = region.getScanner(scan)) { 6659 // Put a lot of cells that have sequenceIDs grater than the readPt of the reverse scan 6660 for (int i = 100000; i < 200000; i++) { 6661 Put p = new Put(Bytes.toBytes("" + i)); 6662 p.addColumn(cf1, col, Bytes.toBytes("" + i)); 6663 region.put(p); 6664 } 6665 List<Cell> currRow = new ArrayList<>(); 6666 boolean hasNext; 6667 do { 6668 hasNext = scanner.next(currRow); 6669 } while (hasNext); 6670 6671 assertEquals(2, currRow.size()); 6672 assertEquals("199996", Bytes.toString(currRow.get(0).getRowArray(), 6673 currRow.get(0).getRowOffset(), currRow.get(0).getRowLength())); 6674 assertEquals("199995", Bytes.toString(currRow.get(1).getRowArray(), 6675 currRow.get(1).getRowOffset(), currRow.get(1).getRowLength())); 6676 } 6677 } 6678 6679 @Test 6680 public void testWriteRequestsCounter() throws IOException { 6681 byte[] fam = Bytes.toBytes("info"); 6682 byte[][] families = { fam }; 6683 this.region = initHRegion(tableName, method, CONF, families); 6684 6685 Assert.assertEquals(0L, region.getWriteRequestsCount()); 6686 6687 Put put = new Put(row); 6688 put.addColumn(fam, fam, fam); 6689 6690 Assert.assertEquals(0L, region.getWriteRequestsCount()); 6691 region.put(put); 6692 Assert.assertEquals(1L, region.getWriteRequestsCount()); 6693 region.put(put); 6694 Assert.assertEquals(2L, region.getWriteRequestsCount()); 6695 region.put(put); 6696 Assert.assertEquals(3L, region.getWriteRequestsCount()); 6697 6698 region.delete(new Delete(row)); 6699 Assert.assertEquals(4L, region.getWriteRequestsCount()); 6700 } 6701 6702 @Test 6703 public void testOpenRegionWrittenToWAL() throws Exception { 6704 final ServerName serverName = ServerName.valueOf(name.getMethodName(), 100, 42); 6705 final RegionServerServices rss = spy(TEST_UTIL.createMockRegionServerService(serverName)); 6706 6707 TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 6708 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam1)) 6709 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam2)).build(); 6710 RegionInfo hri = RegionInfoBuilder.newBuilder(htd.getTableName()).build(); 6711 6712 // open the region w/o rss and wal and flush some files 6713 region = HBaseTestingUtil.createRegionAndWAL(hri, TEST_UTIL.getDataTestDir(), 6714 TEST_UTIL.getConfiguration(), htd); 6715 assertNotNull(region); 6716 6717 // create a file in fam1 for the region before opening in OpenRegionHandler 6718 region.put(new Put(Bytes.toBytes("a")).addColumn(fam1, fam1, fam1)); 6719 region.flush(true); 6720 HBaseTestingUtil.closeRegionAndWAL(region); 6721 6722 ArgumentCaptor<WALEdit> editCaptor = ArgumentCaptor.forClass(WALEdit.class); 6723 6724 // capture append() calls 6725 WAL wal = mockWAL(); 6726 when(rss.getWAL(any(RegionInfo.class))).thenReturn(wal); 6727 6728 region = 6729 HRegion.openHRegion(hri, htd, rss.getWAL(hri), TEST_UTIL.getConfiguration(), rss, null); 6730 6731 verify(wal, times(1)).appendMarker(any(RegionInfo.class), any(WALKeyImpl.class), 6732 editCaptor.capture()); 6733 6734 WALEdit edit = editCaptor.getValue(); 6735 assertNotNull(edit); 6736 assertNotNull(edit.getCells()); 6737 assertEquals(1, edit.getCells().size()); 6738 RegionEventDescriptor desc = WALEdit.getRegionEventDescriptor(edit.getCells().get(0)); 6739 assertNotNull(desc); 6740 6741 LOG.info("RegionEventDescriptor from WAL: " + desc); 6742 6743 assertEquals(RegionEventDescriptor.EventType.REGION_OPEN, desc.getEventType()); 6744 assertTrue(Bytes.equals(desc.getTableName().toByteArray(), htd.getTableName().toBytes())); 6745 assertTrue( 6746 Bytes.equals(desc.getEncodedRegionName().toByteArray(), hri.getEncodedNameAsBytes())); 6747 assertTrue(desc.getLogSequenceNumber() > 0); 6748 assertEquals(serverName, ProtobufUtil.toServerName(desc.getServer())); 6749 assertEquals(2, desc.getStoresCount()); 6750 6751 StoreDescriptor store = desc.getStores(0); 6752 assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), fam1)); 6753 assertEquals(store.getStoreHomeDir(), Bytes.toString(fam1)); 6754 assertEquals(1, store.getStoreFileCount()); // 1store file 6755 assertFalse(store.getStoreFile(0).contains("/")); // ensure path is relative 6756 6757 store = desc.getStores(1); 6758 assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), fam2)); 6759 assertEquals(store.getStoreHomeDir(), Bytes.toString(fam2)); 6760 assertEquals(0, store.getStoreFileCount()); // no store files 6761 } 6762 6763 // Helper for test testOpenRegionWrittenToWALForLogReplay 6764 static class HRegionWithSeqId extends HRegion { 6765 public HRegionWithSeqId(final Path tableDir, final WAL wal, final FileSystem fs, 6766 final Configuration confParam, final RegionInfo regionInfo, final TableDescriptor htd, 6767 final RegionServerServices rsServices) { 6768 super(tableDir, wal, fs, confParam, regionInfo, htd, rsServices); 6769 } 6770 6771 @Override 6772 protected long getNextSequenceId(WAL wal) throws IOException { 6773 return 42; 6774 } 6775 } 6776 6777 @Test 6778 public void testFlushedFileWithNoTags() throws Exception { 6779 final TableName tableName = TableName.valueOf(name.getMethodName()); 6780 TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName) 6781 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam1)).build(); 6782 RegionInfo info = RegionInfoBuilder.newBuilder(tableName).build(); 6783 Path path = TEST_UTIL.getDataTestDir(getClass().getSimpleName()); 6784 region = HBaseTestingUtil.createRegionAndWAL(info, path, TEST_UTIL.getConfiguration(), 6785 tableDescriptor); 6786 Put put = new Put(Bytes.toBytes("a-b-0-0")); 6787 put.addColumn(fam1, qual1, Bytes.toBytes("c1-value")); 6788 region.put(put); 6789 region.flush(true); 6790 HStore store = region.getStore(fam1); 6791 Collection<HStoreFile> storefiles = store.getStorefiles(); 6792 for (HStoreFile sf : storefiles) { 6793 assertFalse("Tags should not be present ", 6794 sf.getReader().getHFileReader().getFileContext().isIncludesTags()); 6795 } 6796 } 6797 6798 /** 6799 * Utility method to setup a WAL mock. 6800 * <p/> 6801 * Needs to do the bit where we close latch on the WALKeyImpl on append else test hangs. 6802 * @return a mock WAL 6803 */ 6804 private WAL mockWAL() throws IOException { 6805 WAL wal = mock(WAL.class); 6806 when(wal.appendData(any(RegionInfo.class), any(WALKeyImpl.class), any(WALEdit.class))) 6807 .thenAnswer(new Answer<Long>() { 6808 @Override 6809 public Long answer(InvocationOnMock invocation) throws Throwable { 6810 WALKeyImpl key = invocation.getArgument(1); 6811 MultiVersionConcurrencyControl.WriteEntry we = key.getMvcc().begin(); 6812 key.setWriteEntry(we); 6813 return 1L; 6814 } 6815 }); 6816 when(wal.appendMarker(any(RegionInfo.class), any(WALKeyImpl.class), any(WALEdit.class))) 6817 .thenAnswer(new Answer<Long>() { 6818 @Override 6819 public Long answer(InvocationOnMock invocation) throws Throwable { 6820 WALKeyImpl key = invocation.getArgument(1); 6821 MultiVersionConcurrencyControl.WriteEntry we = key.getMvcc().begin(); 6822 key.setWriteEntry(we); 6823 return 1L; 6824 } 6825 }); 6826 return wal; 6827 } 6828 6829 @Test 6830 public void testCloseRegionWrittenToWAL() throws Exception { 6831 Path rootDir = new Path(dir + name.getMethodName()); 6832 CommonFSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootDir); 6833 6834 final ServerName serverName = ServerName.valueOf("testCloseRegionWrittenToWAL", 100, 42); 6835 final RegionServerServices rss = spy(TEST_UTIL.createMockRegionServerService(serverName)); 6836 6837 TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 6838 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam1)) 6839 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam2)).build(); 6840 RegionInfo hri = RegionInfoBuilder.newBuilder(htd.getTableName()).build(); 6841 6842 ArgumentCaptor<WALEdit> editCaptor = ArgumentCaptor.forClass(WALEdit.class); 6843 6844 // capture append() calls 6845 WAL wal = mockWAL(); 6846 when(rss.getWAL(any(RegionInfo.class))).thenReturn(wal); 6847 6848 // create the region 6849 region = HBaseTestingUtil.createRegionAndWAL(hri, rootDir, CONF, htd); 6850 HBaseTestingUtil.closeRegionAndWAL(region); 6851 region = null; 6852 // open the region first and then close it 6853 HRegion.openHRegion(hri, htd, rss.getWAL(hri), TEST_UTIL.getConfiguration(), rss, null).close(); 6854 6855 // 2 times, one for region open, the other close region 6856 verify(wal, times(2)).appendMarker(any(RegionInfo.class), (WALKeyImpl) any(WALKeyImpl.class), 6857 editCaptor.capture()); 6858 6859 WALEdit edit = editCaptor.getAllValues().get(1); 6860 assertNotNull(edit); 6861 assertNotNull(edit.getCells()); 6862 assertEquals(1, edit.getCells().size()); 6863 RegionEventDescriptor desc = WALEdit.getRegionEventDescriptor(edit.getCells().get(0)); 6864 assertNotNull(desc); 6865 6866 LOG.info("RegionEventDescriptor from WAL: " + desc); 6867 6868 assertEquals(RegionEventDescriptor.EventType.REGION_CLOSE, desc.getEventType()); 6869 assertTrue(Bytes.equals(desc.getTableName().toByteArray(), htd.getTableName().toBytes())); 6870 assertTrue( 6871 Bytes.equals(desc.getEncodedRegionName().toByteArray(), hri.getEncodedNameAsBytes())); 6872 assertTrue(desc.getLogSequenceNumber() > 0); 6873 assertEquals(serverName, ProtobufUtil.toServerName(desc.getServer())); 6874 assertEquals(2, desc.getStoresCount()); 6875 6876 StoreDescriptor store = desc.getStores(0); 6877 assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), fam1)); 6878 assertEquals(store.getStoreHomeDir(), Bytes.toString(fam1)); 6879 assertEquals(0, store.getStoreFileCount()); // no store files 6880 6881 store = desc.getStores(1); 6882 assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), fam2)); 6883 assertEquals(store.getStoreHomeDir(), Bytes.toString(fam2)); 6884 assertEquals(0, store.getStoreFileCount()); // no store files 6885 } 6886 6887 /** 6888 * Test RegionTooBusyException thrown when region is busy 6889 */ 6890 @Test 6891 public void testRegionTooBusy() throws IOException { 6892 byte[] family = Bytes.toBytes("family"); 6893 long defaultBusyWaitDuration = 6894 CONF.getLong("hbase.busy.wait.duration", HRegion.DEFAULT_BUSY_WAIT_DURATION); 6895 CONF.setLong("hbase.busy.wait.duration", 1000); 6896 region = initHRegion(tableName, method, CONF, family); 6897 final AtomicBoolean stopped = new AtomicBoolean(true); 6898 Thread t = new Thread(new Runnable() { 6899 @Override 6900 public void run() { 6901 try { 6902 region.lock.writeLock().lock(); 6903 stopped.set(false); 6904 while (!stopped.get()) { 6905 Thread.sleep(100); 6906 } 6907 } catch (InterruptedException ie) { 6908 } finally { 6909 region.lock.writeLock().unlock(); 6910 } 6911 } 6912 }); 6913 t.start(); 6914 Get get = new Get(row); 6915 try { 6916 while (stopped.get()) { 6917 Thread.sleep(100); 6918 } 6919 region.get(get); 6920 fail("Should throw RegionTooBusyException"); 6921 } catch (InterruptedException ie) { 6922 fail("test interrupted"); 6923 } catch (RegionTooBusyException e) { 6924 // Good, expected 6925 } finally { 6926 stopped.set(true); 6927 try { 6928 t.join(); 6929 } catch (Throwable e) { 6930 } 6931 6932 HBaseTestingUtil.closeRegionAndWAL(region); 6933 region = null; 6934 CONF.setLong("hbase.busy.wait.duration", defaultBusyWaitDuration); 6935 } 6936 } 6937 6938 @Test 6939 public void testCellTTLs() throws IOException { 6940 IncrementingEnvironmentEdge edge = new IncrementingEnvironmentEdge(); 6941 EnvironmentEdgeManager.injectEdge(edge); 6942 6943 final byte[] row = Bytes.toBytes("testRow"); 6944 final byte[] q1 = Bytes.toBytes("q1"); 6945 final byte[] q2 = Bytes.toBytes("q2"); 6946 final byte[] q3 = Bytes.toBytes("q3"); 6947 final byte[] q4 = Bytes.toBytes("q4"); 6948 6949 // 10 seconds 6950 TableDescriptor tableDescriptor = 6951 TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 6952 .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(fam1).setTimeToLive(10).build()) 6953 .build(); 6954 6955 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 6956 conf.setInt(HFile.FORMAT_VERSION_KEY, HFile.MIN_FORMAT_VERSION_WITH_TAGS); 6957 6958 region = HBaseTestingUtil.createRegionAndWAL( 6959 RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build(), 6960 TEST_UTIL.getDataTestDir(), conf, tableDescriptor); 6961 assertNotNull(region); 6962 long now = EnvironmentEdgeManager.currentTime(); 6963 // Add a cell that will expire in 5 seconds via cell TTL 6964 region.put(new Put(row).add(new KeyValue(row, fam1, q1, now, HConstants.EMPTY_BYTE_ARRAY, 6965 new ArrayBackedTag[] { 6966 // TTL tags specify ts in milliseconds 6967 new ArrayBackedTag(TagType.TTL_TAG_TYPE, Bytes.toBytes(5000L)) }))); 6968 // Add a cell that will expire after 10 seconds via family setting 6969 region.put(new Put(row).addColumn(fam1, q2, now, HConstants.EMPTY_BYTE_ARRAY)); 6970 // Add a cell that will expire in 15 seconds via cell TTL 6971 region.put(new Put(row).add(new KeyValue(row, fam1, q3, now + 10000 - 1, 6972 HConstants.EMPTY_BYTE_ARRAY, new ArrayBackedTag[] { 6973 // TTL tags specify ts in milliseconds 6974 new ArrayBackedTag(TagType.TTL_TAG_TYPE, Bytes.toBytes(5000L)) }))); 6975 // Add a cell that will expire in 20 seconds via family setting 6976 region.put(new Put(row).addColumn(fam1, q4, now + 10000 - 1, HConstants.EMPTY_BYTE_ARRAY)); 6977 6978 // Flush so we are sure store scanning gets this right 6979 region.flush(true); 6980 6981 // A query at time T+0 should return all cells 6982 Result r = region.get(new Get(row)); 6983 assertNotNull(r.getValue(fam1, q1)); 6984 assertNotNull(r.getValue(fam1, q2)); 6985 assertNotNull(r.getValue(fam1, q3)); 6986 assertNotNull(r.getValue(fam1, q4)); 6987 6988 // Increment time to T+5 seconds 6989 edge.incrementTime(5000); 6990 6991 r = region.get(new Get(row)); 6992 assertNull(r.getValue(fam1, q1)); 6993 assertNotNull(r.getValue(fam1, q2)); 6994 assertNotNull(r.getValue(fam1, q3)); 6995 assertNotNull(r.getValue(fam1, q4)); 6996 6997 // Increment time to T+10 seconds 6998 edge.incrementTime(5000); 6999 7000 r = region.get(new Get(row)); 7001 assertNull(r.getValue(fam1, q1)); 7002 assertNull(r.getValue(fam1, q2)); 7003 assertNotNull(r.getValue(fam1, q3)); 7004 assertNotNull(r.getValue(fam1, q4)); 7005 7006 // Increment time to T+15 seconds 7007 edge.incrementTime(5000); 7008 7009 r = region.get(new Get(row)); 7010 assertNull(r.getValue(fam1, q1)); 7011 assertNull(r.getValue(fam1, q2)); 7012 assertNull(r.getValue(fam1, q3)); 7013 assertNotNull(r.getValue(fam1, q4)); 7014 7015 // Increment time to T+20 seconds 7016 edge.incrementTime(10000); 7017 7018 r = region.get(new Get(row)); 7019 assertNull(r.getValue(fam1, q1)); 7020 assertNull(r.getValue(fam1, q2)); 7021 assertNull(r.getValue(fam1, q3)); 7022 assertNull(r.getValue(fam1, q4)); 7023 7024 // Fun with disappearing increments 7025 7026 // Start at 1 7027 region.put(new Put(row).addColumn(fam1, q1, Bytes.toBytes(1L))); 7028 r = region.get(new Get(row)); 7029 byte[] val = r.getValue(fam1, q1); 7030 assertNotNull(val); 7031 assertEquals(1L, Bytes.toLong(val)); 7032 7033 // Increment with a TTL of 5 seconds 7034 Increment incr = new Increment(row).addColumn(fam1, q1, 1L); 7035 incr.setTTL(5000); 7036 region.increment(incr); // 2 7037 7038 // New value should be 2 7039 r = region.get(new Get(row)); 7040 val = r.getValue(fam1, q1); 7041 assertNotNull(val); 7042 assertEquals(2L, Bytes.toLong(val)); 7043 7044 // Increment time to T+25 seconds 7045 edge.incrementTime(5000); 7046 7047 // Value should be back to 1 7048 r = region.get(new Get(row)); 7049 val = r.getValue(fam1, q1); 7050 assertNotNull(val); 7051 assertEquals(1L, Bytes.toLong(val)); 7052 7053 // Increment time to T+30 seconds 7054 edge.incrementTime(5000); 7055 7056 // Original value written at T+20 should be gone now via family TTL 7057 r = region.get(new Get(row)); 7058 assertNull(r.getValue(fam1, q1)); 7059 } 7060 7061 @Test 7062 public void testTTLsUsingSmallHeartBeatCells() throws IOException { 7063 IncrementingEnvironmentEdge edge = new IncrementingEnvironmentEdge(); 7064 EnvironmentEdgeManager.injectEdge(edge); 7065 7066 final byte[] row = Bytes.toBytes("testRow"); 7067 final byte[] q1 = Bytes.toBytes("q1"); 7068 final byte[] q2 = Bytes.toBytes("q2"); 7069 final byte[] q3 = Bytes.toBytes("q3"); 7070 final byte[] q4 = Bytes.toBytes("q4"); 7071 final byte[] q5 = Bytes.toBytes("q5"); 7072 final byte[] q6 = Bytes.toBytes("q6"); 7073 final byte[] q7 = Bytes.toBytes("q7"); 7074 final byte[] q8 = Bytes.toBytes("q8"); 7075 7076 // 10 seconds 7077 int ttlSecs = 10; 7078 TableDescriptor tableDescriptor = 7079 TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())).setColumnFamily( 7080 ColumnFamilyDescriptorBuilder.newBuilder(fam1).setTimeToLive(ttlSecs).build()).build(); 7081 7082 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 7083 conf.setInt(HFile.FORMAT_VERSION_KEY, HFile.MIN_FORMAT_VERSION_WITH_TAGS); 7084 // using small heart beat cells 7085 conf.setLong(StoreScanner.HBASE_CELLS_SCANNED_PER_HEARTBEAT_CHECK, 2); 7086 7087 region = HBaseTestingUtil.createRegionAndWAL( 7088 RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build(), 7089 TEST_UTIL.getDataTestDir(), conf, tableDescriptor); 7090 assertNotNull(region); 7091 long now = EnvironmentEdgeManager.currentTime(); 7092 // Add a cell that will expire in 5 seconds via cell TTL 7093 region.put(new Put(row).addColumn(fam1, q1, now, HConstants.EMPTY_BYTE_ARRAY)); 7094 region.put(new Put(row).addColumn(fam1, q2, now, HConstants.EMPTY_BYTE_ARRAY)); 7095 region.put(new Put(row).addColumn(fam1, q3, now, HConstants.EMPTY_BYTE_ARRAY)); 7096 // Add a cell that will expire after 10 seconds via family setting 7097 region 7098 .put(new Put(row).addColumn(fam1, q4, now + ttlSecs * 1000 + 1, HConstants.EMPTY_BYTE_ARRAY)); 7099 region 7100 .put(new Put(row).addColumn(fam1, q5, now + ttlSecs * 1000 + 1, HConstants.EMPTY_BYTE_ARRAY)); 7101 7102 region.put(new Put(row).addColumn(fam1, q6, now, HConstants.EMPTY_BYTE_ARRAY)); 7103 region.put(new Put(row).addColumn(fam1, q7, now, HConstants.EMPTY_BYTE_ARRAY)); 7104 region 7105 .put(new Put(row).addColumn(fam1, q8, now + ttlSecs * 1000 + 1, HConstants.EMPTY_BYTE_ARRAY)); 7106 7107 // Flush so we are sure store scanning gets this right 7108 region.flush(true); 7109 7110 // A query at time T+0 should return all cells 7111 checkScan(8); 7112 region.delete(new Delete(row).addColumn(fam1, q8)); 7113 7114 // Increment time to T+ttlSecs seconds 7115 edge.incrementTime(ttlSecs * 1000); 7116 checkScan(2); 7117 } 7118 7119 private void checkScan(int expectCellSize) throws IOException { 7120 Scan s = new Scan().withStartRow(row); 7121 ScannerContext.Builder contextBuilder = ScannerContext.newBuilder(true); 7122 ScannerContext scannerContext = contextBuilder.build(); 7123 try (RegionScanner scanner = region.getScanner(s)) { 7124 List<Cell> kvs = new ArrayList<>(); 7125 scanner.next(kvs, scannerContext); 7126 assertEquals(expectCellSize, kvs.size()); 7127 } 7128 } 7129 7130 @Test 7131 public void testIncrementTimestampsAreMonotonic() throws IOException { 7132 region = initHRegion(tableName, method, CONF, fam1); 7133 ManualEnvironmentEdge edge = new ManualEnvironmentEdge(); 7134 EnvironmentEdgeManager.injectEdge(edge); 7135 7136 edge.setValue(10); 7137 Increment inc = new Increment(row); 7138 inc.setDurability(Durability.SKIP_WAL); 7139 inc.addColumn(fam1, qual1, 1L); 7140 region.increment(inc); 7141 7142 Result result = region.get(new Get(row)); 7143 Cell c = result.getColumnLatestCell(fam1, qual1); 7144 assertNotNull(c); 7145 assertEquals(10L, c.getTimestamp()); 7146 7147 edge.setValue(1); // clock goes back 7148 region.increment(inc); 7149 result = region.get(new Get(row)); 7150 c = result.getColumnLatestCell(fam1, qual1); 7151 assertEquals(11L, c.getTimestamp()); 7152 assertEquals(2L, Bytes.toLong(c.getValueArray(), c.getValueOffset(), c.getValueLength())); 7153 } 7154 7155 @Test 7156 public void testAppendTimestampsAreMonotonic() throws IOException { 7157 region = initHRegion(tableName, method, CONF, fam1); 7158 ManualEnvironmentEdge edge = new ManualEnvironmentEdge(); 7159 EnvironmentEdgeManager.injectEdge(edge); 7160 7161 edge.setValue(10); 7162 Append a = new Append(row); 7163 a.setDurability(Durability.SKIP_WAL); 7164 a.addColumn(fam1, qual1, qual1); 7165 region.append(a); 7166 7167 Result result = region.get(new Get(row)); 7168 Cell c = result.getColumnLatestCell(fam1, qual1); 7169 assertNotNull(c); 7170 assertEquals(10L, c.getTimestamp()); 7171 7172 edge.setValue(1); // clock goes back 7173 region.append(a); 7174 result = region.get(new Get(row)); 7175 c = result.getColumnLatestCell(fam1, qual1); 7176 assertEquals(11L, c.getTimestamp()); 7177 7178 byte[] expected = new byte[qual1.length * 2]; 7179 System.arraycopy(qual1, 0, expected, 0, qual1.length); 7180 System.arraycopy(qual1, 0, expected, qual1.length, qual1.length); 7181 7182 assertTrue(Bytes.equals(c.getValueArray(), c.getValueOffset(), c.getValueLength(), expected, 0, 7183 expected.length)); 7184 } 7185 7186 @Test 7187 public void testCheckAndMutateTimestampsAreMonotonic() throws IOException { 7188 region = initHRegion(tableName, method, CONF, fam1); 7189 ManualEnvironmentEdge edge = new ManualEnvironmentEdge(); 7190 EnvironmentEdgeManager.injectEdge(edge); 7191 7192 edge.setValue(10); 7193 Put p = new Put(row); 7194 p.setDurability(Durability.SKIP_WAL); 7195 p.addColumn(fam1, qual1, qual1); 7196 region.put(p); 7197 7198 Result result = region.get(new Get(row)); 7199 Cell c = result.getColumnLatestCell(fam1, qual1); 7200 assertNotNull(c); 7201 assertEquals(10L, c.getTimestamp()); 7202 7203 edge.setValue(1); // clock goes back 7204 p = new Put(row); 7205 p.setDurability(Durability.SKIP_WAL); 7206 p.addColumn(fam1, qual1, qual2); 7207 region.checkAndMutate(row, fam1, qual1, CompareOperator.EQUAL, new BinaryComparator(qual1), p); 7208 result = region.get(new Get(row)); 7209 c = result.getColumnLatestCell(fam1, qual1); 7210 assertEquals(10L, c.getTimestamp()); 7211 7212 assertTrue(Bytes.equals(c.getValueArray(), c.getValueOffset(), c.getValueLength(), qual2, 0, 7213 qual2.length)); 7214 } 7215 7216 @Test 7217 public void testBatchMutateWithWrongRegionException() throws Exception { 7218 final byte[] a = Bytes.toBytes("a"); 7219 final byte[] b = Bytes.toBytes("b"); 7220 final byte[] c = Bytes.toBytes("c"); // exclusive 7221 7222 int prevLockTimeout = CONF.getInt("hbase.rowlock.wait.duration", 30000); 7223 CONF.setInt("hbase.rowlock.wait.duration", 1000); 7224 region = initHRegion(tableName, a, c, method, CONF, false, fam1); 7225 7226 Mutation[] mutations = new Mutation[] { 7227 new Put(a).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(a) 7228 .setFamily(fam1).setTimestamp(HConstants.LATEST_TIMESTAMP).setType(Cell.Type.Put).build()), 7229 // this is outside the region boundary 7230 new Put(c).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(c) 7231 .setFamily(fam1).setTimestamp(HConstants.LATEST_TIMESTAMP).setType(Type.Put).build()), 7232 new Put(b) 7233 .add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(b).setFamily(fam1) 7234 .setTimestamp(HConstants.LATEST_TIMESTAMP).setType(Cell.Type.Put).build()) }; 7235 7236 OperationStatus[] status = region.batchMutate(mutations); 7237 assertEquals(OperationStatusCode.SUCCESS, status[0].getOperationStatusCode()); 7238 assertEquals(OperationStatusCode.SANITY_CHECK_FAILURE, status[1].getOperationStatusCode()); 7239 assertEquals(OperationStatusCode.SUCCESS, status[2].getOperationStatusCode()); 7240 7241 // test with a row lock held for a long time 7242 final CountDownLatch obtainedRowLock = new CountDownLatch(1); 7243 ExecutorService exec = Executors.newFixedThreadPool(2); 7244 Future<Void> f1 = exec.submit(new Callable<Void>() { 7245 @Override 7246 public Void call() throws Exception { 7247 LOG.info("Acquiring row lock"); 7248 RowLock rl = region.getRowLock(b); 7249 obtainedRowLock.countDown(); 7250 LOG.info("Waiting for 5 seconds before releasing lock"); 7251 Threads.sleep(5000); 7252 LOG.info("Releasing row lock"); 7253 rl.release(); 7254 return null; 7255 } 7256 }); 7257 obtainedRowLock.await(30, TimeUnit.SECONDS); 7258 7259 Future<Void> f2 = exec.submit(new Callable<Void>() { 7260 @Override 7261 public Void call() throws Exception { 7262 Mutation[] mutations = new Mutation[] { 7263 new Put(a) 7264 .add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(a).setFamily(fam1) 7265 .setTimestamp(HConstants.LATEST_TIMESTAMP).setType(Cell.Type.Put).build()), 7266 new Put(b) 7267 .add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(b).setFamily(fam1) 7268 .setTimestamp(HConstants.LATEST_TIMESTAMP).setType(Cell.Type.Put).build()), }; 7269 7270 // this will wait for the row lock, and it will eventually succeed 7271 OperationStatus[] status = region.batchMutate(mutations); 7272 assertEquals(OperationStatusCode.SUCCESS, status[0].getOperationStatusCode()); 7273 assertEquals(OperationStatusCode.SUCCESS, status[1].getOperationStatusCode()); 7274 return null; 7275 } 7276 }); 7277 7278 f1.get(); 7279 f2.get(); 7280 7281 CONF.setInt("hbase.rowlock.wait.duration", prevLockTimeout); 7282 } 7283 7284 @Test 7285 public void testBatchMutateWithZeroRowLockWait() throws Exception { 7286 final byte[] a = Bytes.toBytes("a"); 7287 final byte[] b = Bytes.toBytes("b"); 7288 final byte[] c = Bytes.toBytes("c"); // exclusive 7289 7290 Configuration conf = new Configuration(CONF); 7291 conf.setInt("hbase.rowlock.wait.duration", 0); 7292 final RegionInfo hri = 7293 RegionInfoBuilder.newBuilder(tableName).setStartKey(a).setEndKey(c).build(); 7294 final TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName) 7295 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam1)).build(); 7296 region = HRegion.createHRegion(hri, TEST_UTIL.getDataTestDir(), conf, htd, 7297 HBaseTestingUtil.createWal(conf, TEST_UTIL.getDataTestDirOnTestFS(method + ".log"), hri)); 7298 7299 Mutation[] mutations = new Mutation[] { 7300 new Put(a).add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(a) 7301 .setFamily(fam1).setTimestamp(HConstants.LATEST_TIMESTAMP).setType(Cell.Type.Put).build()), 7302 new Put(b) 7303 .add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(b).setFamily(fam1) 7304 .setTimestamp(HConstants.LATEST_TIMESTAMP).setType(Cell.Type.Put).build()) }; 7305 7306 OperationStatus[] status = region.batchMutate(mutations); 7307 assertEquals(OperationStatusCode.SUCCESS, status[0].getOperationStatusCode()); 7308 assertEquals(OperationStatusCode.SUCCESS, status[1].getOperationStatusCode()); 7309 7310 // test with a row lock held for a long time 7311 final CountDownLatch obtainedRowLock = new CountDownLatch(1); 7312 ExecutorService exec = Executors.newFixedThreadPool(2); 7313 Future<Void> f1 = exec.submit(new Callable<Void>() { 7314 @Override 7315 public Void call() throws Exception { 7316 LOG.info("Acquiring row lock"); 7317 RowLock rl = region.getRowLock(b); 7318 obtainedRowLock.countDown(); 7319 LOG.info("Waiting for 5 seconds before releasing lock"); 7320 Threads.sleep(5000); 7321 LOG.info("Releasing row lock"); 7322 rl.release(); 7323 return null; 7324 } 7325 }); 7326 obtainedRowLock.await(30, TimeUnit.SECONDS); 7327 7328 Future<Void> f2 = exec.submit(new Callable<Void>() { 7329 @Override 7330 public Void call() throws Exception { 7331 Mutation[] mutations = new Mutation[] { 7332 new Put(a) 7333 .add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(a).setFamily(fam1) 7334 .setTimestamp(HConstants.LATEST_TIMESTAMP).setType(Cell.Type.Put).build()), 7335 new Put(b) 7336 .add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(b).setFamily(fam1) 7337 .setTimestamp(HConstants.LATEST_TIMESTAMP).setType(Cell.Type.Put).build()), }; 7338 // when handling row b we are going to spin on the failure to get the row lock 7339 // until the lock above is released, but we will still succeed so long as that 7340 // takes less time then the test time out. 7341 OperationStatus[] status = region.batchMutate(mutations); 7342 assertEquals(OperationStatusCode.SUCCESS, status[0].getOperationStatusCode()); 7343 assertEquals(OperationStatusCode.SUCCESS, status[1].getOperationStatusCode()); 7344 return null; 7345 } 7346 }); 7347 7348 f1.get(); 7349 f2.get(); 7350 } 7351 7352 @Test 7353 public void testCheckAndRowMutateTimestampsAreMonotonic() throws IOException { 7354 region = initHRegion(tableName, method, CONF, fam1); 7355 ManualEnvironmentEdge edge = new ManualEnvironmentEdge(); 7356 EnvironmentEdgeManager.injectEdge(edge); 7357 7358 edge.setValue(10); 7359 Put p = new Put(row); 7360 p.setDurability(Durability.SKIP_WAL); 7361 p.addColumn(fam1, qual1, qual1); 7362 region.put(p); 7363 7364 Result result = region.get(new Get(row)); 7365 Cell c = result.getColumnLatestCell(fam1, qual1); 7366 assertNotNull(c); 7367 assertEquals(10L, c.getTimestamp()); 7368 7369 edge.setValue(1); // clock goes back 7370 p = new Put(row); 7371 p.setDurability(Durability.SKIP_WAL); 7372 p.addColumn(fam1, qual1, qual2); 7373 RowMutations rm = new RowMutations(row); 7374 rm.add(p); 7375 assertTrue(region.checkAndRowMutate(row, fam1, qual1, CompareOperator.EQUAL, 7376 new BinaryComparator(qual1), rm)); 7377 result = region.get(new Get(row)); 7378 c = result.getColumnLatestCell(fam1, qual1); 7379 assertEquals(10L, c.getTimestamp()); 7380 LOG.info( 7381 "c value " + Bytes.toStringBinary(c.getValueArray(), c.getValueOffset(), c.getValueLength())); 7382 7383 assertTrue(Bytes.equals(c.getValueArray(), c.getValueOffset(), c.getValueLength(), qual2, 0, 7384 qual2.length)); 7385 } 7386 7387 private HRegion initHRegion(TableName tableName, String callingMethod, byte[]... families) 7388 throws IOException { 7389 return initHRegion(tableName, callingMethod, HBaseConfiguration.create(), families); 7390 } 7391 7392 /** 7393 * HBASE-16429 Make sure no stuck if roll writer when ring buffer is filled with appends 7394 * @throws IOException if IO error occurred during test 7395 */ 7396 @Test 7397 public void testWritesWhileRollWriter() throws IOException { 7398 int testCount = 10; 7399 int numRows = 1024; 7400 int numFamilies = 2; 7401 int numQualifiers = 2; 7402 final byte[][] families = new byte[numFamilies][]; 7403 for (int i = 0; i < numFamilies; i++) { 7404 families[i] = Bytes.toBytes("family" + i); 7405 } 7406 final byte[][] qualifiers = new byte[numQualifiers][]; 7407 for (int i = 0; i < numQualifiers; i++) { 7408 qualifiers[i] = Bytes.toBytes("qual" + i); 7409 } 7410 7411 CONF.setInt("hbase.regionserver.wal.disruptor.event.count", 2); 7412 this.region = initHRegion(tableName, method, CONF, families); 7413 try { 7414 List<Thread> threads = new ArrayList<>(); 7415 for (int i = 0; i < numRows; i++) { 7416 final int count = i; 7417 Thread t = new Thread(new Runnable() { 7418 7419 @Override 7420 public void run() { 7421 byte[] row = Bytes.toBytes("row" + count); 7422 Put put = new Put(row); 7423 put.setDurability(Durability.SYNC_WAL); 7424 byte[] value = Bytes.toBytes(String.valueOf(count)); 7425 for (byte[] family : families) { 7426 for (byte[] qualifier : qualifiers) { 7427 put.addColumn(family, qualifier, count, value); 7428 } 7429 } 7430 try { 7431 region.put(put); 7432 } catch (IOException e) { 7433 throw new RuntimeException(e); 7434 } 7435 } 7436 }); 7437 threads.add(t); 7438 } 7439 for (Thread t : threads) { 7440 t.start(); 7441 } 7442 7443 for (int i = 0; i < testCount; i++) { 7444 region.getWAL().rollWriter(); 7445 Thread.yield(); 7446 } 7447 } finally { 7448 try { 7449 HBaseTestingUtil.closeRegionAndWAL(this.region); 7450 CONF.setInt("hbase.regionserver.wal.disruptor.event.count", 16 * 1024); 7451 } catch (DroppedSnapshotException dse) { 7452 // We could get this on way out because we interrupt the background flusher and it could 7453 // fail anywhere causing a DSE over in the background flusher... only it is not properly 7454 // dealt with so could still be memory hanging out when we get to here -- memory we can't 7455 // flush because the accounting is 'off' since original DSE. 7456 } 7457 this.region = null; 7458 } 7459 } 7460 7461 @Test 7462 public void testMutateRow() throws Exception { 7463 final byte[] row = Bytes.toBytes("row"); 7464 final byte[] q1 = Bytes.toBytes("q1"); 7465 final byte[] q2 = Bytes.toBytes("q2"); 7466 final byte[] q3 = Bytes.toBytes("q3"); 7467 final byte[] q4 = Bytes.toBytes("q4"); 7468 final String v1 = "v1"; 7469 7470 region = initHRegion(tableName, method, CONF, fam1); 7471 7472 // Initial values 7473 region 7474 .batchMutate(new Mutation[] { new Put(row).addColumn(fam1, q2, Bytes.toBytes("toBeDeleted")), 7475 new Put(row).addColumn(fam1, q3, Bytes.toBytes(5L)), 7476 new Put(row).addColumn(fam1, q4, Bytes.toBytes("a")), }); 7477 7478 // Do mutateRow 7479 Result result = region.mutateRow( 7480 new RowMutations(row).add(Arrays.asList(new Put(row).addColumn(fam1, q1, Bytes.toBytes(v1)), 7481 new Delete(row).addColumns(fam1, q2), new Increment(row).addColumn(fam1, q3, 1), 7482 new Append(row).addColumn(fam1, q4, Bytes.toBytes("b"))))); 7483 7484 assertNotNull(result); 7485 assertEquals(6L, Bytes.toLong(result.getValue(fam1, q3))); 7486 assertEquals("ab", Bytes.toString(result.getValue(fam1, q4))); 7487 7488 // Verify the value 7489 result = region.get(new Get(row)); 7490 assertEquals(v1, Bytes.toString(result.getValue(fam1, q1))); 7491 assertNull(result.getValue(fam1, q2)); 7492 assertEquals(6L, Bytes.toLong(result.getValue(fam1, q3))); 7493 assertEquals("ab", Bytes.toString(result.getValue(fam1, q4))); 7494 } 7495 7496 @Test 7497 public void testMutateRowInParallel() throws Exception { 7498 final int numReaderThreads = 100; 7499 final CountDownLatch latch = new CountDownLatch(numReaderThreads); 7500 7501 final byte[] row = Bytes.toBytes("row"); 7502 final byte[] q1 = Bytes.toBytes("q1"); 7503 final byte[] q2 = Bytes.toBytes("q2"); 7504 final byte[] q3 = Bytes.toBytes("q3"); 7505 final byte[] q4 = Bytes.toBytes("q4"); 7506 final String v1 = "v1"; 7507 final String v2 = "v2"; 7508 7509 // We need to ensure the timestamp of the delete operation is more than the previous one 7510 final AtomicLong deleteTimestamp = new AtomicLong(); 7511 7512 region = initHRegion(tableName, method, CONF, fam1); 7513 7514 // Initial values 7515 region.batchMutate(new Mutation[] { new Put(row).addColumn(fam1, q1, Bytes.toBytes(v1)) 7516 .addColumn(fam1, q2, deleteTimestamp.getAndIncrement(), Bytes.toBytes(v2)) 7517 .addColumn(fam1, q3, Bytes.toBytes(1L)).addColumn(fam1, q4, Bytes.toBytes("a")) }); 7518 7519 final AtomicReference<AssertionError> assertionError = new AtomicReference<>(); 7520 7521 // Writer thread 7522 Thread writerThread = new Thread(() -> { 7523 try { 7524 while (!Thread.currentThread().isInterrupted()) { 7525 // If all the reader threads finish, then stop the writer thread 7526 if (latch.await(0, TimeUnit.MILLISECONDS)) { 7527 return; 7528 } 7529 7530 // Execute the mutations. This should be done atomically 7531 region.mutateRow(new RowMutations(row) 7532 .add(Arrays.asList(new Put(row).addColumn(fam1, q1, Bytes.toBytes(v2)), 7533 new Delete(row).addColumns(fam1, q2, deleteTimestamp.getAndIncrement()), 7534 new Increment(row).addColumn(fam1, q3, 1L), 7535 new Append(row).addColumn(fam1, q4, Bytes.toBytes("b"))))); 7536 7537 // We need to ensure the timestamps of the Increment/Append operations are more than the 7538 // previous ones 7539 Result result = region.get(new Get(row).addColumn(fam1, q3).addColumn(fam1, q4)); 7540 long tsIncrement = result.getColumnLatestCell(fam1, q3).getTimestamp(); 7541 long tsAppend = result.getColumnLatestCell(fam1, q4).getTimestamp(); 7542 7543 // Put the initial values 7544 region.batchMutate(new Mutation[] { new Put(row).addColumn(fam1, q1, Bytes.toBytes(v1)) 7545 .addColumn(fam1, q2, deleteTimestamp.getAndIncrement(), Bytes.toBytes(v2)) 7546 .addColumn(fam1, q3, tsIncrement + 1, Bytes.toBytes(1L)) 7547 .addColumn(fam1, q4, tsAppend + 1, Bytes.toBytes("a")) }); 7548 } 7549 } catch (InterruptedException e) { 7550 // Test interrupted, exit gracefully 7551 Thread.currentThread().interrupt(); 7552 } catch (Exception e) { 7553 assertionError.set(new AssertionError(e)); 7554 } 7555 }); 7556 writerThread.setName("WriterThread-" + method); 7557 writerThread.start(); 7558 7559 // Reader threads 7560 for (int i = 0; i < numReaderThreads; i++) { 7561 Thread readerThread = new Thread(() -> { 7562 try { 7563 for (int j = 0; j < 10000; j++) { 7564 // Verify the values 7565 Result result = region.get(new Get(row)); 7566 7567 // The values should be equals to either the initial values or the values after 7568 // executing the mutations 7569 String q1Value = Bytes.toString(result.getValue(fam1, q1)); 7570 if (v1.equals(q1Value)) { 7571 assertEquals(v2, Bytes.toString(result.getValue(fam1, q2))); 7572 assertEquals(1L, Bytes.toLong(result.getValue(fam1, q3))); 7573 assertEquals("a", Bytes.toString(result.getValue(fam1, q4))); 7574 } else if (v2.equals(q1Value)) { 7575 assertNull(Bytes.toString(result.getValue(fam1, q2))); 7576 assertEquals(2L, Bytes.toLong(result.getValue(fam1, q3))); 7577 assertEquals("ab", Bytes.toString(result.getValue(fam1, q4))); 7578 } else { 7579 fail("the qualifier " + Bytes.toString(q1) + " should be " + v1 + " or " + v2 7580 + ", but " + q1Value); 7581 } 7582 } 7583 } catch (Exception e) { 7584 assertionError.set(new AssertionError(e)); 7585 } catch (AssertionError e) { 7586 assertionError.set(e); 7587 } 7588 7589 latch.countDown(); 7590 }); 7591 readerThread.setName("ReaderThread-" + i + "-" + method); 7592 readerThread.start(); 7593 } 7594 7595 try { 7596 writerThread.join(); 7597 7598 if (assertionError.get() != null) { 7599 throw assertionError.get(); 7600 } 7601 } finally { 7602 // Ensure writer thread is stopped on test timeout 7603 writerThread.interrupt(); 7604 writerThread.join(5000); 7605 if (writerThread.isAlive()) { 7606 LOG.warn("Writer thread did not stop within timeout for test " + method); 7607 } 7608 } 7609 } 7610 7611 @Test 7612 public void testMutateRow_WriteRequestCount() throws Exception { 7613 byte[] row1 = Bytes.toBytes("row1"); 7614 byte[] fam1 = Bytes.toBytes("fam1"); 7615 byte[] qf1 = Bytes.toBytes("qualifier"); 7616 byte[] val1 = Bytes.toBytes("value1"); 7617 7618 RowMutations rm = new RowMutations(row1); 7619 Put put = new Put(row1); 7620 put.addColumn(fam1, qf1, val1); 7621 rm.add(put); 7622 7623 this.region = initHRegion(tableName, method, CONF, fam1); 7624 long wrcBeforeMutate = this.region.writeRequestsCount.longValue(); 7625 this.region.mutateRow(rm); 7626 long wrcAfterMutate = this.region.writeRequestsCount.longValue(); 7627 Assert.assertEquals(wrcBeforeMutate + rm.getMutations().size(), wrcAfterMutate); 7628 } 7629 7630 @Test 7631 public void testBulkLoadReplicationEnabled() throws IOException { 7632 TEST_UTIL.getConfiguration().setBoolean(HConstants.REPLICATION_BULKLOAD_ENABLE_KEY, true); 7633 final ServerName serverName = ServerName.valueOf(name.getMethodName(), 100, 42); 7634 final RegionServerServices rss = spy(TEST_UTIL.createMockRegionServerService(serverName)); 7635 7636 TableDescriptor tableDescriptor = 7637 TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 7638 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam1)).build(); 7639 RegionInfo hri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build(); 7640 TEST_UTIL.createRegionDir(hri); 7641 region = HRegion.openHRegion(hri, tableDescriptor, rss.getWAL(hri), 7642 TEST_UTIL.getConfiguration(), rss, null); 7643 7644 assertTrue(region.conf.getBoolean(HConstants.REPLICATION_BULKLOAD_ENABLE_KEY, false)); 7645 String plugins = region.conf.get(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, ""); 7646 String replicationCoprocessorClass = ReplicationObserver.class.getCanonicalName(); 7647 assertTrue(plugins.contains(replicationCoprocessorClass)); 7648 assertTrue(region.getCoprocessorHost().getCoprocessors() 7649 .contains(ReplicationObserver.class.getSimpleName())); 7650 } 7651 7652 /** 7653 * The same as HRegion class, the only difference is that instantiateHStore will create a 7654 * different HStore - HStoreForTesting. [HBASE-8518] 7655 */ 7656 public static class HRegionForTesting extends HRegion { 7657 7658 public HRegionForTesting(final Path tableDir, final WAL wal, final FileSystem fs, 7659 final Configuration confParam, final RegionInfo regionInfo, final TableDescriptor htd, 7660 final RegionServerServices rsServices) { 7661 this(new HRegionFileSystem(confParam, fs, tableDir, regionInfo), wal, confParam, htd, 7662 rsServices); 7663 } 7664 7665 public HRegionForTesting(HRegionFileSystem fs, WAL wal, Configuration confParam, 7666 TableDescriptor htd, RegionServerServices rsServices) { 7667 super(fs, wal, confParam, htd, rsServices); 7668 } 7669 7670 /** 7671 * Create HStore instance. 7672 * @return If Mob is enabled, return HMobStore, otherwise return HStoreForTesting. 7673 */ 7674 @Override 7675 protected HStore instantiateHStore(final ColumnFamilyDescriptor family, boolean warmup) 7676 throws IOException { 7677 if (family.isMobEnabled()) { 7678 if (HFile.getFormatVersion(this.conf) < HFile.MIN_FORMAT_VERSION_WITH_TAGS) { 7679 throw new IOException("A minimum HFile version of " + HFile.MIN_FORMAT_VERSION_WITH_TAGS 7680 + " is required for MOB feature. Consider setting " + HFile.FORMAT_VERSION_KEY 7681 + " accordingly."); 7682 } 7683 return new HMobStore(this, family, this.conf, warmup); 7684 } 7685 return new HStoreForTesting(this, family, this.conf, warmup); 7686 } 7687 } 7688 7689 /** 7690 * HStoreForTesting is merely the same as HStore, the difference is in the doCompaction method of 7691 * HStoreForTesting there is a checkpoint "hbase.hstore.compaction.complete" which doesn't let 7692 * hstore compaction complete. In the former edition, this config is set in HStore class inside 7693 * compact method, though this is just for testing, otherwise it doesn't do any help. In 7694 * HBASE-8518, we try to get rid of all "hbase.hstore.compaction.complete" config (except for 7695 * testing code). 7696 */ 7697 public static class HStoreForTesting extends HStore { 7698 7699 protected HStoreForTesting(final HRegion region, final ColumnFamilyDescriptor family, 7700 final Configuration confParam, boolean warmup) throws IOException { 7701 super(region, family, confParam, warmup); 7702 } 7703 7704 @Override 7705 protected List<HStoreFile> doCompaction(CompactionRequestImpl cr, 7706 Collection<HStoreFile> filesToCompact, User user, long compactionStartTime, 7707 List<Path> newFiles) throws IOException { 7708 // let compaction incomplete. 7709 if (!this.conf.getBoolean("hbase.hstore.compaction.complete", true)) { 7710 LOG.warn("hbase.hstore.compaction.complete is set to false"); 7711 List<HStoreFile> sfs = new ArrayList<>(newFiles.size()); 7712 final boolean evictOnClose = 7713 getCacheConfig() != null ? getCacheConfig().shouldEvictOnClose() : true; 7714 for (Path newFile : newFiles) { 7715 // Create storefile around what we wrote with a reader on it. 7716 HStoreFile sf = storeEngine.createStoreFileAndReader(newFile); 7717 sf.closeStoreFile(evictOnClose); 7718 sfs.add(sf); 7719 } 7720 return sfs; 7721 } 7722 return super.doCompaction(cr, filesToCompact, user, compactionStartTime, newFiles); 7723 } 7724 } 7725 7726 @Test 7727 public void testCloseNoInterrupt() throws Exception { 7728 byte[] cf1 = Bytes.toBytes("CF1"); 7729 byte[][] families = { cf1 }; 7730 final int SLEEP_TIME = 10 * 1000; 7731 7732 Configuration conf = new Configuration(CONF); 7733 // Disable close thread interrupt and server abort behavior 7734 conf.setBoolean(HRegion.CLOSE_WAIT_ABORT, false); 7735 conf.setInt(HRegion.CLOSE_WAIT_INTERVAL, 1000); 7736 region = initHRegion(tableName, method, conf, families); 7737 7738 final CountDownLatch latch = new CountDownLatch(1); 7739 final AtomicBoolean holderInterrupted = new AtomicBoolean(); 7740 Thread holder = new Thread(new Runnable() { 7741 @Override 7742 public void run() { 7743 try { 7744 LOG.info("Starting region operation holder"); 7745 region.startRegionOperation(Operation.SCAN); 7746 latch.countDown(); 7747 try { 7748 Thread.sleep(SLEEP_TIME); 7749 } catch (InterruptedException e) { 7750 LOG.info("Interrupted"); 7751 holderInterrupted.set(true); 7752 } 7753 } catch (Exception e) { 7754 throw new RuntimeException(e); 7755 } finally { 7756 try { 7757 region.closeRegionOperation(); 7758 } catch (IOException e) { 7759 } 7760 LOG.info("Stopped region operation holder"); 7761 } 7762 } 7763 }); 7764 7765 holder.start(); 7766 latch.await(); 7767 HBaseTestingUtil.closeRegionAndWAL(region); 7768 region = null; 7769 holder.join(); 7770 7771 assertFalse("Region lock holder should not have been interrupted", holderInterrupted.get()); 7772 } 7773 7774 @Test 7775 public void testCloseInterrupt() throws Exception { 7776 byte[] cf1 = Bytes.toBytes("CF1"); 7777 byte[][] families = { cf1 }; 7778 final int SLEEP_TIME = 10 * 1000; 7779 7780 Configuration conf = new Configuration(CONF); 7781 // Enable close thread interrupt and server abort behavior 7782 conf.setBoolean(HRegion.CLOSE_WAIT_ABORT, true); 7783 // Speed up the unit test, no need to wait default 10 seconds. 7784 conf.setInt(HRegion.CLOSE_WAIT_INTERVAL, 1000); 7785 region = initHRegion(tableName, method, conf, families); 7786 7787 final CountDownLatch latch = new CountDownLatch(1); 7788 final AtomicBoolean holderInterrupted = new AtomicBoolean(); 7789 Thread holder = new Thread(new Runnable() { 7790 @Override 7791 public void run() { 7792 try { 7793 LOG.info("Starting region operation holder"); 7794 region.startRegionOperation(Operation.SCAN); 7795 latch.countDown(); 7796 try { 7797 Thread.sleep(SLEEP_TIME); 7798 } catch (InterruptedException e) { 7799 LOG.info("Interrupted"); 7800 holderInterrupted.set(true); 7801 } 7802 } catch (Exception e) { 7803 throw new RuntimeException(e); 7804 } finally { 7805 try { 7806 region.closeRegionOperation(); 7807 } catch (IOException e) { 7808 } 7809 LOG.info("Stopped region operation holder"); 7810 } 7811 } 7812 }); 7813 7814 holder.start(); 7815 latch.await(); 7816 region.close(); 7817 region = null; 7818 holder.join(); 7819 7820 assertTrue("Region lock holder was not interrupted", holderInterrupted.get()); 7821 } 7822 7823 @Test 7824 public void testCloseAbort() throws Exception { 7825 byte[] cf1 = Bytes.toBytes("CF1"); 7826 byte[][] families = { cf1 }; 7827 final int SLEEP_TIME = 10 * 1000; 7828 7829 Configuration conf = new Configuration(CONF); 7830 // Enable close thread interrupt and server abort behavior. 7831 conf.setBoolean(HRegion.CLOSE_WAIT_ABORT, true); 7832 // Set the abort interval to a fraction of sleep time so we are guaranteed to be aborted. 7833 conf.setInt(HRegion.CLOSE_WAIT_TIME, SLEEP_TIME / 2); 7834 // Set the wait interval to a fraction of sleep time so we are guaranteed to be interrupted. 7835 conf.setInt(HRegion.CLOSE_WAIT_INTERVAL, SLEEP_TIME / 4); 7836 region = initHRegion(tableName, method, conf, families); 7837 RegionServerServices rsServices = mock(RegionServerServices.class); 7838 when(rsServices.getServerName()).thenReturn(ServerName.valueOf("localhost", 1000, 1000)); 7839 region.rsServices = rsServices; 7840 7841 final CountDownLatch latch = new CountDownLatch(1); 7842 Thread holder = new Thread(new Runnable() { 7843 @Override 7844 public void run() { 7845 try { 7846 LOG.info("Starting region operation holder"); 7847 region.startRegionOperation(Operation.SCAN); 7848 latch.countDown(); 7849 // Hold the lock for SLEEP_TIME seconds no matter how many times we are interrupted. 7850 int timeRemaining = SLEEP_TIME; 7851 while (timeRemaining > 0) { 7852 long start = EnvironmentEdgeManager.currentTime(); 7853 try { 7854 Thread.sleep(timeRemaining); 7855 } catch (InterruptedException e) { 7856 LOG.info("Interrupted"); 7857 } 7858 long end = EnvironmentEdgeManager.currentTime(); 7859 timeRemaining -= end - start; 7860 if (timeRemaining < 0) { 7861 timeRemaining = 0; 7862 } 7863 if (timeRemaining > 0) { 7864 LOG.info("Sleeping again, remaining time " + timeRemaining + " ms"); 7865 } 7866 } 7867 } catch (Exception e) { 7868 throw new RuntimeException(e); 7869 } finally { 7870 try { 7871 region.closeRegionOperation(); 7872 } catch (IOException e) { 7873 } 7874 LOG.info("Stopped region operation holder"); 7875 } 7876 } 7877 }); 7878 7879 holder.start(); 7880 latch.await(); 7881 assertThrows(IOException.class, () -> region.close()); 7882 holder.join(); 7883 7884 // Verify the region tried to abort the server 7885 verify(rsServices, atLeast(1)).abort(anyString(), any()); 7886 } 7887 7888 @Test 7889 public void testInterruptProtection() throws Exception { 7890 byte[] cf1 = Bytes.toBytes("CF1"); 7891 byte[][] families = { cf1 }; 7892 final int SLEEP_TIME = 10 * 1000; 7893 7894 Configuration conf = new Configuration(CONF); 7895 // Enable close thread interrupt and server abort behavior. 7896 conf.setBoolean(HRegion.CLOSE_WAIT_ABORT, true); 7897 conf.setInt(HRegion.CLOSE_WAIT_INTERVAL, 1000); 7898 region = initHRegion(tableName, method, conf, families); 7899 7900 final CountDownLatch latch = new CountDownLatch(1); 7901 final AtomicBoolean holderInterrupted = new AtomicBoolean(); 7902 Thread holder = new Thread(new Runnable() { 7903 @Override 7904 public void run() { 7905 try { 7906 LOG.info("Starting region operation holder"); 7907 region.startRegionOperation(Operation.SCAN); 7908 LOG.info("Protecting against interrupts"); 7909 region.disableInterrupts(); 7910 try { 7911 latch.countDown(); 7912 try { 7913 Thread.sleep(SLEEP_TIME); 7914 } catch (InterruptedException e) { 7915 LOG.info("Interrupted"); 7916 holderInterrupted.set(true); 7917 } 7918 } finally { 7919 region.enableInterrupts(); 7920 } 7921 } catch (Exception e) { 7922 throw new RuntimeException(e); 7923 } finally { 7924 try { 7925 region.closeRegionOperation(); 7926 } catch (IOException e) { 7927 } 7928 LOG.info("Stopped region operation holder"); 7929 } 7930 } 7931 }); 7932 7933 holder.start(); 7934 latch.await(); 7935 region.close(); 7936 region = null; 7937 holder.join(); 7938 7939 assertFalse("Region lock holder should not have been interrupted", holderInterrupted.get()); 7940 } 7941 7942 @Test 7943 public void testRegionOnCoprocessorsChange() throws IOException { 7944 byte[] cf1 = Bytes.toBytes("CF1"); 7945 byte[][] families = { cf1 }; 7946 7947 Configuration conf = new Configuration(CONF); 7948 region = initHRegion(tableName, method, conf, families); 7949 assertNull(region.getCoprocessorHost()); 7950 7951 // set and verify the system coprocessors for region and user region 7952 Configuration newConf = new Configuration(conf); 7953 newConf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, MetaTableMetrics.class.getName()); 7954 newConf.set(CoprocessorHost.USER_REGION_COPROCESSOR_CONF_KEY, 7955 NoOpRegionCoprocessor.class.getName()); 7956 // trigger configuration change 7957 region.onConfigurationChange(newConf); 7958 assertTrue(region.getCoprocessorHost() != null); 7959 Set<String> coprocessors = region.getCoprocessorHost().getCoprocessors(); 7960 assertTrue(coprocessors.size() == 2); 7961 assertTrue(region.getCoprocessorHost().getCoprocessors() 7962 .contains(MetaTableMetrics.class.getSimpleName())); 7963 assertTrue(region.getCoprocessorHost().getCoprocessors() 7964 .contains(NoOpRegionCoprocessor.class.getSimpleName())); 7965 7966 // remove region coprocessor and keep only user region coprocessor 7967 newConf.unset(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY); 7968 region.onConfigurationChange(newConf); 7969 assertTrue(region.getCoprocessorHost() != null); 7970 coprocessors = region.getCoprocessorHost().getCoprocessors(); 7971 assertTrue(coprocessors.size() == 1); 7972 assertTrue(region.getCoprocessorHost().getCoprocessors() 7973 .contains(NoOpRegionCoprocessor.class.getSimpleName())); 7974 } 7975 7976 @Test 7977 public void testRegionOnCoprocessorsWithoutChange() throws IOException { 7978 byte[] cf1 = Bytes.toBytes("CF1"); 7979 byte[][] families = { cf1 }; 7980 7981 Configuration conf = new Configuration(CONF); 7982 conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, 7983 MetaTableMetrics.class.getCanonicalName()); 7984 region = initHRegion(tableName, method, conf, families); 7985 // region service is null in unit test, we need to load the coprocessor once 7986 region.setCoprocessorHost(new RegionCoprocessorHost(region, null, conf)); 7987 RegionCoprocessor regionCoprocessor = 7988 region.getCoprocessorHost().findCoprocessor(MetaTableMetrics.class.getName()); 7989 7990 // simulate when other configuration may have changed and onConfigurationChange execute once 7991 region.onConfigurationChange(conf); 7992 RegionCoprocessor regionCoprocessorAfterOnConfigurationChange = 7993 region.getCoprocessorHost().findCoprocessor(MetaTableMetrics.class.getName()); 7994 assertEquals(regionCoprocessor, regionCoprocessorAfterOnConfigurationChange); 7995 } 7996 7997 public static class NoOpRegionCoprocessor implements RegionCoprocessor, RegionObserver { 7998 // a empty region coprocessor class 7999 } 8000 8001 /** 8002 * Test for HBASE-29662: HRegion.initialize() should fail when trying to recreate .regioninfo file 8003 * after the region directory has been deleted. This validates that .regioninfo file creation does 8004 * not create parent directories recursively. 8005 */ 8006 @Test 8007 public void testHRegionInitializeFailsWithDeletedRegionDir() throws Exception { 8008 LOG.info("Testing HRegion initialize failure with deleted region directory"); 8009 8010 TEST_UTIL = new HBaseTestingUtil(); 8011 Configuration conf = TEST_UTIL.getConfiguration(); 8012 Path testDir = TEST_UTIL.getDataTestDir("testHRegionInitFailure"); 8013 FileSystem fs = testDir.getFileSystem(conf); 8014 8015 // Create table descriptor 8016 TableName tableName = TableName.valueOf("TestHRegionInitWithDeletedDir"); 8017 byte[] family = Bytes.toBytes("info"); 8018 TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName) 8019 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)).build(); 8020 8021 // Create region info 8022 RegionInfo regionInfo = 8023 RegionInfoBuilder.newBuilder(tableName).setStartKey(null).setEndKey(null).build(); 8024 8025 Path tableDir = CommonFSUtils.getTableDir(testDir, tableName); 8026 8027 // Create WAL for the region 8028 WAL wal = HBaseTestingUtil.createWal(conf, testDir, regionInfo); 8029 8030 try { 8031 // Create region normally (this should succeed and create region directory) 8032 LOG.info("Creating region normally - should succeed"); 8033 HRegion region = HRegion.createHRegion(regionInfo, testDir, conf, htd, wal, true); 8034 8035 // Verify region directory exists 8036 Path regionDir = new Path(tableDir, regionInfo.getEncodedName()); 8037 assertTrue("Region directory should exist after creation", fs.exists(regionDir)); 8038 8039 Path regionInfoFile = new Path(regionDir, HRegionFileSystem.REGION_INFO_FILE); 8040 assertTrue("Region info file should exist after creation", fs.exists(regionInfoFile)); 8041 8042 // Delete the region directory (simulating external deletion or corruption) 8043 assertTrue(fs.delete(regionDir, true)); 8044 assertFalse("Region directory should not exist after deletion", fs.exists(regionDir)); 8045 8046 // Try to open/initialize the region again - this should fail 8047 LOG.info("Attempting to re-initialize region with deleted directory - should fail"); 8048 8049 // Create a new region instance (simulating region server restart or reopen) 8050 HRegion newRegion = HRegion.newHRegion(tableDir, wal, fs, conf, regionInfo, htd, null); 8051 // Try to initialize - this should fail because the regionDir doesn't exist 8052 IOException regionInitializeException = null; 8053 try { 8054 newRegion.initialize(null); 8055 } catch (IOException e) { 8056 regionInitializeException = e; 8057 } 8058 8059 // Verify the exception is related to missing parent directory 8060 assertNotNull("Exception should be thrown", regionInitializeException); 8061 String exceptionMessage = regionInitializeException.getMessage().toLowerCase(); 8062 assertTrue(exceptionMessage.contains("region directory does not exist")); 8063 assertFalse("Region directory should still not exist after failed initialization", 8064 fs.exists(regionDir)); 8065 8066 } finally { 8067 if (wal != null) { 8068 wal.close(); 8069 } 8070 TEST_UTIL.cleanupTestDir(); 8071 } 8072 } 8073}