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