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