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