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