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