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