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