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