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.io.hfile.CacheConfig.CACHE_BLOCKS_ON_WRITE_KEY; 021import static org.apache.hadoop.hbase.io.hfile.CacheConfig.CACHE_DATA_ON_READ_KEY; 022import static org.apache.hadoop.hbase.io.hfile.CacheConfig.DEFAULT_CACHE_DATA_ON_READ; 023import static org.apache.hadoop.hbase.io.hfile.CacheConfig.DEFAULT_CACHE_DATA_ON_WRITE; 024import static org.apache.hadoop.hbase.io.hfile.CacheConfig.DEFAULT_EVICT_ON_CLOSE; 025import static org.apache.hadoop.hbase.io.hfile.CacheConfig.EVICT_BLOCKS_ON_CLOSE_KEY; 026import static org.apache.hadoop.hbase.regionserver.DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY; 027import static org.junit.jupiter.api.Assertions.assertArrayEquals; 028import static org.junit.jupiter.api.Assertions.assertEquals; 029import static org.junit.jupiter.api.Assertions.assertFalse; 030import static org.junit.jupiter.api.Assertions.assertNull; 031import static org.junit.jupiter.api.Assertions.assertTrue; 032import static org.junit.jupiter.api.Assertions.fail; 033import static org.mockito.ArgumentMatchers.any; 034import static org.mockito.Mockito.mock; 035import static org.mockito.Mockito.spy; 036import static org.mockito.Mockito.times; 037import static org.mockito.Mockito.verify; 038import static org.mockito.Mockito.when; 039 040import java.io.FileNotFoundException; 041import java.io.IOException; 042import java.lang.ref.SoftReference; 043import java.security.PrivilegedExceptionAction; 044import java.util.ArrayList; 045import java.util.Arrays; 046import java.util.Collection; 047import java.util.Collections; 048import java.util.Iterator; 049import java.util.List; 050import java.util.ListIterator; 051import java.util.NavigableSet; 052import java.util.Optional; 053import java.util.TreeSet; 054import java.util.concurrent.BrokenBarrierException; 055import java.util.concurrent.ConcurrentSkipListSet; 056import java.util.concurrent.CountDownLatch; 057import java.util.concurrent.CyclicBarrier; 058import java.util.concurrent.ExecutorService; 059import java.util.concurrent.Executors; 060import java.util.concurrent.Future; 061import java.util.concurrent.ThreadPoolExecutor; 062import java.util.concurrent.TimeUnit; 063import java.util.concurrent.atomic.AtomicBoolean; 064import java.util.concurrent.atomic.AtomicInteger; 065import java.util.concurrent.atomic.AtomicLong; 066import java.util.concurrent.atomic.AtomicReference; 067import java.util.concurrent.locks.ReentrantReadWriteLock; 068import java.util.function.Consumer; 069import java.util.function.IntBinaryOperator; 070import java.util.function.IntConsumer; 071import org.apache.hadoop.conf.Configuration; 072import org.apache.hadoop.fs.FSDataOutputStream; 073import org.apache.hadoop.fs.FileStatus; 074import org.apache.hadoop.fs.FileSystem; 075import org.apache.hadoop.fs.FilterFileSystem; 076import org.apache.hadoop.fs.LocalFileSystem; 077import org.apache.hadoop.fs.Path; 078import org.apache.hadoop.fs.permission.FsPermission; 079import org.apache.hadoop.hbase.Cell; 080import org.apache.hadoop.hbase.CellBuilderType; 081import org.apache.hadoop.hbase.CellComparator; 082import org.apache.hadoop.hbase.CellComparatorImpl; 083import org.apache.hadoop.hbase.CellUtil; 084import org.apache.hadoop.hbase.ExtendedCell; 085import org.apache.hadoop.hbase.ExtendedCellBuilderFactory; 086import org.apache.hadoop.hbase.HBaseConfiguration; 087import org.apache.hadoop.hbase.HBaseTestingUtil; 088import org.apache.hadoop.hbase.HConstants; 089import org.apache.hadoop.hbase.KeyValue; 090import org.apache.hadoop.hbase.MemoryCompactionPolicy; 091import org.apache.hadoop.hbase.NamespaceDescriptor; 092import org.apache.hadoop.hbase.PrivateCellUtil; 093import org.apache.hadoop.hbase.TableName; 094import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 095import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 096import org.apache.hadoop.hbase.client.Get; 097import org.apache.hadoop.hbase.client.RegionInfo; 098import org.apache.hadoop.hbase.client.RegionInfoBuilder; 099import org.apache.hadoop.hbase.client.Scan; 100import org.apache.hadoop.hbase.client.Scan.ReadType; 101import org.apache.hadoop.hbase.client.TableDescriptor; 102import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 103import org.apache.hadoop.hbase.exceptions.IllegalArgumentIOException; 104import org.apache.hadoop.hbase.filter.Filter; 105import org.apache.hadoop.hbase.filter.FilterBase; 106import org.apache.hadoop.hbase.io.compress.Compression; 107import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding; 108import org.apache.hadoop.hbase.io.hfile.CacheConfig; 109import org.apache.hadoop.hbase.io.hfile.HFile; 110import org.apache.hadoop.hbase.io.hfile.HFileContext; 111import org.apache.hadoop.hbase.io.hfile.HFileContextBuilder; 112import org.apache.hadoop.hbase.monitoring.MonitoredTask; 113import org.apache.hadoop.hbase.nio.RefCnt; 114import org.apache.hadoop.hbase.quotas.RegionSizeStoreImpl; 115import org.apache.hadoop.hbase.regionserver.ChunkCreator.ChunkType; 116import org.apache.hadoop.hbase.regionserver.MemStoreCompactionStrategy.Action; 117import org.apache.hadoop.hbase.regionserver.compactions.CompactionConfiguration; 118import org.apache.hadoop.hbase.regionserver.compactions.CompactionContext; 119import org.apache.hadoop.hbase.regionserver.compactions.DefaultCompactor; 120import org.apache.hadoop.hbase.regionserver.compactions.EverythingPolicy; 121import org.apache.hadoop.hbase.regionserver.querymatcher.ScanQueryMatcher; 122import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker; 123import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory; 124import org.apache.hadoop.hbase.regionserver.throttle.NoLimitThroughputController; 125import org.apache.hadoop.hbase.regionserver.throttle.ThroughputController; 126import org.apache.hadoop.hbase.security.User; 127import org.apache.hadoop.hbase.testclassification.MediumTests; 128import org.apache.hadoop.hbase.testclassification.RegionServerTests; 129import org.apache.hadoop.hbase.util.BloomFilterUtil; 130import org.apache.hadoop.hbase.util.Bytes; 131import org.apache.hadoop.hbase.util.CommonFSUtils; 132import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 133import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper; 134import org.apache.hadoop.hbase.util.IncrementingEnvironmentEdge; 135import org.apache.hadoop.hbase.util.ManualEnvironmentEdge; 136import org.apache.hadoop.hbase.wal.AbstractFSWALProvider; 137import org.apache.hadoop.hbase.wal.WALFactory; 138import org.apache.hadoop.util.Progressable; 139import org.junit.jupiter.api.AfterAll; 140import org.junit.jupiter.api.AfterEach; 141import org.junit.jupiter.api.BeforeEach; 142import org.junit.jupiter.api.Tag; 143import org.junit.jupiter.api.Test; 144import org.junit.jupiter.api.TestInfo; 145import org.junit.jupiter.api.Timeout; 146import org.mockito.Mockito; 147import org.slf4j.Logger; 148import org.slf4j.LoggerFactory; 149 150import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 151 152/** 153 * Test class for the HStore 154 */ 155@Tag(RegionServerTests.TAG) 156@Tag(MediumTests.TAG) 157public class TestHStore { 158 159 private static final Logger LOG = LoggerFactory.getLogger(TestHStore.class); 160 private String name; 161 162 HRegion region; 163 HStore store; 164 byte[] table = Bytes.toBytes("table"); 165 byte[] family = Bytes.toBytes("family"); 166 167 byte[] row = Bytes.toBytes("row"); 168 byte[] row2 = Bytes.toBytes("row2"); 169 byte[] qf1 = Bytes.toBytes("qf1"); 170 byte[] qf2 = Bytes.toBytes("qf2"); 171 byte[] qf3 = Bytes.toBytes("qf3"); 172 byte[] qf4 = Bytes.toBytes("qf4"); 173 byte[] qf5 = Bytes.toBytes("qf5"); 174 byte[] qf6 = Bytes.toBytes("qf6"); 175 176 NavigableSet<byte[]> qualifiers = new ConcurrentSkipListSet<>(Bytes.BYTES_COMPARATOR); 177 178 List<Cell> expected = new ArrayList<>(); 179 List<Cell> result = new ArrayList<>(); 180 181 long id = EnvironmentEdgeManager.currentTime(); 182 Get get = new Get(row); 183 184 private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 185 private static final String DIR = TEST_UTIL.getDataTestDir("TestStore").toString(); 186 187 @BeforeEach 188 public void setUp(TestInfo testInfo) throws IOException { 189 this.name = testInfo.getTestMethod().get().getName(); 190 qualifiers.clear(); 191 qualifiers.add(qf1); 192 qualifiers.add(qf3); 193 qualifiers.add(qf5); 194 195 Iterator<byte[]> iter = qualifiers.iterator(); 196 while (iter.hasNext()) { 197 byte[] next = iter.next(); 198 expected.add(new KeyValue(row, family, next, 1, (byte[]) null)); 199 get.addColumn(family, next); 200 } 201 } 202 203 private void init(String methodName) throws IOException { 204 init(methodName, TEST_UTIL.getConfiguration()); 205 } 206 207 private HStore init(String methodName, Configuration conf) throws IOException { 208 // some of the tests write 4 versions and then flush 209 // (with HBASE-4241, lower versions are collected on flush) 210 return init(methodName, conf, 211 ColumnFamilyDescriptorBuilder.newBuilder(family).setMaxVersions(4).build()); 212 } 213 214 private HStore init(String methodName, Configuration conf, ColumnFamilyDescriptor hcd) 215 throws IOException { 216 return init(methodName, conf, TableDescriptorBuilder.newBuilder(TableName.valueOf(table)), hcd); 217 } 218 219 private HStore init(String methodName, Configuration conf, TableDescriptorBuilder builder, 220 ColumnFamilyDescriptor hcd) throws IOException { 221 return init(methodName, conf, builder, hcd, null); 222 } 223 224 private HStore init(String methodName, Configuration conf, TableDescriptorBuilder builder, 225 ColumnFamilyDescriptor hcd, MyStoreHook hook) throws IOException { 226 return init(methodName, conf, builder, hcd, hook, false); 227 } 228 229 private void initHRegion(String methodName, Configuration conf, TableDescriptorBuilder builder, 230 ColumnFamilyDescriptor hcd, MyStoreHook hook, boolean switchToPread) throws IOException { 231 TableDescriptor htd = builder.setColumnFamily(hcd).build(); 232 Path basedir = new Path(DIR + methodName); 233 Path tableDir = CommonFSUtils.getTableDir(basedir, htd.getTableName()); 234 final Path logdir = new Path(basedir, AbstractFSWALProvider.getWALDirectoryName(methodName)); 235 236 FileSystem fs = FileSystem.get(conf); 237 238 fs.delete(logdir, true); 239 ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 240 MemStoreLABImpl.CHUNK_SIZE_DEFAULT, 1, 0, null, 241 MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT); 242 RegionInfo info = RegionInfoBuilder.newBuilder(htd.getTableName()).build(); 243 Configuration walConf = new Configuration(conf); 244 CommonFSUtils.setRootDir(walConf, basedir); 245 WALFactory wals = new WALFactory(walConf, methodName); 246 region = new HRegion(new HRegionFileSystem(conf, fs, tableDir, info), wals.getWAL(info), conf, 247 htd, null); 248 region.regionServicesForStores = Mockito.spy(region.regionServicesForStores); 249 ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(1); 250 Mockito.when(region.regionServicesForStores.getInMemoryCompactionPool()).thenReturn(pool); 251 } 252 253 private HStore init(String methodName, Configuration conf, TableDescriptorBuilder builder, 254 ColumnFamilyDescriptor hcd, MyStoreHook hook, boolean switchToPread) throws IOException { 255 initHRegion(methodName, conf, builder, hcd, hook, switchToPread); 256 if (hook == null) { 257 store = new HStore(region, hcd, conf, false); 258 } else { 259 store = new MyStore(region, hcd, conf, hook, switchToPread); 260 } 261 region.stores.put(store.getColumnFamilyDescriptor().getName(), store); 262 return store; 263 } 264 265 /** 266 * Test we do not lose data if we fail a flush and then close. Part of HBase-10466 267 */ 268 269 @Test 270 public void testFlushSizeSizing() throws Exception { 271 LOG.info("Setting up a faulty file system that cannot write in " + name); 272 final Configuration conf = HBaseConfiguration.create(TEST_UTIL.getConfiguration()); 273 // Only retry once. 274 conf.setInt("hbase.hstore.flush.retries.number", 1); 275 User user = User.createUserForTesting(conf, name, new String[] { "foo" }); 276 // Inject our faulty LocalFileSystem 277 conf.setClass("fs.file.impl", FaultyFileSystem.class, FileSystem.class); 278 user.runAs(new PrivilegedExceptionAction<Object>() { 279 @Override 280 public Object run() throws Exception { 281 // Make sure it worked (above is sensitive to caching details in hadoop core) 282 FileSystem fs = FileSystem.get(conf); 283 assertEquals(FaultyFileSystem.class, fs.getClass()); 284 FaultyFileSystem ffs = (FaultyFileSystem) fs; 285 286 // Initialize region 287 init(name, conf); 288 289 MemStoreSize mss = store.memstore.getFlushableSize(); 290 assertEquals(0, mss.getDataSize()); 291 LOG.info("Adding some data"); 292 MemStoreSizing kvSize = new NonThreadSafeMemStoreSizing(); 293 store.add(new KeyValue(row, family, qf1, 1, (byte[]) null), kvSize); 294 // add the heap size of active (mutable) segment 295 kvSize.incMemStoreSize(0, MutableSegment.DEEP_OVERHEAD, 0, 0); 296 mss = store.memstore.getFlushableSize(); 297 assertEquals(kvSize.getMemStoreSize(), mss); 298 // Flush. Bug #1 from HBASE-10466. Make sure size calculation on failed flush is right. 299 try { 300 LOG.info("Flushing"); 301 flushStore(store, id++); 302 fail("Didn't bubble up IOE!"); 303 } catch (IOException ioe) { 304 assertTrue(ioe.getMessage().contains("Fault injected")); 305 } 306 // due to snapshot, change mutable to immutable segment 307 kvSize.incMemStoreSize(0, 308 CSLMImmutableSegment.DEEP_OVERHEAD_CSLM - MutableSegment.DEEP_OVERHEAD, 0, 0); 309 mss = store.memstore.getFlushableSize(); 310 assertEquals(kvSize.getMemStoreSize(), mss); 311 MemStoreSizing kvSize2 = new NonThreadSafeMemStoreSizing(); 312 store.add(new KeyValue(row, family, qf2, 2, (byte[]) null), kvSize2); 313 kvSize2.incMemStoreSize(0, MutableSegment.DEEP_OVERHEAD, 0, 0); 314 // Even though we add a new kv, we expect the flushable size to be 'same' since we have 315 // not yet cleared the snapshot -- the above flush failed. 316 assertEquals(kvSize.getMemStoreSize(), mss); 317 ffs.fault.set(false); 318 flushStore(store, id++); 319 mss = store.memstore.getFlushableSize(); 320 // Size should be the foreground kv size. 321 assertEquals(kvSize2.getMemStoreSize(), mss); 322 flushStore(store, id++); 323 mss = store.memstore.getFlushableSize(); 324 assertEquals(0, mss.getDataSize()); 325 assertEquals(MutableSegment.DEEP_OVERHEAD, mss.getHeapSize()); 326 return null; 327 } 328 }); 329 } 330 331 @Test 332 public void testStoreBloomFilterMetricsWithBloomRowCol() throws IOException { 333 int numStoreFiles = 5; 334 writeAndRead(BloomType.ROWCOL, numStoreFiles); 335 336 assertEquals(0, store.getBloomFilterEligibleRequestsCount()); 337 // hard to know exactly the numbers here, we are just trying to 338 // prove that they are incrementing 339 assertTrue(store.getBloomFilterRequestsCount() >= numStoreFiles); 340 assertTrue(store.getBloomFilterNegativeResultsCount() > 0); 341 } 342 343 @Test 344 public void testStoreBloomFilterMetricsWithBloomRow() throws IOException { 345 int numStoreFiles = 5; 346 writeAndRead(BloomType.ROWCOL, numStoreFiles); 347 348 assertEquals(0, store.getBloomFilterEligibleRequestsCount()); 349 // hard to know exactly the numbers here, we are just trying to 350 // prove that they are incrementing 351 assertTrue(store.getBloomFilterRequestsCount() >= numStoreFiles); 352 assertTrue(store.getBloomFilterNegativeResultsCount() > 0); 353 } 354 355 @Test 356 public void testStoreBloomFilterMetricsWithBloomRowPrefix() throws IOException { 357 int numStoreFiles = 5; 358 writeAndRead(BloomType.ROWPREFIX_FIXED_LENGTH, numStoreFiles); 359 360 assertEquals(0, store.getBloomFilterEligibleRequestsCount()); 361 // hard to know exactly the numbers here, we are just trying to 362 // prove that they are incrementing 363 assertTrue(store.getBloomFilterRequestsCount() >= numStoreFiles); 364 } 365 366 @Test 367 public void testStoreBloomFilterMetricsWithBloomNone() throws IOException { 368 int numStoreFiles = 5; 369 writeAndRead(BloomType.NONE, numStoreFiles); 370 371 assertEquals(0, store.getBloomFilterRequestsCount()); 372 assertEquals(0, store.getBloomFilterNegativeResultsCount()); 373 374 // hard to know exactly the numbers here, we are just trying to 375 // prove that they are incrementing 376 assertTrue(store.getBloomFilterEligibleRequestsCount() >= numStoreFiles); 377 } 378 379 private void writeAndRead(BloomType bloomType, int numStoreFiles) throws IOException { 380 Configuration conf = HBaseConfiguration.create(); 381 FileSystem fs = FileSystem.get(conf); 382 383 ColumnFamilyDescriptor hcd = ColumnFamilyDescriptorBuilder.newBuilder(family) 384 .setCompressionType(Compression.Algorithm.GZ).setBloomFilterType(bloomType) 385 .setConfiguration(BloomFilterUtil.PREFIX_LENGTH_KEY, "3").build(); 386 init(name, conf, hcd); 387 388 for (int i = 1; i <= numStoreFiles; i++) { 389 byte[] row = Bytes.toBytes("row" + i); 390 LOG.info("Adding some data for the store file #" + i); 391 long timeStamp = EnvironmentEdgeManager.currentTime(); 392 this.store.add(new KeyValue(row, family, qf1, timeStamp, (byte[]) null), null); 393 this.store.add(new KeyValue(row, family, qf2, timeStamp, (byte[]) null), null); 394 this.store.add(new KeyValue(row, family, qf3, timeStamp, (byte[]) null), null); 395 flush(i); 396 } 397 398 // Verify the total number of store files 399 assertEquals(numStoreFiles, this.store.getStorefiles().size()); 400 401 TreeSet<byte[]> columns = new TreeSet<>(Bytes.BYTES_COMPARATOR); 402 columns.add(qf1); 403 404 for (int i = 1; i <= numStoreFiles; i++) { 405 KeyValueScanner scanner = 406 store.getScanner(new Scan(new Get(Bytes.toBytes("row" + i))), columns, 0); 407 scanner.peek(); 408 } 409 } 410 411 /** 412 * Verify that compression and data block encoding are respected by the createWriter method, used 413 * on store flush. 414 */ 415 @Test 416 public void testCreateWriter() throws Exception { 417 Configuration conf = HBaseConfiguration.create(); 418 FileSystem fs = FileSystem.get(conf); 419 420 ColumnFamilyDescriptor hcd = 421 ColumnFamilyDescriptorBuilder.newBuilder(family).setCompressionType(Compression.Algorithm.GZ) 422 .setDataBlockEncoding(DataBlockEncoding.DIFF).build(); 423 init(name, conf, hcd); 424 425 // Test createWriter 426 StoreFileWriter writer = store.getStoreEngine() 427 .createWriter(CreateStoreFileWriterParams.create().maxKeyCount(4) 428 .compression(hcd.getCompressionType()).isCompaction(false).includeMVCCReadpoint(true) 429 .includesTag(false).shouldDropBehind(false)); 430 Path path = writer.getPath(); 431 writer.append(new KeyValue(row, family, qf1, Bytes.toBytes(1))); 432 writer.append(new KeyValue(row, family, qf2, Bytes.toBytes(2))); 433 writer.append(new KeyValue(row2, family, qf1, Bytes.toBytes(3))); 434 writer.append(new KeyValue(row2, family, qf2, Bytes.toBytes(4))); 435 writer.close(); 436 437 // Verify that compression and encoding settings are respected 438 HFile.Reader reader = HFile.createReader(fs, path, new CacheConfig(conf), true, conf); 439 assertEquals(hcd.getCompressionType(), reader.getTrailer().getCompressionCodec()); 440 assertEquals(hcd.getDataBlockEncoding(), reader.getDataBlockEncoding()); 441 reader.close(); 442 } 443 444 @Test 445 public void testDeleteExpiredStoreFiles() throws Exception { 446 testDeleteExpiredStoreFiles(0); 447 testDeleteExpiredStoreFiles(1); 448 } 449 450 /** 451 * @param minVersions the MIN_VERSIONS for the column family 452 */ 453 public void testDeleteExpiredStoreFiles(int minVersions) throws Exception { 454 int storeFileNum = 4; 455 int ttl = 4; 456 IncrementingEnvironmentEdge edge = new IncrementingEnvironmentEdge(); 457 EnvironmentEdgeManagerTestHelper.injectEdge(edge); 458 459 Configuration conf = HBaseConfiguration.create(); 460 // Enable the expired store file deletion 461 conf.setBoolean("hbase.store.delete.expired.storefile", true); 462 // Set the compaction threshold higher to avoid normal compactions. 463 conf.setInt(CompactionConfiguration.HBASE_HSTORE_COMPACTION_MIN_KEY, 5); 464 465 init(name + "-" + minVersions, conf, ColumnFamilyDescriptorBuilder.newBuilder(family) 466 .setMinVersions(minVersions).setTimeToLive(ttl).build()); 467 468 long storeTtl = this.store.getScanInfo().getTtl(); 469 long sleepTime = storeTtl / storeFileNum; 470 long timeStamp; 471 // There are 4 store files and the max time stamp difference among these 472 // store files will be (this.store.ttl / storeFileNum) 473 for (int i = 1; i <= storeFileNum; i++) { 474 LOG.info("Adding some data for the store file #" + i); 475 timeStamp = EnvironmentEdgeManager.currentTime(); 476 this.store.add(new KeyValue(row, family, qf1, timeStamp, (byte[]) null), null); 477 this.store.add(new KeyValue(row, family, qf2, timeStamp, (byte[]) null), null); 478 this.store.add(new KeyValue(row, family, qf3, timeStamp, (byte[]) null), null); 479 flush(i); 480 edge.incrementTime(sleepTime); 481 } 482 483 // Verify the total number of store files 484 assertEquals(storeFileNum, this.store.getStorefiles().size()); 485 486 // Each call will find one expired store file and delete it before compaction happens. 487 // There will be no compaction due to threshold above. Last file will not be replaced. 488 for (int i = 1; i <= storeFileNum - 1; i++) { 489 // verify the expired store file. 490 assertFalse(this.store.requestCompaction().isPresent()); 491 Collection<HStoreFile> sfs = this.store.getStorefiles(); 492 // Ensure i files are gone. 493 if (minVersions == 0) { 494 assertEquals(storeFileNum - i, sfs.size()); 495 // Ensure only non-expired files remain. 496 for (HStoreFile sf : sfs) { 497 assertTrue(sf.getReader().getMaxTimestamp() >= (edge.currentTime() - storeTtl)); 498 } 499 } else { 500 assertEquals(storeFileNum, sfs.size()); 501 } 502 // Let the next store file expired. 503 edge.incrementTime(sleepTime); 504 } 505 assertFalse(this.store.requestCompaction().isPresent()); 506 507 Collection<HStoreFile> sfs = this.store.getStorefiles(); 508 // Assert the last expired file is not removed. 509 if (minVersions == 0) { 510 assertEquals(1, sfs.size()); 511 } 512 long ts = sfs.iterator().next().getReader().getMaxTimestamp(); 513 assertTrue(ts < (edge.currentTime() - storeTtl)); 514 515 for (HStoreFile sf : sfs) { 516 sf.closeStoreFile(true); 517 } 518 } 519 520 @Test 521 public void testLowestModificationTime() throws Exception { 522 Configuration conf = HBaseConfiguration.create(); 523 FileSystem fs = FileSystem.get(conf); 524 // Initialize region 525 init(name, conf); 526 527 int storeFileNum = 4; 528 for (int i = 1; i <= storeFileNum; i++) { 529 LOG.info("Adding some data for the store file #" + i); 530 this.store.add(new KeyValue(row, family, qf1, i, (byte[]) null), null); 531 this.store.add(new KeyValue(row, family, qf2, i, (byte[]) null), null); 532 this.store.add(new KeyValue(row, family, qf3, i, (byte[]) null), null); 533 flush(i); 534 } 535 // after flush; check the lowest time stamp 536 long lowestTimeStampFromManager = StoreUtils.getLowestTimestamp(store.getStorefiles()); 537 long lowestTimeStampFromFS = getLowestTimeStampFromFS(fs, store.getStorefiles()); 538 assertEquals(lowestTimeStampFromManager, lowestTimeStampFromFS); 539 540 // after compact; check the lowest time stamp 541 store.compact(store.requestCompaction().get(), NoLimitThroughputController.INSTANCE, null); 542 lowestTimeStampFromManager = StoreUtils.getLowestTimestamp(store.getStorefiles()); 543 lowestTimeStampFromFS = getLowestTimeStampFromFS(fs, store.getStorefiles()); 544 assertEquals(lowestTimeStampFromManager, lowestTimeStampFromFS); 545 } 546 547 private static long getLowestTimeStampFromFS(FileSystem fs, 548 final Collection<HStoreFile> candidates) throws IOException { 549 long minTs = Long.MAX_VALUE; 550 if (candidates.isEmpty()) { 551 return minTs; 552 } 553 Path[] p = new Path[candidates.size()]; 554 int i = 0; 555 for (HStoreFile sf : candidates) { 556 p[i] = sf.getPath(); 557 ++i; 558 } 559 560 FileStatus[] stats = fs.listStatus(p); 561 if (stats == null || stats.length == 0) { 562 return minTs; 563 } 564 for (FileStatus s : stats) { 565 minTs = Math.min(minTs, s.getModificationTime()); 566 } 567 return minTs; 568 } 569 570 ////////////////////////////////////////////////////////////////////////////// 571 // Get tests 572 ////////////////////////////////////////////////////////////////////////////// 573 574 private static final int BLOCKSIZE_SMALL = 8192; 575 576 /** 577 * Test for hbase-1686. 578 */ 579 @Test 580 public void testEmptyStoreFile() throws IOException { 581 init(name); 582 // Write a store file. 583 this.store.add(new KeyValue(row, family, qf1, 1, (byte[]) null), null); 584 this.store.add(new KeyValue(row, family, qf2, 1, (byte[]) null), null); 585 flush(1); 586 // Now put in place an empty store file. Its a little tricky. Have to 587 // do manually with hacked in sequence id. 588 HStoreFile f = this.store.getStorefiles().iterator().next(); 589 Path storedir = f.getPath().getParent(); 590 long seqid = f.getMaxSequenceId(); 591 Configuration c = HBaseConfiguration.create(); 592 FileSystem fs = FileSystem.get(c); 593 HFileContext meta = new HFileContextBuilder().withBlockSize(BLOCKSIZE_SMALL).build(); 594 StoreFileWriter w = new StoreFileWriter.Builder(c, new CacheConfig(c), fs) 595 .withOutputDir(storedir).withFileContext(meta).build(); 596 w.appendMetadata(seqid + 1, false); 597 w.close(); 598 this.store.close(); 599 // Reopen it... should pick up two files 600 this.store = 601 new HStore(this.store.getHRegion(), this.store.getColumnFamilyDescriptor(), c, false); 602 assertEquals(2, this.store.getStorefilesCount()); 603 604 result = HBaseTestingUtil.getFromStoreFile(store, get.getRow(), qualifiers); 605 assertEquals(1, result.size()); 606 } 607 608 /** 609 * Getting data from memstore only 610 */ 611 @Test 612 public void testGet_FromMemStoreOnly() throws IOException { 613 init(name); 614 615 // Put data in memstore 616 this.store.add(new KeyValue(row, family, qf1, 1, (byte[]) null), null); 617 this.store.add(new KeyValue(row, family, qf2, 1, (byte[]) null), null); 618 this.store.add(new KeyValue(row, family, qf3, 1, (byte[]) null), null); 619 this.store.add(new KeyValue(row, family, qf4, 1, (byte[]) null), null); 620 this.store.add(new KeyValue(row, family, qf5, 1, (byte[]) null), null); 621 this.store.add(new KeyValue(row, family, qf6, 1, (byte[]) null), null); 622 623 // Get 624 result = HBaseTestingUtil.getFromStoreFile(store, get.getRow(), qualifiers); 625 626 // Compare 627 assertCheck(); 628 } 629 630 @Test 631 public void testTimeRangeIfSomeCellsAreDroppedInFlush() throws IOException { 632 testTimeRangeIfSomeCellsAreDroppedInFlush(1); 633 testTimeRangeIfSomeCellsAreDroppedInFlush(3); 634 testTimeRangeIfSomeCellsAreDroppedInFlush(5); 635 } 636 637 private void testTimeRangeIfSomeCellsAreDroppedInFlush(int maxVersion) throws IOException { 638 init(name, TEST_UTIL.getConfiguration(), 639 ColumnFamilyDescriptorBuilder.newBuilder(family).setMaxVersions(maxVersion).build()); 640 long currentTs = 100; 641 long minTs = currentTs; 642 // the extra cell won't be flushed to disk, 643 // so the min of timerange will be different between memStore and hfile. 644 for (int i = 0; i != (maxVersion + 1); ++i) { 645 this.store.add(new KeyValue(row, family, qf1, ++currentTs, (byte[]) null), null); 646 if (i == 1) { 647 minTs = currentTs; 648 } 649 } 650 flushStore(store, id++); 651 652 Collection<HStoreFile> files = store.getStorefiles(); 653 assertEquals(1, files.size()); 654 HStoreFile f = files.iterator().next(); 655 f.initReader(); 656 StoreFileReader reader = f.getReader(); 657 assertEquals(minTs, reader.timeRange.getMin()); 658 assertEquals(currentTs, reader.timeRange.getMax()); 659 } 660 661 /** 662 * Getting data from files only 663 */ 664 @Test 665 public void testGet_FromFilesOnly() throws IOException { 666 init(name); 667 668 // Put data in memstore 669 this.store.add(new KeyValue(row, family, qf1, 1, (byte[]) null), null); 670 this.store.add(new KeyValue(row, family, qf2, 1, (byte[]) null), null); 671 // flush 672 flush(1); 673 674 // Add more data 675 this.store.add(new KeyValue(row, family, qf3, 1, (byte[]) null), null); 676 this.store.add(new KeyValue(row, family, qf4, 1, (byte[]) null), null); 677 // flush 678 flush(2); 679 680 // Add more data 681 this.store.add(new KeyValue(row, family, qf5, 1, (byte[]) null), null); 682 this.store.add(new KeyValue(row, family, qf6, 1, (byte[]) null), null); 683 // flush 684 flush(3); 685 686 // Get 687 result = HBaseTestingUtil.getFromStoreFile(store, get.getRow(), qualifiers); 688 // this.store.get(get, qualifiers, result); 689 690 // Need to sort the result since multiple files 691 Collections.sort(result, CellComparatorImpl.COMPARATOR); 692 693 // Compare 694 assertCheck(); 695 } 696 697 /** 698 * Getting data from memstore and files 699 */ 700 @Test 701 public void testGet_FromMemStoreAndFiles() throws IOException { 702 init(name); 703 704 // Put data in memstore 705 this.store.add(new KeyValue(row, family, qf1, 1, (byte[]) null), null); 706 this.store.add(new KeyValue(row, family, qf2, 1, (byte[]) null), null); 707 // flush 708 flush(1); 709 710 // Add more data 711 this.store.add(new KeyValue(row, family, qf3, 1, (byte[]) null), null); 712 this.store.add(new KeyValue(row, family, qf4, 1, (byte[]) null), null); 713 // flush 714 flush(2); 715 716 // Add more data 717 this.store.add(new KeyValue(row, family, qf5, 1, (byte[]) null), null); 718 this.store.add(new KeyValue(row, family, qf6, 1, (byte[]) null), null); 719 720 // Get 721 result = HBaseTestingUtil.getFromStoreFile(store, get.getRow(), qualifiers); 722 723 // Need to sort the result since multiple files 724 Collections.sort(result, CellComparatorImpl.COMPARATOR); 725 726 // Compare 727 assertCheck(); 728 } 729 730 private void flush(int storeFilessize) throws IOException { 731 flushStore(store, id++); 732 assertEquals(storeFilessize, this.store.getStorefiles().size()); 733 assertEquals(0, ((AbstractMemStore) this.store.memstore).getActive().getCellsCount()); 734 } 735 736 private void assertCheck() { 737 assertEquals(expected.size(), result.size()); 738 for (int i = 0; i < expected.size(); i++) { 739 assertEquals(expected.get(i), result.get(i)); 740 } 741 } 742 743 @AfterEach 744 public void tearDown() throws Exception { 745 EnvironmentEdgeManagerTestHelper.reset(); 746 if (store != null) { 747 try { 748 store.close(); 749 } catch (IOException e) { 750 } 751 store = null; 752 } 753 if (region != null) { 754 region.close(); 755 region = null; 756 } 757 } 758 759 @AfterAll 760 public static void tearDownAfterClass() throws IOException { 761 TEST_UTIL.cleanupTestDir(); 762 } 763 764 @Test 765 public void testHandleErrorsInFlush() throws Exception { 766 LOG.info("Setting up a faulty file system that cannot write"); 767 768 final Configuration conf = HBaseConfiguration.create(TEST_UTIL.getConfiguration()); 769 User user = User.createUserForTesting(conf, "testhandleerrorsinflush", new String[] { "foo" }); 770 // Inject our faulty LocalFileSystem 771 conf.setClass("fs.file.impl", FaultyFileSystem.class, FileSystem.class); 772 user.runAs(new PrivilegedExceptionAction<Object>() { 773 @Override 774 public Object run() throws Exception { 775 // Make sure it worked (above is sensitive to caching details in hadoop core) 776 FileSystem fs = FileSystem.get(conf); 777 assertEquals(FaultyFileSystem.class, fs.getClass()); 778 779 // Initialize region 780 init(name, conf); 781 782 LOG.info("Adding some data"); 783 store.add(new KeyValue(row, family, qf1, 1, (byte[]) null), null); 784 store.add(new KeyValue(row, family, qf2, 1, (byte[]) null), null); 785 store.add(new KeyValue(row, family, qf3, 1, (byte[]) null), null); 786 787 LOG.info("Before flush, we should have no files"); 788 789 StoreFileTracker sft = StoreFileTrackerFactory.create(conf, false, store.getStoreContext()); 790 Collection<StoreFileInfo> files = sft.load(); 791 assertEquals(0, files != null ? files.size() : 0); 792 793 // flush 794 try { 795 LOG.info("Flushing"); 796 flush(1); 797 fail("Didn't bubble up IOE!"); 798 } catch (IOException ioe) { 799 assertTrue(ioe.getMessage().contains("Fault injected")); 800 } 801 802 LOG.info("After failed flush, we should still have no files!"); 803 files = sft.load(); 804 assertEquals(0, files != null ? files.size() : 0); 805 store.getHRegion().getWAL().close(); 806 return null; 807 } 808 }); 809 FileSystem.closeAllForUGI(user.getUGI()); 810 } 811 812 /** 813 * Faulty file system that will fail if you write past its fault position the FIRST TIME only; 814 * thereafter it will succeed. Used by {@link TestHRegion} too. 815 */ 816 static class FaultyFileSystem extends FilterFileSystem { 817 List<SoftReference<FaultyOutputStream>> outStreams = new ArrayList<>(); 818 private long faultPos = 200; 819 AtomicBoolean fault = new AtomicBoolean(true); 820 821 public FaultyFileSystem() { 822 super(new LocalFileSystem()); 823 LOG.info("Creating faulty!"); 824 } 825 826 @Override 827 public FSDataOutputStream create(Path p) throws IOException { 828 return new FaultyOutputStream(super.create(p), faultPos, fault); 829 } 830 831 @Override 832 public FSDataOutputStream create(Path f, FsPermission permission, boolean overwrite, 833 int bufferSize, short replication, long blockSize, Progressable progress) throws IOException { 834 return new FaultyOutputStream( 835 super.create(f, permission, overwrite, bufferSize, replication, blockSize, progress), 836 faultPos, fault); 837 } 838 839 @Override 840 public FSDataOutputStream createNonRecursive(Path f, boolean overwrite, int bufferSize, 841 short replication, long blockSize, Progressable progress) throws IOException { 842 // Fake it. Call create instead. The default implementation throws an IOE 843 // that this is not supported. 844 return create(f, overwrite, bufferSize, replication, blockSize, progress); 845 } 846 } 847 848 static class FaultyOutputStream extends FSDataOutputStream { 849 volatile long faultPos = Long.MAX_VALUE; 850 private final AtomicBoolean fault; 851 852 public FaultyOutputStream(FSDataOutputStream out, long faultPos, final AtomicBoolean fault) 853 throws IOException { 854 super(out, null); 855 this.faultPos = faultPos; 856 this.fault = fault; 857 } 858 859 @Override 860 public synchronized void write(byte[] buf, int offset, int length) throws IOException { 861 LOG.info("faulty stream write at pos " + getPos()); 862 injectFault(); 863 super.write(buf, offset, length); 864 } 865 866 private void injectFault() throws IOException { 867 if (this.fault.get() && getPos() >= faultPos) { 868 throw new IOException("Fault injected"); 869 } 870 } 871 } 872 873 private static StoreFlushContext flushStore(HStore store, long id) throws IOException { 874 StoreFlushContext storeFlushCtx = store.createFlushContext(id, FlushLifeCycleTracker.DUMMY); 875 storeFlushCtx.prepare(); 876 storeFlushCtx.flushCache(Mockito.mock(MonitoredTask.class)); 877 storeFlushCtx.commit(Mockito.mock(MonitoredTask.class)); 878 return storeFlushCtx; 879 } 880 881 /** 882 * Generate a list of KeyValues for testing based on given parameters 883 * @return the rows key-value list 884 */ 885 private List<ExtendedCell> getKeyValueSet(long[] timestamps, int numRows, byte[] qualifier, 886 byte[] family) { 887 List<ExtendedCell> kvList = new ArrayList<>(); 888 for (int i = 1; i <= numRows; i++) { 889 byte[] b = Bytes.toBytes(i); 890 for (long timestamp : timestamps) { 891 kvList.add(new KeyValue(b, family, qualifier, timestamp, b)); 892 } 893 } 894 return kvList; 895 } 896 897 /** 898 * Test to ensure correctness when using Stores with multiple timestamps 899 */ 900 @Test 901 public void testMultipleTimestamps() throws IOException { 902 int numRows = 1; 903 long[] timestamps1 = new long[] { 1, 5, 10, 20 }; 904 long[] timestamps2 = new long[] { 30, 80 }; 905 906 init(name); 907 908 List<ExtendedCell> kvList1 = getKeyValueSet(timestamps1, numRows, qf1, family); 909 for (ExtendedCell kv : kvList1) { 910 this.store.add(kv, null); 911 } 912 913 flushStore(store, id++); 914 915 List<ExtendedCell> kvList2 = getKeyValueSet(timestamps2, numRows, qf1, family); 916 for (ExtendedCell kv : kvList2) { 917 this.store.add(kv, null); 918 } 919 920 List<Cell> result; 921 Get get = new Get(Bytes.toBytes(1)); 922 get.addColumn(family, qf1); 923 924 get.setTimeRange(0, 15); 925 result = HBaseTestingUtil.getFromStoreFile(store, get); 926 assertTrue(result.size() > 0); 927 928 get.setTimeRange(40, 90); 929 result = HBaseTestingUtil.getFromStoreFile(store, get); 930 assertTrue(result.size() > 0); 931 932 get.setTimeRange(10, 45); 933 result = HBaseTestingUtil.getFromStoreFile(store, get); 934 assertTrue(result.size() > 0); 935 936 get.setTimeRange(80, 145); 937 result = HBaseTestingUtil.getFromStoreFile(store, get); 938 assertTrue(result.size() > 0); 939 940 get.setTimeRange(1, 2); 941 result = HBaseTestingUtil.getFromStoreFile(store, get); 942 assertTrue(result.size() > 0); 943 944 get.setTimeRange(90, 200); 945 result = HBaseTestingUtil.getFromStoreFile(store, get); 946 assertTrue(result.size() == 0); 947 } 948 949 /** 950 * Test for HBASE-3492 - Test split on empty colfam (no store files). 951 * @throws IOException When the IO operations fail. 952 */ 953 @Test 954 public void testSplitWithEmptyColFam() throws IOException { 955 init(name); 956 assertFalse(store.getSplitPoint().isPresent()); 957 } 958 959 @Test 960 public void testStoreUsesConfigurationFromHcdAndHtd() throws Exception { 961 final String CONFIG_KEY = "hbase.regionserver.thread.compaction.throttle"; 962 long anyValue = 10; 963 964 // We'll check that it uses correct config and propagates it appropriately by going thru 965 // the simplest "real" path I can find - "throttleCompaction", which just checks whether 966 // a number we pass in is higher than some config value, inside compactionPolicy. 967 Configuration conf = HBaseConfiguration.create(); 968 conf.setLong(CONFIG_KEY, anyValue); 969 init(name + "-xml", conf); 970 assertTrue(store.throttleCompaction(anyValue + 1)); 971 assertFalse(store.throttleCompaction(anyValue)); 972 973 // HTD overrides XML. 974 --anyValue; 975 init(name + "-htd", conf, TableDescriptorBuilder.newBuilder(TableName.valueOf(table)) 976 .setValue(CONFIG_KEY, Long.toString(anyValue)), ColumnFamilyDescriptorBuilder.of(family)); 977 assertTrue(store.throttleCompaction(anyValue + 1)); 978 assertFalse(store.throttleCompaction(anyValue)); 979 980 // HCD overrides them both. 981 --anyValue; 982 init(name + "-hcd", conf, 983 TableDescriptorBuilder.newBuilder(TableName.valueOf(table)).setValue(CONFIG_KEY, 984 Long.toString(anyValue)), 985 ColumnFamilyDescriptorBuilder.newBuilder(family).setValue(CONFIG_KEY, Long.toString(anyValue)) 986 .build()); 987 assertTrue(store.throttleCompaction(anyValue + 1)); 988 assertFalse(store.throttleCompaction(anyValue)); 989 } 990 991 public static class DummyStoreEngine extends DefaultStoreEngine { 992 public static DefaultCompactor lastCreatedCompactor = null; 993 994 @Override 995 protected void createComponents(Configuration conf, HStore store, CellComparator comparator) 996 throws IOException { 997 super.createComponents(conf, store, comparator); 998 lastCreatedCompactor = this.compactor; 999 } 1000 } 1001 1002 @Test 1003 public void testStoreUsesSearchEngineOverride() throws Exception { 1004 Configuration conf = HBaseConfiguration.create(); 1005 conf.set(StoreEngine.STORE_ENGINE_CLASS_KEY, DummyStoreEngine.class.getName()); 1006 init(name, conf); 1007 assertEquals(DummyStoreEngine.lastCreatedCompactor, this.store.storeEngine.getCompactor()); 1008 } 1009 1010 private void addStoreFile() throws IOException { 1011 HStoreFile f = this.store.getStorefiles().iterator().next(); 1012 Path storedir = f.getPath().getParent(); 1013 long seqid = this.store.getMaxSequenceId().orElse(0L); 1014 Configuration c = TEST_UTIL.getConfiguration(); 1015 FileSystem fs = FileSystem.get(c); 1016 HFileContext fileContext = new HFileContextBuilder().withBlockSize(BLOCKSIZE_SMALL).build(); 1017 StoreFileWriter w = new StoreFileWriter.Builder(c, new CacheConfig(c), fs) 1018 .withOutputDir(storedir).withFileContext(fileContext).build(); 1019 w.appendMetadata(seqid + 1, false); 1020 w.close(); 1021 LOG.info("Added store file:" + w.getPath()); 1022 } 1023 1024 private void archiveStoreFile(int index) throws IOException { 1025 Collection<HStoreFile> files = this.store.getStorefiles(); 1026 HStoreFile sf = null; 1027 Iterator<HStoreFile> it = files.iterator(); 1028 for (int i = 0; i <= index; i++) { 1029 sf = it.next(); 1030 } 1031 store.getRegionFileSystem().removeStoreFiles(store.getColumnFamilyName(), 1032 Lists.newArrayList(sf)); 1033 } 1034 1035 private void closeCompactedFile(int index) throws IOException { 1036 Collection<HStoreFile> files = 1037 this.store.getStoreEngine().getStoreFileManager().getCompactedfiles(); 1038 if (files.size() > 0) { 1039 HStoreFile sf = null; 1040 Iterator<HStoreFile> it = files.iterator(); 1041 for (int i = 0; i <= index; i++) { 1042 sf = it.next(); 1043 } 1044 sf.closeStoreFile(true); 1045 store.getStoreEngine().getStoreFileManager() 1046 .removeCompactedFiles(Collections.singletonList(sf)); 1047 } 1048 } 1049 1050 @Test 1051 public void testRefreshStoreFiles() throws Exception { 1052 init(name); 1053 1054 assertEquals(0, this.store.getStorefilesCount()); 1055 1056 // Test refreshing store files when no store files are there 1057 store.refreshStoreFiles(); 1058 assertEquals(0, this.store.getStorefilesCount()); 1059 1060 // add some data, flush 1061 this.store.add(new KeyValue(row, family, qf1, 1, (byte[]) null), null); 1062 flush(1); 1063 assertEquals(1, this.store.getStorefilesCount()); 1064 1065 // add one more file 1066 addStoreFile(); 1067 1068 assertEquals(1, this.store.getStorefilesCount()); 1069 store.refreshStoreFiles(); 1070 assertEquals(2, this.store.getStorefilesCount()); 1071 1072 // add three more files 1073 addStoreFile(); 1074 addStoreFile(); 1075 addStoreFile(); 1076 1077 assertEquals(2, this.store.getStorefilesCount()); 1078 store.refreshStoreFiles(); 1079 assertEquals(5, this.store.getStorefilesCount()); 1080 1081 closeCompactedFile(0); 1082 archiveStoreFile(0); 1083 1084 assertEquals(5, this.store.getStorefilesCount()); 1085 store.refreshStoreFiles(); 1086 assertEquals(4, this.store.getStorefilesCount()); 1087 1088 archiveStoreFile(0); 1089 archiveStoreFile(1); 1090 archiveStoreFile(2); 1091 1092 assertEquals(4, this.store.getStorefilesCount()); 1093 store.refreshStoreFiles(); 1094 assertEquals(1, this.store.getStorefilesCount()); 1095 1096 archiveStoreFile(0); 1097 store.refreshStoreFiles(); 1098 assertEquals(0, this.store.getStorefilesCount()); 1099 } 1100 1101 @Test 1102 public void testRefreshStoreFilesNotChanged() throws IOException { 1103 init(name); 1104 1105 assertEquals(0, this.store.getStorefilesCount()); 1106 1107 // add some data, flush 1108 this.store.add(new KeyValue(row, family, qf1, 1, (byte[]) null), null); 1109 flush(1); 1110 // add one more file 1111 addStoreFile(); 1112 1113 StoreEngine<?, ?, ?, ?> spiedStoreEngine = spy(store.getStoreEngine()); 1114 1115 // call first time after files changed 1116 spiedStoreEngine.refreshStoreFiles(); 1117 assertEquals(2, this.store.getStorefilesCount()); 1118 verify(spiedStoreEngine, times(1)).replaceStoreFiles(any(), any(), any(), any()); 1119 1120 // call second time 1121 spiedStoreEngine.refreshStoreFiles(); 1122 1123 // ensure that replaceStoreFiles is not called, i.e, the times does not change, if files are not 1124 // refreshed, 1125 verify(spiedStoreEngine, times(1)).replaceStoreFiles(any(), any(), any(), any()); 1126 } 1127 1128 @Test 1129 public void testScanWithCompactionAfterFlush() throws Exception { 1130 TEST_UTIL.getConfiguration().set(DEFAULT_COMPACTION_POLICY_CLASS_KEY, 1131 EverythingPolicy.class.getName()); 1132 init(name); 1133 1134 assertEquals(0, this.store.getStorefilesCount()); 1135 1136 KeyValue kv = new KeyValue(row, family, qf1, 1, (byte[]) null); 1137 // add some data, flush 1138 this.store.add(kv, null); 1139 flush(1); 1140 kv = new KeyValue(row, family, qf2, 1, (byte[]) null); 1141 // add some data, flush 1142 this.store.add(kv, null); 1143 flush(2); 1144 kv = new KeyValue(row, family, qf3, 1, (byte[]) null); 1145 // add some data, flush 1146 this.store.add(kv, null); 1147 flush(3); 1148 1149 ExecutorService service = Executors.newFixedThreadPool(2); 1150 1151 Scan scan = new Scan(new Get(row)); 1152 Future<KeyValueScanner> scanFuture = service.submit(() -> { 1153 try { 1154 LOG.info(">>>> creating scanner"); 1155 return this.store.createScanner(scan, 1156 new ScanInfo(HBaseConfiguration.create(), 1157 ColumnFamilyDescriptorBuilder.newBuilder(family).setMaxVersions(4).build(), 1158 Long.MAX_VALUE, 0, CellComparator.getInstance()), 1159 scan.getFamilyMap().get(store.getColumnFamilyDescriptor().getName()), 0); 1160 } catch (IOException e) { 1161 e.printStackTrace(); 1162 return null; 1163 } 1164 }); 1165 Future compactFuture = service.submit(() -> { 1166 try { 1167 LOG.info(">>>>>> starting compaction"); 1168 Optional<CompactionContext> opCompaction = this.store.requestCompaction(); 1169 assertTrue(opCompaction.isPresent()); 1170 store.compact(opCompaction.get(), new NoLimitThroughputController(), User.getCurrent()); 1171 LOG.info(">>>>>> Compaction is finished"); 1172 this.store.closeAndArchiveCompactedFiles(); 1173 LOG.info(">>>>>> Compacted files deleted"); 1174 } catch (IOException e) { 1175 e.printStackTrace(); 1176 } 1177 }); 1178 1179 KeyValueScanner kvs = scanFuture.get(); 1180 compactFuture.get(); 1181 ((StoreScanner) kvs).currentScanners.forEach(s -> { 1182 if (s instanceof StoreFileScanner) { 1183 assertEquals(1, ((StoreFileScanner) s).getReader().getRefCount()); 1184 } 1185 }); 1186 kvs.seek(kv); 1187 service.shutdownNow(); 1188 } 1189 1190 private long countMemStoreScanner(StoreScanner scanner) { 1191 if (scanner.currentScanners == null) { 1192 return 0; 1193 } 1194 return scanner.currentScanners.stream().filter(s -> !s.isFileScanner()).count(); 1195 } 1196 1197 @Test 1198 public void testNumberOfMemStoreScannersAfterFlush() throws IOException { 1199 long seqId = 100; 1200 long timestamp = EnvironmentEdgeManager.currentTime(); 1201 ExtendedCell cell0 = 1202 ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row).setFamily(family) 1203 .setQualifier(qf1).setTimestamp(timestamp).setType(Cell.Type.Put).setValue(qf1).build(); 1204 PrivateCellUtil.setSequenceId(cell0, seqId); 1205 testNumberOfMemStoreScannersAfterFlush(Arrays.asList(cell0), Collections.emptyList()); 1206 1207 ExtendedCell cell1 = 1208 ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row).setFamily(family) 1209 .setQualifier(qf2).setTimestamp(timestamp).setType(Cell.Type.Put).setValue(qf1).build(); 1210 PrivateCellUtil.setSequenceId(cell1, seqId); 1211 testNumberOfMemStoreScannersAfterFlush(Arrays.asList(cell0), Arrays.asList(cell1)); 1212 1213 seqId = 101; 1214 timestamp = EnvironmentEdgeManager.currentTime(); 1215 ExtendedCell cell2 = 1216 ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row2).setFamily(family) 1217 .setQualifier(qf2).setTimestamp(timestamp).setType(Cell.Type.Put).setValue(qf1).build(); 1218 PrivateCellUtil.setSequenceId(cell2, seqId); 1219 testNumberOfMemStoreScannersAfterFlush(Arrays.asList(cell0), Arrays.asList(cell1, cell2)); 1220 } 1221 1222 private void testNumberOfMemStoreScannersAfterFlush(List<ExtendedCell> inputCellsBeforeSnapshot, 1223 List<ExtendedCell> inputCellsAfterSnapshot) throws IOException { 1224 init(name + "-" + inputCellsBeforeSnapshot.size()); 1225 TreeSet<byte[]> quals = new TreeSet<>(Bytes.BYTES_COMPARATOR); 1226 long seqId = Long.MIN_VALUE; 1227 for (ExtendedCell c : inputCellsBeforeSnapshot) { 1228 quals.add(CellUtil.cloneQualifier(c)); 1229 seqId = Math.max(seqId, c.getSequenceId()); 1230 } 1231 for (ExtendedCell c : inputCellsAfterSnapshot) { 1232 quals.add(CellUtil.cloneQualifier(c)); 1233 seqId = Math.max(seqId, c.getSequenceId()); 1234 } 1235 inputCellsBeforeSnapshot.forEach(c -> store.add(c, null)); 1236 StoreFlushContext storeFlushCtx = store.createFlushContext(id++, FlushLifeCycleTracker.DUMMY); 1237 storeFlushCtx.prepare(); 1238 inputCellsAfterSnapshot.forEach(c -> store.add(c, null)); 1239 int numberOfMemScannersBeforeFlush = inputCellsAfterSnapshot.isEmpty() ? 1 : 2; 1240 try (StoreScanner s = (StoreScanner) store.getScanner(new Scan(), quals, seqId)) { 1241 // snapshot + active (if inputCellsAfterSnapshot isn't empty) 1242 assertEquals(numberOfMemScannersBeforeFlush, countMemStoreScanner(s)); 1243 storeFlushCtx.flushCache(Mockito.mock(MonitoredTask.class)); 1244 storeFlushCtx.commit(Mockito.mock(MonitoredTask.class)); 1245 // snapshot has no data after flush 1246 int numberOfMemScannersAfterFlush = inputCellsAfterSnapshot.isEmpty() ? 0 : 1; 1247 boolean more; 1248 int cellCount = 0; 1249 do { 1250 List<Cell> cells = new ArrayList<>(); 1251 more = s.next(cells); 1252 cellCount += cells.size(); 1253 assertEquals(more ? numberOfMemScannersAfterFlush : 0, countMemStoreScanner(s)); 1254 } while (more); 1255 assertEquals(inputCellsBeforeSnapshot.size() + inputCellsAfterSnapshot.size(), cellCount, 1256 "The number of cells added before snapshot is " + inputCellsBeforeSnapshot.size() 1257 + ", The number of cells added after snapshot is " + inputCellsAfterSnapshot.size()); 1258 // the current scanners is cleared 1259 assertEquals(0, countMemStoreScanner(s)); 1260 } 1261 } 1262 1263 private ExtendedCell createCell(byte[] qualifier, long ts, long sequenceId, byte[] value) 1264 throws IOException { 1265 return createCell(row, qualifier, ts, sequenceId, value); 1266 } 1267 1268 private ExtendedCell createCell(byte[] row, byte[] qualifier, long ts, long sequenceId, 1269 byte[] value) throws IOException { 1270 return ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row) 1271 .setFamily(family).setQualifier(qualifier).setTimestamp(ts).setType(Cell.Type.Put) 1272 .setValue(value).setSequenceId(sequenceId).build(); 1273 } 1274 1275 private ExtendedCell createDeleteCell(byte[] row, byte[] qualifier, long ts, long sequenceId) { 1276 return ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row) 1277 .setFamily(family).setQualifier(qualifier).setTimestamp(ts).setType(Cell.Type.DeleteColumn) 1278 .setSequenceId(sequenceId).build(); 1279 } 1280 1281 @Test 1282 public void testFlushBeforeCompletingScanWoFilter() throws IOException, InterruptedException { 1283 final AtomicBoolean timeToGoNextRow = new AtomicBoolean(false); 1284 final int expectedSize = 3; 1285 testFlushBeforeCompletingScan(new MyListHook() { 1286 @Override 1287 public void hook(int currentSize) { 1288 if (currentSize == expectedSize - 1) { 1289 try { 1290 flushStore(store, id++); 1291 timeToGoNextRow.set(true); 1292 } catch (IOException e) { 1293 throw new RuntimeException(e); 1294 } 1295 } 1296 } 1297 }, new FilterBase() { 1298 @Override 1299 public Filter.ReturnCode filterCell(final Cell c) throws IOException { 1300 return ReturnCode.INCLUDE; 1301 } 1302 }, expectedSize); 1303 } 1304 1305 @Test 1306 public void testFlushBeforeCompletingScanWithFilter() throws IOException, InterruptedException { 1307 final AtomicBoolean timeToGoNextRow = new AtomicBoolean(false); 1308 final int expectedSize = 2; 1309 testFlushBeforeCompletingScan(new MyListHook() { 1310 @Override 1311 public void hook(int currentSize) { 1312 if (currentSize == expectedSize - 1) { 1313 try { 1314 flushStore(store, id++); 1315 timeToGoNextRow.set(true); 1316 } catch (IOException e) { 1317 throw new RuntimeException(e); 1318 } 1319 } 1320 } 1321 }, new FilterBase() { 1322 @Override 1323 public Filter.ReturnCode filterCell(final Cell c) throws IOException { 1324 if (timeToGoNextRow.get()) { 1325 timeToGoNextRow.set(false); 1326 return ReturnCode.NEXT_ROW; 1327 } else { 1328 return ReturnCode.INCLUDE; 1329 } 1330 } 1331 }, expectedSize); 1332 } 1333 1334 @Test 1335 public void testFlushBeforeCompletingScanWithFilterHint() 1336 throws IOException, InterruptedException { 1337 final AtomicBoolean timeToGetHint = new AtomicBoolean(false); 1338 final int expectedSize = 2; 1339 testFlushBeforeCompletingScan(new MyListHook() { 1340 @Override 1341 public void hook(int currentSize) { 1342 if (currentSize == expectedSize - 1) { 1343 try { 1344 flushStore(store, id++); 1345 timeToGetHint.set(true); 1346 } catch (IOException e) { 1347 throw new RuntimeException(e); 1348 } 1349 } 1350 } 1351 }, new FilterBase() { 1352 @Override 1353 public Filter.ReturnCode filterCell(final Cell c) throws IOException { 1354 if (timeToGetHint.get()) { 1355 timeToGetHint.set(false); 1356 return Filter.ReturnCode.SEEK_NEXT_USING_HINT; 1357 } else { 1358 return Filter.ReturnCode.INCLUDE; 1359 } 1360 } 1361 1362 @Override 1363 public Cell getNextCellHint(Cell currentCell) throws IOException { 1364 return currentCell; 1365 } 1366 }, expectedSize); 1367 } 1368 1369 private void testFlushBeforeCompletingScan(MyListHook hook, Filter filter, int expectedSize) 1370 throws IOException, InterruptedException { 1371 Configuration conf = HBaseConfiguration.create(); 1372 byte[] r0 = Bytes.toBytes("row0"); 1373 byte[] r1 = Bytes.toBytes("row1"); 1374 byte[] r2 = Bytes.toBytes("row2"); 1375 byte[] value0 = Bytes.toBytes("value0"); 1376 byte[] value1 = Bytes.toBytes("value1"); 1377 byte[] value2 = Bytes.toBytes("value2"); 1378 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 1379 long ts = EnvironmentEdgeManager.currentTime(); 1380 long seqId = 100; 1381 init(name, conf, TableDescriptorBuilder.newBuilder(TableName.valueOf(table)), 1382 ColumnFamilyDescriptorBuilder.newBuilder(family).setMaxVersions(1).build(), 1383 new MyStoreHook() { 1384 @Override 1385 public long getSmallestReadPoint(HStore store) { 1386 return seqId + 3; 1387 } 1388 }); 1389 // The cells having the value0 won't be flushed to disk because the value of max version is 1 1390 store.add(createCell(r0, qf1, ts, seqId, value0), memStoreSizing); 1391 store.add(createCell(r0, qf2, ts, seqId, value0), memStoreSizing); 1392 store.add(createCell(r0, qf3, ts, seqId, value0), memStoreSizing); 1393 store.add(createCell(r1, qf1, ts + 1, seqId + 1, value1), memStoreSizing); 1394 store.add(createCell(r1, qf2, ts + 1, seqId + 1, value1), memStoreSizing); 1395 store.add(createCell(r1, qf3, ts + 1, seqId + 1, value1), memStoreSizing); 1396 store.add(createCell(r2, qf1, ts + 2, seqId + 2, value2), memStoreSizing); 1397 store.add(createCell(r2, qf2, ts + 2, seqId + 2, value2), memStoreSizing); 1398 store.add(createCell(r2, qf3, ts + 2, seqId + 2, value2), memStoreSizing); 1399 store.add(createCell(r1, qf1, ts + 3, seqId + 3, value1), memStoreSizing); 1400 store.add(createCell(r1, qf2, ts + 3, seqId + 3, value1), memStoreSizing); 1401 store.add(createCell(r1, qf3, ts + 3, seqId + 3, value1), memStoreSizing); 1402 List<Cell> myList = new MyList<>(hook); 1403 Scan scan = new Scan().withStartRow(r1).setFilter(filter); 1404 try (InternalScanner scanner = (InternalScanner) store.getScanner(scan, null, seqId + 3)) { 1405 // r1 1406 scanner.next(myList); 1407 assertEquals(expectedSize, myList.size()); 1408 for (Cell c : myList) { 1409 byte[] actualValue = CellUtil.cloneValue(c); 1410 assertTrue(Bytes.equals(actualValue, value1), "expected:" + Bytes.toStringBinary(value1) 1411 + ", actual:" + Bytes.toStringBinary(actualValue)); 1412 } 1413 List<Cell> normalList = new ArrayList<>(3); 1414 // r2 1415 scanner.next(normalList); 1416 assertEquals(3, normalList.size()); 1417 for (Cell c : normalList) { 1418 byte[] actualValue = CellUtil.cloneValue(c); 1419 assertTrue(Bytes.equals(actualValue, value2), "expected:" + Bytes.toStringBinary(value2) 1420 + ", actual:" + Bytes.toStringBinary(actualValue)); 1421 } 1422 } 1423 } 1424 1425 @Test 1426 public void testFlushBeforeCompletingScanWithDeleteCell() throws IOException { 1427 final Configuration conf = HBaseConfiguration.create(); 1428 1429 byte[] r1 = Bytes.toBytes("row1"); 1430 byte[] r2 = Bytes.toBytes("row2"); 1431 1432 byte[] value1 = Bytes.toBytes("value1"); 1433 byte[] value2 = Bytes.toBytes("value2"); 1434 1435 final MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 1436 final long ts = EnvironmentEdgeManager.currentTime(); 1437 final long seqId = 100; 1438 1439 init(name, conf, TableDescriptorBuilder.newBuilder(TableName.valueOf(table)), 1440 ColumnFamilyDescriptorBuilder.newBuilder(family).setMaxVersions(1).build(), 1441 new MyStoreHook() { 1442 @Override 1443 long getSmallestReadPoint(HStore store) { 1444 return seqId + 3; 1445 } 1446 }); 1447 1448 store.add(createCell(r1, qf1, ts + 1, seqId + 1, value2), memStoreSizing); 1449 store.add(createCell(r1, qf2, ts + 1, seqId + 1, value2), memStoreSizing); 1450 store.add(createCell(r1, qf3, ts + 1, seqId + 1, value2), memStoreSizing); 1451 1452 store.add(createDeleteCell(r1, qf1, ts + 2, seqId + 2), memStoreSizing); 1453 store.add(createDeleteCell(r1, qf2, ts + 2, seqId + 2), memStoreSizing); 1454 store.add(createDeleteCell(r1, qf3, ts + 2, seqId + 2), memStoreSizing); 1455 1456 store.add(createCell(r2, qf1, ts + 3, seqId + 3, value1), memStoreSizing); 1457 store.add(createCell(r2, qf2, ts + 3, seqId + 3, value1), memStoreSizing); 1458 store.add(createCell(r2, qf3, ts + 3, seqId + 3, value1), memStoreSizing); 1459 1460 Scan scan = new Scan().withStartRow(r1); 1461 1462 try (final InternalScanner scanner = 1463 new StoreScanner(store, store.getScanInfo(), scan, null, seqId + 3) { 1464 @Override 1465 protected KeyValueHeap newKVHeap(List<? extends KeyValueScanner> scanners, 1466 CellComparator comparator) throws IOException { 1467 return new MyKeyValueHeap(scanners, comparator, recordBlockSizeCallCount -> { 1468 if (recordBlockSizeCallCount == 6) { 1469 try { 1470 flushStore(store, id++); 1471 } catch (IOException e) { 1472 throw new RuntimeException(e); 1473 } 1474 } 1475 }); 1476 } 1477 }) { 1478 List<Cell> cellResult = new ArrayList<>(); 1479 1480 scanner.next(cellResult); 1481 assertEquals(0, cellResult.size()); 1482 1483 cellResult.clear(); 1484 1485 scanner.next(cellResult); 1486 assertEquals(3, cellResult.size()); 1487 for (Cell cell : cellResult) { 1488 assertArrayEquals(r2, CellUtil.cloneRow(cell)); 1489 } 1490 } 1491 } 1492 1493 @Test 1494 public void testCreateScannerAndSnapshotConcurrently() throws IOException, InterruptedException { 1495 Configuration conf = HBaseConfiguration.create(); 1496 conf.set(HStore.MEMSTORE_CLASS_NAME, MyCompactingMemStore.class.getName()); 1497 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family) 1498 .setInMemoryCompaction(MemoryCompactionPolicy.BASIC).build()); 1499 byte[] value = Bytes.toBytes("value"); 1500 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 1501 long ts = EnvironmentEdgeManager.currentTime(); 1502 long seqId = 100; 1503 // older data whihc shouldn't be "seen" by client 1504 store.add(createCell(qf1, ts, seqId, value), memStoreSizing); 1505 store.add(createCell(qf2, ts, seqId, value), memStoreSizing); 1506 store.add(createCell(qf3, ts, seqId, value), memStoreSizing); 1507 TreeSet<byte[]> quals = new TreeSet<>(Bytes.BYTES_COMPARATOR); 1508 quals.add(qf1); 1509 quals.add(qf2); 1510 quals.add(qf3); 1511 StoreFlushContext storeFlushCtx = store.createFlushContext(id++, FlushLifeCycleTracker.DUMMY); 1512 MyCompactingMemStore.START_TEST.set(true); 1513 Runnable flush = () -> { 1514 // this is blocked until we create first scanner from pipeline and snapshot -- phase (1/5) 1515 // recreate the active memstore -- phase (4/5) 1516 storeFlushCtx.prepare(); 1517 }; 1518 ExecutorService service = Executors.newSingleThreadExecutor(); 1519 service.execute(flush); 1520 // we get scanner from pipeline and snapshot but they are empty. -- phase (2/5) 1521 // this is blocked until we recreate the active memstore -- phase (3/5) 1522 // we get scanner from active memstore but it is empty -- phase (5/5) 1523 InternalScanner scanner = 1524 (InternalScanner) store.getScanner(new Scan(new Get(row)), quals, seqId + 1); 1525 service.shutdown(); 1526 service.awaitTermination(20, TimeUnit.SECONDS); 1527 try { 1528 try { 1529 List<Cell> results = new ArrayList<>(); 1530 scanner.next(results); 1531 assertEquals(3, results.size()); 1532 for (Cell c : results) { 1533 byte[] actualValue = CellUtil.cloneValue(c); 1534 assertTrue(Bytes.equals(actualValue, value), "expected:" + Bytes.toStringBinary(value) 1535 + ", actual:" + Bytes.toStringBinary(actualValue)); 1536 } 1537 } finally { 1538 scanner.close(); 1539 } 1540 } finally { 1541 MyCompactingMemStore.START_TEST.set(false); 1542 storeFlushCtx.flushCache(Mockito.mock(MonitoredTask.class)); 1543 storeFlushCtx.commit(Mockito.mock(MonitoredTask.class)); 1544 } 1545 } 1546 1547 @Test 1548 public void testScanWithDoubleFlush() throws IOException { 1549 Configuration conf = HBaseConfiguration.create(); 1550 // Initialize region 1551 MyStore myStore = initMyStore(name, conf, new MyStoreHook() { 1552 @Override 1553 public void getScanners(MyStore store) throws IOException { 1554 final long tmpId = id++; 1555 ExecutorService s = Executors.newSingleThreadExecutor(); 1556 s.execute(() -> { 1557 try { 1558 // flush the store before storescanner updates the scanners from store. 1559 // The current data will be flushed into files, and the memstore will 1560 // be clear. 1561 // -- phase (4/4) 1562 flushStore(store, tmpId); 1563 } catch (IOException ex) { 1564 throw new RuntimeException(ex); 1565 } 1566 }); 1567 s.shutdown(); 1568 try { 1569 // wait for the flush, the thread will be blocked in HStore#notifyChangedReadersObservers. 1570 s.awaitTermination(3, TimeUnit.SECONDS); 1571 } catch (InterruptedException ex) { 1572 } 1573 } 1574 }); 1575 byte[] oldValue = Bytes.toBytes("oldValue"); 1576 byte[] currentValue = Bytes.toBytes("currentValue"); 1577 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 1578 long ts = EnvironmentEdgeManager.currentTime(); 1579 long seqId = 100; 1580 // older data whihc shouldn't be "seen" by client 1581 myStore.add(createCell(qf1, ts, seqId, oldValue), memStoreSizing); 1582 myStore.add(createCell(qf2, ts, seqId, oldValue), memStoreSizing); 1583 myStore.add(createCell(qf3, ts, seqId, oldValue), memStoreSizing); 1584 long snapshotId = id++; 1585 // push older data into snapshot -- phase (1/4) 1586 StoreFlushContext storeFlushCtx = 1587 store.createFlushContext(snapshotId, FlushLifeCycleTracker.DUMMY); 1588 storeFlushCtx.prepare(); 1589 1590 // insert current data into active -- phase (2/4) 1591 myStore.add(createCell(qf1, ts + 1, seqId + 1, currentValue), memStoreSizing); 1592 myStore.add(createCell(qf2, ts + 1, seqId + 1, currentValue), memStoreSizing); 1593 myStore.add(createCell(qf3, ts + 1, seqId + 1, currentValue), memStoreSizing); 1594 TreeSet<byte[]> quals = new TreeSet<>(Bytes.BYTES_COMPARATOR); 1595 quals.add(qf1); 1596 quals.add(qf2); 1597 quals.add(qf3); 1598 try (InternalScanner scanner = 1599 (InternalScanner) myStore.getScanner(new Scan(new Get(row)), quals, seqId + 1)) { 1600 // complete the flush -- phase (3/4) 1601 storeFlushCtx.flushCache(Mockito.mock(MonitoredTask.class)); 1602 storeFlushCtx.commit(Mockito.mock(MonitoredTask.class)); 1603 1604 List<Cell> results = new ArrayList<>(); 1605 scanner.next(results); 1606 assertEquals(3, results.size()); 1607 for (Cell c : results) { 1608 byte[] actualValue = CellUtil.cloneValue(c); 1609 assertTrue(Bytes.equals(actualValue, currentValue), "expected:" 1610 + Bytes.toStringBinary(currentValue) + ", actual:" + Bytes.toStringBinary(actualValue)); 1611 } 1612 } 1613 } 1614 1615 /** 1616 * This test is for HBASE-27519, when the {@link StoreScanner} is scanning,the Flush and the 1617 * Compaction execute concurrently and theCcompaction compact and archive the flushed 1618 * {@link HStoreFile} which is used by {@link StoreScanner#updateReaders}.Before 1619 * HBASE-27519,{@link StoreScanner.updateReaders} would throw {@link FileNotFoundException}. 1620 */ 1621 @Test 1622 public void testStoreScannerUpdateReadersWhenFlushAndCompactConcurrently() throws IOException { 1623 Configuration conf = HBaseConfiguration.create(); 1624 conf.setBoolean(WALFactory.WAL_ENABLED, false); 1625 conf.set(DEFAULT_COMPACTION_POLICY_CLASS_KEY, EverythingPolicy.class.getName()); 1626 byte[] r0 = Bytes.toBytes("row0"); 1627 byte[] r1 = Bytes.toBytes("row1"); 1628 final CyclicBarrier cyclicBarrier = new CyclicBarrier(2); 1629 final AtomicBoolean shouldWaitRef = new AtomicBoolean(false); 1630 // Initialize region 1631 final MyStore myStore = initMyStore(name, conf, new MyStoreHook() { 1632 @Override 1633 public void getScanners(MyStore store) throws IOException { 1634 try { 1635 // Here this method is called by StoreScanner.updateReaders which is invoked by the 1636 // following TestHStore.flushStore 1637 if (shouldWaitRef.get()) { 1638 // wait the following compaction Task start 1639 cyclicBarrier.await(); 1640 // wait the following HStore.closeAndArchiveCompactedFiles end. 1641 cyclicBarrier.await(); 1642 } 1643 } catch (BrokenBarrierException | InterruptedException e) { 1644 throw new RuntimeException(e); 1645 } 1646 } 1647 }); 1648 1649 final AtomicReference<Throwable> compactionExceptionRef = new AtomicReference<Throwable>(null); 1650 Runnable compactionTask = () -> { 1651 try { 1652 // Only when the StoreScanner.updateReaders invoked by TestHStore.flushStore prepares for 1653 // entering the MyStore.getScanners, compactionTask could start. 1654 cyclicBarrier.await(); 1655 region.compactStore(family, new NoLimitThroughputController()); 1656 myStore.closeAndArchiveCompactedFiles(); 1657 // Notify StoreScanner.updateReaders could enter MyStore.getScanners. 1658 cyclicBarrier.await(); 1659 } catch (Throwable e) { 1660 compactionExceptionRef.set(e); 1661 } 1662 }; 1663 1664 long ts = EnvironmentEdgeManager.currentTime(); 1665 long seqId = 100; 1666 byte[] value = Bytes.toBytes("value"); 1667 // older data whihc shouldn't be "seen" by client 1668 myStore.add(createCell(r0, qf1, ts, seqId, value), null); 1669 flushStore(myStore, id++); 1670 myStore.add(createCell(r0, qf2, ts, seqId, value), null); 1671 flushStore(myStore, id++); 1672 myStore.add(createCell(r0, qf3, ts, seqId, value), null); 1673 TreeSet<byte[]> quals = new TreeSet<>(Bytes.BYTES_COMPARATOR); 1674 quals.add(qf1); 1675 quals.add(qf2); 1676 quals.add(qf3); 1677 1678 myStore.add(createCell(r1, qf1, ts, seqId, value), null); 1679 myStore.add(createCell(r1, qf2, ts, seqId, value), null); 1680 myStore.add(createCell(r1, qf3, ts, seqId, value), null); 1681 1682 Thread.currentThread() 1683 .setName("testStoreScannerUpdateReadersWhenFlushAndCompactConcurrently thread"); 1684 Scan scan = new Scan(); 1685 scan.withStartRow(r0, true); 1686 try (InternalScanner scanner = (InternalScanner) myStore.getScanner(scan, quals, seqId)) { 1687 List<Cell> results = new MyList<>(size -> { 1688 switch (size) { 1689 case 1: 1690 shouldWaitRef.set(true); 1691 Thread thread = new Thread(compactionTask); 1692 thread.setName("MyCompacting Thread."); 1693 thread.start(); 1694 try { 1695 flushStore(myStore, id++); 1696 thread.join(); 1697 } catch (IOException | InterruptedException e) { 1698 throw new RuntimeException(e); 1699 } 1700 shouldWaitRef.set(false); 1701 break; 1702 default: 1703 break; 1704 } 1705 }); 1706 // Before HBASE-27519, here would throw java.io.FileNotFoundException because the storeFile 1707 // which used by StoreScanner.updateReaders is deleted by compactionTask. 1708 scanner.next(results); 1709 // The results is r0 row cells. 1710 assertEquals(3, results.size()); 1711 assertTrue(compactionExceptionRef.get() == null); 1712 } 1713 } 1714 1715 @Test 1716 public void testReclaimChunkWhenScaning() throws IOException { 1717 init("testReclaimChunkWhenScaning"); 1718 long ts = EnvironmentEdgeManager.currentTime(); 1719 long seqId = 100; 1720 byte[] value = Bytes.toBytes("value"); 1721 // older data whihc shouldn't be "seen" by client 1722 store.add(createCell(qf1, ts, seqId, value), null); 1723 store.add(createCell(qf2, ts, seqId, value), null); 1724 store.add(createCell(qf3, ts, seqId, value), null); 1725 TreeSet<byte[]> quals = new TreeSet<>(Bytes.BYTES_COMPARATOR); 1726 quals.add(qf1); 1727 quals.add(qf2); 1728 quals.add(qf3); 1729 try (InternalScanner scanner = 1730 (InternalScanner) store.getScanner(new Scan(new Get(row)), quals, seqId)) { 1731 List<Cell> results = new MyList<>(size -> { 1732 switch (size) { 1733 // 1) we get the first cell (qf1) 1734 // 2) flush the data to have StoreScanner update inner scanners 1735 // 3) the chunk will be reclaimed after updaing 1736 case 1: 1737 try { 1738 flushStore(store, id++); 1739 } catch (IOException e) { 1740 throw new RuntimeException(e); 1741 } 1742 break; 1743 // 1) we get the second cell (qf2) 1744 // 2) add some cell to fill some byte into the chunk (we have only one chunk) 1745 case 2: 1746 try { 1747 byte[] newValue = Bytes.toBytes("newValue"); 1748 // older data whihc shouldn't be "seen" by client 1749 store.add(createCell(qf1, ts + 1, seqId + 1, newValue), null); 1750 store.add(createCell(qf2, ts + 1, seqId + 1, newValue), null); 1751 store.add(createCell(qf3, ts + 1, seqId + 1, newValue), null); 1752 } catch (IOException e) { 1753 throw new RuntimeException(e); 1754 } 1755 break; 1756 default: 1757 break; 1758 } 1759 }); 1760 scanner.next(results); 1761 assertEquals(3, results.size()); 1762 for (Cell c : results) { 1763 byte[] actualValue = CellUtil.cloneValue(c); 1764 assertTrue(Bytes.equals(actualValue, value), "expected:" + Bytes.toStringBinary(value) 1765 + ", actual:" + Bytes.toStringBinary(actualValue)); 1766 } 1767 } 1768 } 1769 1770 /** 1771 * If there are two running InMemoryFlushRunnable, the later InMemoryFlushRunnable may change the 1772 * versionedList. And the first InMemoryFlushRunnable will use the chagned versionedList to remove 1773 * the corresponding segments. In short, there will be some segements which isn't in merge are 1774 * removed. 1775 */ 1776 @Test 1777 public void testRunDoubleMemStoreCompactors() throws IOException, InterruptedException { 1778 int flushSize = 500; 1779 Configuration conf = HBaseConfiguration.create(); 1780 conf.set(HStore.MEMSTORE_CLASS_NAME, MyCompactingMemStoreWithCustomCompactor.class.getName()); 1781 conf.setDouble(CompactingMemStore.IN_MEMORY_FLUSH_THRESHOLD_FACTOR_KEY, 0.25); 1782 MyCompactingMemStoreWithCustomCompactor.RUNNER_COUNT.set(0); 1783 conf.set(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, String.valueOf(flushSize)); 1784 // Set the lower threshold to invoke the "MERGE" policy 1785 conf.set(MemStoreCompactionStrategy.COMPACTING_MEMSTORE_THRESHOLD_KEY, String.valueOf(0)); 1786 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family) 1787 .setInMemoryCompaction(MemoryCompactionPolicy.BASIC).build()); 1788 byte[] value = Bytes.toBytes("thisisavarylargevalue"); 1789 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 1790 long ts = EnvironmentEdgeManager.currentTime(); 1791 long seqId = 100; 1792 // older data whihc shouldn't be "seen" by client 1793 store.add(createCell(qf1, ts, seqId, value), memStoreSizing); 1794 store.add(createCell(qf2, ts, seqId, value), memStoreSizing); 1795 store.add(createCell(qf3, ts, seqId, value), memStoreSizing); 1796 assertEquals(1, MyCompactingMemStoreWithCustomCompactor.RUNNER_COUNT.get()); 1797 StoreFlushContext storeFlushCtx = store.createFlushContext(id++, FlushLifeCycleTracker.DUMMY); 1798 storeFlushCtx.prepare(); 1799 // This shouldn't invoke another in-memory flush because the first compactor thread 1800 // hasn't accomplished the in-memory compaction. 1801 store.add(createCell(qf1, ts + 1, seqId + 1, value), memStoreSizing); 1802 store.add(createCell(qf1, ts + 1, seqId + 1, value), memStoreSizing); 1803 store.add(createCell(qf1, ts + 1, seqId + 1, value), memStoreSizing); 1804 assertEquals(1, MyCompactingMemStoreWithCustomCompactor.RUNNER_COUNT.get()); 1805 // okay. Let the compaction be completed 1806 MyMemStoreCompactor.START_COMPACTOR_LATCH.countDown(); 1807 CompactingMemStore mem = (CompactingMemStore) ((HStore) store).memstore; 1808 while (mem.isMemStoreFlushingInMemory()) { 1809 TimeUnit.SECONDS.sleep(1); 1810 } 1811 // This should invoke another in-memory flush. 1812 store.add(createCell(qf1, ts + 2, seqId + 2, value), memStoreSizing); 1813 store.add(createCell(qf1, ts + 2, seqId + 2, value), memStoreSizing); 1814 store.add(createCell(qf1, ts + 2, seqId + 2, value), memStoreSizing); 1815 assertEquals(2, MyCompactingMemStoreWithCustomCompactor.RUNNER_COUNT.get()); 1816 conf.set(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, 1817 String.valueOf(TableDescriptorBuilder.DEFAULT_MEMSTORE_FLUSH_SIZE)); 1818 storeFlushCtx.flushCache(Mockito.mock(MonitoredTask.class)); 1819 storeFlushCtx.commit(Mockito.mock(MonitoredTask.class)); 1820 } 1821 1822 @Test 1823 public void testAge() throws IOException { 1824 long currentTime = EnvironmentEdgeManager.currentTime(); 1825 ManualEnvironmentEdge edge = new ManualEnvironmentEdge(); 1826 edge.setValue(currentTime); 1827 EnvironmentEdgeManager.injectEdge(edge); 1828 Configuration conf = TEST_UTIL.getConfiguration(); 1829 ColumnFamilyDescriptor hcd = ColumnFamilyDescriptorBuilder.of(family); 1830 initHRegion(name, conf, TableDescriptorBuilder.newBuilder(TableName.valueOf(table)), hcd, null, 1831 false); 1832 HStore store = new HStore(region, hcd, conf, false) { 1833 1834 @Override 1835 protected StoreEngine<?, ?, ?, ?> createStoreEngine(HStore store, Configuration conf, 1836 CellComparator kvComparator) throws IOException { 1837 List<HStoreFile> storefiles = 1838 Arrays.asList(mockStoreFile(currentTime - 10), mockStoreFile(currentTime - 100), 1839 mockStoreFile(currentTime - 1000), mockStoreFile(currentTime - 10000)); 1840 StoreFileManager sfm = mock(StoreFileManager.class); 1841 when(sfm.getStoreFiles()).thenReturn(storefiles); 1842 StoreEngine<?, ?, ?, ?> storeEngine = mock(StoreEngine.class); 1843 when(storeEngine.getStoreFileManager()).thenReturn(sfm); 1844 return storeEngine; 1845 } 1846 }; 1847 assertEquals(10L, store.getMinStoreFileAge().getAsLong()); 1848 assertEquals(10000L, store.getMaxStoreFileAge().getAsLong()); 1849 assertEquals((10 + 100 + 1000 + 10000) / 4.0, store.getAvgStoreFileAge().getAsDouble(), 1E-4); 1850 } 1851 1852 private HStoreFile mockStoreFile(long createdTime) { 1853 StoreFileInfo info = mock(StoreFileInfo.class); 1854 when(info.getCreatedTimestamp()).thenReturn(createdTime); 1855 HStoreFile sf = mock(HStoreFile.class); 1856 when(sf.getReader()).thenReturn(mock(StoreFileReader.class)); 1857 when(sf.isHFile()).thenReturn(true); 1858 when(sf.getFileInfo()).thenReturn(info); 1859 return sf; 1860 } 1861 1862 private MyStore initMyStore(String methodName, Configuration conf, MyStoreHook hook) 1863 throws IOException { 1864 return (MyStore) init(methodName, conf, 1865 TableDescriptorBuilder.newBuilder(TableName.valueOf(table)), 1866 ColumnFamilyDescriptorBuilder.newBuilder(family).setMaxVersions(5).build(), hook); 1867 } 1868 1869 private static class MyStore extends HStore { 1870 private final MyStoreHook hook; 1871 1872 MyStore(final HRegion region, final ColumnFamilyDescriptor family, 1873 final Configuration confParam, MyStoreHook hook, boolean switchToPread) throws IOException { 1874 super(region, family, confParam, false); 1875 this.hook = hook; 1876 } 1877 1878 @Override 1879 public List<KeyValueScanner> getScanners(List<HStoreFile> files, boolean cacheBlocks, 1880 boolean usePread, boolean isCompaction, ScanQueryMatcher matcher, byte[] startRow, 1881 boolean includeStartRow, byte[] stopRow, boolean includeStopRow, long readPt, 1882 boolean includeMemstoreScanner, boolean onlyLatestVersion) throws IOException { 1883 hook.getScanners(this); 1884 return super.getScanners(files, cacheBlocks, usePread, isCompaction, matcher, startRow, true, 1885 stopRow, false, readPt, includeMemstoreScanner, onlyLatestVersion); 1886 } 1887 1888 @Override 1889 public long getSmallestReadPoint() { 1890 return hook.getSmallestReadPoint(this); 1891 } 1892 } 1893 1894 private abstract static class MyStoreHook { 1895 1896 void getScanners(MyStore store) throws IOException { 1897 } 1898 1899 long getSmallestReadPoint(HStore store) { 1900 return store.getHRegion().getSmallestReadPoint(); 1901 } 1902 } 1903 1904 @Test 1905 public void testSwitchingPreadtoStreamParallelyWithCompactionDischarger() throws Exception { 1906 Configuration conf = HBaseConfiguration.create(); 1907 conf.set("hbase.hstore.engine.class", DummyStoreEngine.class.getName()); 1908 conf.setLong(StoreScanner.STORESCANNER_PREAD_MAX_BYTES, 0); 1909 // Set the lower threshold to invoke the "MERGE" policy 1910 MyStore store = initMyStore(name, conf, new MyStoreHook() { 1911 }); 1912 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 1913 long ts = EnvironmentEdgeManager.currentTime(); 1914 long seqID = 1L; 1915 // Add some data to the region and do some flushes 1916 for (int i = 1; i < 10; i++) { 1917 store.add(createCell(Bytes.toBytes("row" + i), qf1, ts, seqID++, Bytes.toBytes("")), 1918 memStoreSizing); 1919 } 1920 // flush them 1921 flushStore(store, seqID); 1922 for (int i = 11; i < 20; i++) { 1923 store.add(createCell(Bytes.toBytes("row" + i), qf1, ts, seqID++, Bytes.toBytes("")), 1924 memStoreSizing); 1925 } 1926 // flush them 1927 flushStore(store, seqID); 1928 for (int i = 21; i < 30; i++) { 1929 store.add(createCell(Bytes.toBytes("row" + i), qf1, ts, seqID++, Bytes.toBytes("")), 1930 memStoreSizing); 1931 } 1932 // flush them 1933 flushStore(store, seqID); 1934 1935 assertEquals(3, store.getStorefilesCount()); 1936 Scan scan = new Scan(); 1937 scan.addFamily(family); 1938 Collection<HStoreFile> storefiles2 = store.getStorefiles(); 1939 ArrayList<HStoreFile> actualStorefiles = Lists.newArrayList(storefiles2); 1940 StoreScanner storeScanner = 1941 (StoreScanner) store.getScanner(scan, scan.getFamilyMap().get(family), Long.MAX_VALUE); 1942 // get the current heap 1943 KeyValueHeap heap = storeScanner.heap; 1944 // create more store files 1945 for (int i = 31; i < 40; i++) { 1946 store.add(createCell(Bytes.toBytes("row" + i), qf1, ts, seqID++, Bytes.toBytes("")), 1947 memStoreSizing); 1948 } 1949 // flush them 1950 flushStore(store, seqID); 1951 1952 for (int i = 41; i < 50; i++) { 1953 store.add(createCell(Bytes.toBytes("row" + i), qf1, ts, seqID++, Bytes.toBytes("")), 1954 memStoreSizing); 1955 } 1956 // flush them 1957 flushStore(store, seqID); 1958 storefiles2 = store.getStorefiles(); 1959 ArrayList<HStoreFile> actualStorefiles1 = Lists.newArrayList(storefiles2); 1960 actualStorefiles1.removeAll(actualStorefiles); 1961 // Do compaction 1962 MyThread thread = new MyThread(storeScanner); 1963 thread.start(); 1964 store.replaceStoreFiles(actualStorefiles, actualStorefiles1, false); 1965 thread.join(); 1966 KeyValueHeap heap2 = thread.getHeap(); 1967 assertFalse(heap.equals(heap2)); 1968 } 1969 1970 @Test 1971 public void testMaxPreadBytesConfiguredToBeLessThanZero() throws Exception { 1972 Configuration conf = HBaseConfiguration.create(); 1973 conf.set("hbase.hstore.engine.class", DummyStoreEngine.class.getName()); 1974 // Set 'hbase.storescanner.pread.max.bytes' < 0, so that StoreScanner will be a STREAM type. 1975 conf.setLong(StoreScanner.STORESCANNER_PREAD_MAX_BYTES, -1); 1976 MyStore store = initMyStore(name, conf, new MyStoreHook() { 1977 }); 1978 Scan scan = new Scan(); 1979 scan.addFamily(family); 1980 // ReadType on Scan is still DEFAULT only. 1981 assertEquals(ReadType.DEFAULT, scan.getReadType()); 1982 StoreScanner storeScanner = 1983 (StoreScanner) store.getScanner(scan, scan.getFamilyMap().get(family), Long.MAX_VALUE); 1984 assertFalse(storeScanner.isScanUsePread()); 1985 } 1986 1987 @Test 1988 public void testInMemoryCompactionTypeWithLowerCase() throws IOException, InterruptedException { 1989 Configuration conf = HBaseConfiguration.create(); 1990 conf.set("hbase.systemtables.compacting.memstore.type", "eager"); 1991 init(name, conf, 1992 TableDescriptorBuilder.newBuilder( 1993 TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME, "meta".getBytes())), 1994 ColumnFamilyDescriptorBuilder.newBuilder(family) 1995 .setInMemoryCompaction(MemoryCompactionPolicy.NONE).build()); 1996 assertTrue(((MemStoreCompactor) ((CompactingMemStore) store.memstore).compactor).toString() 1997 .startsWith("eager".toUpperCase())); 1998 } 1999 2000 @Test 2001 public void testSpaceQuotaChangeAfterReplacement() throws IOException { 2002 final TableName tn = TableName.valueOf(name); 2003 init(name); 2004 2005 RegionSizeStoreImpl sizeStore = new RegionSizeStoreImpl(); 2006 2007 HStoreFile sf1 = mockStoreFileWithLength(1024L); 2008 HStoreFile sf2 = mockStoreFileWithLength(2048L); 2009 HStoreFile sf3 = mockStoreFileWithLength(4096L); 2010 HStoreFile sf4 = mockStoreFileWithLength(8192L); 2011 2012 RegionInfo regionInfo = RegionInfoBuilder.newBuilder(tn).setStartKey(Bytes.toBytes("a")) 2013 .setEndKey(Bytes.toBytes("b")).build(); 2014 2015 // Compacting two files down to one, reducing size 2016 sizeStore.put(regionInfo, 1024L + 4096L); 2017 store.updateSpaceQuotaAfterFileReplacement(sizeStore, regionInfo, Arrays.asList(sf1, sf3), 2018 Arrays.asList(sf2)); 2019 2020 assertEquals(2048L, sizeStore.getRegionSize(regionInfo).getSize()); 2021 2022 // The same file length in and out should have no change 2023 store.updateSpaceQuotaAfterFileReplacement(sizeStore, regionInfo, Arrays.asList(sf2), 2024 Arrays.asList(sf2)); 2025 2026 assertEquals(2048L, sizeStore.getRegionSize(regionInfo).getSize()); 2027 2028 // Increase the total size used 2029 store.updateSpaceQuotaAfterFileReplacement(sizeStore, regionInfo, Arrays.asList(sf2), 2030 Arrays.asList(sf3)); 2031 2032 assertEquals(4096L, sizeStore.getRegionSize(regionInfo).getSize()); 2033 2034 RegionInfo regionInfo2 = RegionInfoBuilder.newBuilder(tn).setStartKey(Bytes.toBytes("b")) 2035 .setEndKey(Bytes.toBytes("c")).build(); 2036 store.updateSpaceQuotaAfterFileReplacement(sizeStore, regionInfo2, null, Arrays.asList(sf4)); 2037 2038 assertEquals(8192L, sizeStore.getRegionSize(regionInfo2).getSize()); 2039 } 2040 2041 @Test 2042 public void testHFileContextSetWithCFAndTable() throws Exception { 2043 init(name); 2044 StoreFileWriter writer = store.getStoreEngine() 2045 .createWriter(CreateStoreFileWriterParams.create().maxKeyCount(10000L) 2046 .compression(Compression.Algorithm.NONE).isCompaction(true).includeMVCCReadpoint(true) 2047 .includesTag(false).shouldDropBehind(true)); 2048 HFileContext hFileContext = writer.getLiveFileWriter().getFileContext(); 2049 assertArrayEquals(family, hFileContext.getColumnFamily()); 2050 assertArrayEquals(table, hFileContext.getTableName()); 2051 } 2052 2053 // This test is for HBASE-26026, HBase Write be stuck when active segment has no cell 2054 // but its dataSize exceeds inmemoryFlushSize 2055 @Test 2056 public void testCompactingMemStoreNoCellButDataSizeExceedsInmemoryFlushSize() 2057 throws IOException, InterruptedException { 2058 Configuration conf = HBaseConfiguration.create(); 2059 2060 byte[] smallValue = new byte[3]; 2061 byte[] largeValue = new byte[9]; 2062 final long timestamp = EnvironmentEdgeManager.currentTime(); 2063 final long seqId = 100; 2064 final ExtendedCell smallCell = createCell(qf1, timestamp, seqId, smallValue); 2065 final ExtendedCell largeCell = createCell(qf2, timestamp, seqId, largeValue); 2066 int smallCellByteSize = MutableSegment.getCellLength(smallCell); 2067 int largeCellByteSize = MutableSegment.getCellLength(largeCell); 2068 int flushByteSize = smallCellByteSize + largeCellByteSize - 2; 2069 2070 // set CompactingMemStore.inmemoryFlushSize to flushByteSize. 2071 conf.set(HStore.MEMSTORE_CLASS_NAME, MyCompactingMemStore2.class.getName()); 2072 conf.setDouble(CompactingMemStore.IN_MEMORY_FLUSH_THRESHOLD_FACTOR_KEY, 0.005); 2073 conf.set(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, String.valueOf(flushByteSize * 200)); 2074 2075 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family) 2076 .setInMemoryCompaction(MemoryCompactionPolicy.BASIC).build()); 2077 2078 MyCompactingMemStore2 myCompactingMemStore = ((MyCompactingMemStore2) store.memstore); 2079 assertTrue((int) (myCompactingMemStore.getInmemoryFlushSize()) == flushByteSize); 2080 myCompactingMemStore.smallCellPreUpdateCounter.set(0); 2081 myCompactingMemStore.largeCellPreUpdateCounter.set(0); 2082 2083 final AtomicReference<Throwable> exceptionRef = new AtomicReference<Throwable>(); 2084 Thread smallCellThread = new Thread(() -> { 2085 try { 2086 store.add(smallCell, new NonThreadSafeMemStoreSizing()); 2087 } catch (Throwable exception) { 2088 exceptionRef.set(exception); 2089 } 2090 }); 2091 smallCellThread.setName(MyCompactingMemStore2.SMALL_CELL_THREAD_NAME); 2092 smallCellThread.start(); 2093 2094 String oldThreadName = Thread.currentThread().getName(); 2095 try { 2096 /** 2097 * 1.smallCellThread enters CompactingMemStore.checkAndAddToActiveSize first, then 2098 * largeCellThread enters CompactingMemStore.checkAndAddToActiveSize, and then largeCellThread 2099 * invokes flushInMemory. 2100 * <p/> 2101 * 2. After largeCellThread finished CompactingMemStore.flushInMemory method, smallCellThread 2102 * can add cell to currentActive . That is to say when largeCellThread called flushInMemory 2103 * method, CompactingMemStore.active has no cell. 2104 */ 2105 Thread.currentThread().setName(MyCompactingMemStore2.LARGE_CELL_THREAD_NAME); 2106 store.add(largeCell, new NonThreadSafeMemStoreSizing()); 2107 smallCellThread.join(); 2108 2109 for (int i = 0; i < 100; i++) { 2110 long currentTimestamp = timestamp + 100 + i; 2111 ExtendedCell cell = createCell(qf2, currentTimestamp, seqId, largeValue); 2112 store.add(cell, new NonThreadSafeMemStoreSizing()); 2113 } 2114 } finally { 2115 Thread.currentThread().setName(oldThreadName); 2116 } 2117 2118 assertTrue(exceptionRef.get() == null); 2119 2120 } 2121 2122 // This test is for HBASE-26210, HBase Write be stuck when there is cell which size exceeds 2123 // InmemoryFlushSize 2124 @Test 2125 @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS) 2126 public void testCompactingMemStoreCellExceedInmemoryFlushSize() throws Exception { 2127 Configuration conf = HBaseConfiguration.create(); 2128 conf.set(HStore.MEMSTORE_CLASS_NAME, MyCompactingMemStore6.class.getName()); 2129 2130 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family) 2131 .setInMemoryCompaction(MemoryCompactionPolicy.BASIC).build()); 2132 2133 MyCompactingMemStore6 myCompactingMemStore = ((MyCompactingMemStore6) store.memstore); 2134 2135 int size = (int) (myCompactingMemStore.getInmemoryFlushSize()); 2136 byte[] value = new byte[size + 1]; 2137 2138 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 2139 long timestamp = EnvironmentEdgeManager.currentTime(); 2140 long seqId = 100; 2141 ExtendedCell cell = createCell(qf1, timestamp, seqId, value); 2142 int cellByteSize = MutableSegment.getCellLength(cell); 2143 store.add(cell, memStoreSizing); 2144 assertTrue(memStoreSizing.getCellsCount() == 1); 2145 assertTrue(memStoreSizing.getDataSize() == cellByteSize); 2146 // Waiting the in memory compaction completed, see HBASE-26438 2147 myCompactingMemStore.inMemoryCompactionEndCyclicBarrier.await(); 2148 } 2149 2150 /** 2151 * This test is for HBASE-27464, before this JIRA,when init {@link CellChunkImmutableSegment} for 2152 * 'COMPACT' action, we not force copy to current MSLab. When cell size bigger than 2153 * {@link MemStoreLABImpl#maxAlloc}, cell will stay in previous chunk which will recycle after 2154 * segment replace, and we may read wrong data when these chunk reused by others. 2155 */ 2156 @Test 2157 public void testForceCloneOfBigCellForCellChunkImmutableSegment() throws Exception { 2158 Configuration conf = HBaseConfiguration.create(); 2159 int maxAllocByteSize = conf.getInt(MemStoreLAB.MAX_ALLOC_KEY, MemStoreLAB.MAX_ALLOC_DEFAULT); 2160 2161 // Construct big cell,which is large than {@link MemStoreLABImpl#maxAlloc}. 2162 byte[] cellValue = new byte[maxAllocByteSize + 1]; 2163 final long timestamp = EnvironmentEdgeManager.currentTime(); 2164 final long seqId = 100; 2165 final byte[] rowKey1 = Bytes.toBytes("rowKey1"); 2166 final ExtendedCell originalCell1 = createCell(rowKey1, qf1, timestamp, seqId, cellValue); 2167 final byte[] rowKey2 = Bytes.toBytes("rowKey2"); 2168 final ExtendedCell originalCell2 = createCell(rowKey2, qf1, timestamp, seqId, cellValue); 2169 TreeSet<byte[]> quals = new TreeSet<>(Bytes.BYTES_COMPARATOR); 2170 quals.add(qf1); 2171 2172 int cellByteSize = MutableSegment.getCellLength(originalCell1); 2173 int inMemoryFlushByteSize = cellByteSize - 1; 2174 2175 // set CompactingMemStore.inmemoryFlushSize to flushByteSize. 2176 conf.set(HStore.MEMSTORE_CLASS_NAME, MyCompactingMemStore6.class.getName()); 2177 conf.setDouble(CompactingMemStore.IN_MEMORY_FLUSH_THRESHOLD_FACTOR_KEY, 0.005); 2178 conf.set(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, String.valueOf(inMemoryFlushByteSize * 200)); 2179 conf.setBoolean(WALFactory.WAL_ENABLED, false); 2180 2181 // Use {@link MemoryCompactionPolicy#EAGER} for always compacting. 2182 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family) 2183 .setInMemoryCompaction(MemoryCompactionPolicy.EAGER).build()); 2184 2185 MyCompactingMemStore6 myCompactingMemStore = ((MyCompactingMemStore6) store.memstore); 2186 assertTrue((int) (myCompactingMemStore.getInmemoryFlushSize()) == inMemoryFlushByteSize); 2187 2188 // Data chunk Pool is disabled. 2189 assertTrue(ChunkCreator.getInstance().getMaxCount(ChunkType.DATA_CHUNK) == 0); 2190 2191 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 2192 2193 // First compact 2194 store.add(originalCell1, memStoreSizing); 2195 // Waiting for the first in-memory compaction finished 2196 myCompactingMemStore.inMemoryCompactionEndCyclicBarrier.await(); 2197 2198 StoreScanner storeScanner = 2199 (StoreScanner) store.getScanner(new Scan(new Get(rowKey1)), quals, seqId + 1); 2200 SegmentScanner segmentScanner = getTypeKeyValueScanner(storeScanner, SegmentScanner.class); 2201 ExtendedCell resultCell1 = segmentScanner.next(); 2202 assertTrue(PrivateCellUtil.equals(resultCell1, originalCell1)); 2203 int cell1ChunkId = resultCell1.getChunkId(); 2204 assertTrue(cell1ChunkId != ExtendedCell.CELL_NOT_BASED_ON_CHUNK); 2205 assertNull(segmentScanner.next()); 2206 segmentScanner.close(); 2207 storeScanner.close(); 2208 Segment segment = segmentScanner.segment; 2209 assertTrue(segment instanceof CellChunkImmutableSegment); 2210 MemStoreLABImpl memStoreLAB1 = (MemStoreLABImpl) (segmentScanner.segment.getMemStoreLAB()); 2211 assertTrue(!memStoreLAB1.isClosed()); 2212 assertTrue(!memStoreLAB1.chunks.isEmpty()); 2213 assertTrue(!memStoreLAB1.isReclaimed()); 2214 2215 // Second compact 2216 store.add(originalCell2, memStoreSizing); 2217 // Waiting for the second in-memory compaction finished 2218 myCompactingMemStore.inMemoryCompactionEndCyclicBarrier.await(); 2219 2220 // Before HBASE-27464, here may throw java.lang.IllegalArgumentException: In CellChunkMap, cell 2221 // must be associated with chunk.. We were looking for a cell at index 0. 2222 // The cause for this exception is because the data chunk Pool is disabled,when the data chunks 2223 // are recycled after the second in-memory compaction finished,the 2224 // {@link ChunkCreator.putbackChunks} method does not put the chunks back to the data chunk 2225 // pool,it just removes them from {@link ChunkCreator#chunkIdMap},so in 2226 // {@link CellChunkMap#getCell} we could not get the data chunk by chunkId. 2227 storeScanner = (StoreScanner) store.getScanner(new Scan(new Get(rowKey1)), quals, seqId + 1); 2228 segmentScanner = getTypeKeyValueScanner(storeScanner, SegmentScanner.class); 2229 ExtendedCell newResultCell1 = segmentScanner.next(); 2230 assertTrue(newResultCell1 != resultCell1); 2231 assertTrue(PrivateCellUtil.equals(newResultCell1, originalCell1)); 2232 2233 ExtendedCell resultCell2 = segmentScanner.next(); 2234 assertTrue(PrivateCellUtil.equals(resultCell2, originalCell2)); 2235 assertNull(segmentScanner.next()); 2236 segmentScanner.close(); 2237 storeScanner.close(); 2238 2239 segment = segmentScanner.segment; 2240 assertTrue(segment instanceof CellChunkImmutableSegment); 2241 MemStoreLABImpl memStoreLAB2 = (MemStoreLABImpl) (segmentScanner.segment.getMemStoreLAB()); 2242 assertTrue(!memStoreLAB2.isClosed()); 2243 assertTrue(!memStoreLAB2.chunks.isEmpty()); 2244 assertTrue(!memStoreLAB2.isReclaimed()); 2245 assertTrue(memStoreLAB1.isClosed()); 2246 assertTrue(memStoreLAB1.chunks.isEmpty()); 2247 assertTrue(memStoreLAB1.isReclaimed()); 2248 } 2249 2250 // This test is for HBASE-26210 also, test write large cell and small cell concurrently when 2251 // InmemoryFlushSize is smaller,equal with and larger than cell size. 2252 @Test 2253 public void testCompactingMemStoreWriteLargeCellAndSmallCellConcurrently() 2254 throws IOException, InterruptedException { 2255 doWriteTestLargeCellAndSmallCellConcurrently( 2256 (smallCellByteSize, largeCellByteSize) -> largeCellByteSize - 1); 2257 doWriteTestLargeCellAndSmallCellConcurrently( 2258 (smallCellByteSize, largeCellByteSize) -> largeCellByteSize); 2259 doWriteTestLargeCellAndSmallCellConcurrently( 2260 (smallCellByteSize, largeCellByteSize) -> smallCellByteSize + largeCellByteSize - 1); 2261 doWriteTestLargeCellAndSmallCellConcurrently( 2262 (smallCellByteSize, largeCellByteSize) -> smallCellByteSize + largeCellByteSize); 2263 doWriteTestLargeCellAndSmallCellConcurrently( 2264 (smallCellByteSize, largeCellByteSize) -> smallCellByteSize + largeCellByteSize + 1); 2265 } 2266 2267 private void doWriteTestLargeCellAndSmallCellConcurrently(IntBinaryOperator getFlushByteSize) 2268 throws IOException, InterruptedException { 2269 2270 Configuration conf = HBaseConfiguration.create(); 2271 2272 byte[] smallValue = new byte[3]; 2273 byte[] largeValue = new byte[100]; 2274 final long timestamp = EnvironmentEdgeManager.currentTime(); 2275 final long seqId = 100; 2276 final Cell smallCell = createCell(qf1, timestamp, seqId, smallValue); 2277 final Cell largeCell = createCell(qf2, timestamp, seqId, largeValue); 2278 int smallCellByteSize = MutableSegment.getCellLength(smallCell); 2279 int largeCellByteSize = MutableSegment.getCellLength(largeCell); 2280 int flushByteSize = getFlushByteSize.applyAsInt(smallCellByteSize, largeCellByteSize); 2281 boolean flushByteSizeLessThanSmallAndLargeCellSize = 2282 flushByteSize < (smallCellByteSize + largeCellByteSize); 2283 2284 conf.set(HStore.MEMSTORE_CLASS_NAME, MyCompactingMemStore3.class.getName()); 2285 conf.setDouble(CompactingMemStore.IN_MEMORY_FLUSH_THRESHOLD_FACTOR_KEY, 0.005); 2286 conf.set(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, String.valueOf(flushByteSize * 200)); 2287 2288 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family) 2289 .setInMemoryCompaction(MemoryCompactionPolicy.BASIC).build()); 2290 2291 MyCompactingMemStore3 myCompactingMemStore = ((MyCompactingMemStore3) store.memstore); 2292 assertTrue((int) (myCompactingMemStore.getInmemoryFlushSize()) == flushByteSize); 2293 myCompactingMemStore.disableCompaction(); 2294 if (flushByteSizeLessThanSmallAndLargeCellSize) { 2295 myCompactingMemStore.flushByteSizeLessThanSmallAndLargeCellSize = true; 2296 } else { 2297 myCompactingMemStore.flushByteSizeLessThanSmallAndLargeCellSize = false; 2298 } 2299 2300 final ThreadSafeMemStoreSizing memStoreSizing = new ThreadSafeMemStoreSizing(); 2301 final AtomicLong totalCellByteSize = new AtomicLong(0); 2302 final AtomicReference<Throwable> exceptionRef = new AtomicReference<Throwable>(); 2303 Thread smallCellThread = new Thread(() -> { 2304 try { 2305 for (int i = 1; i <= MyCompactingMemStore3.CELL_COUNT; i++) { 2306 long currentTimestamp = timestamp + i; 2307 ExtendedCell cell = createCell(qf1, currentTimestamp, seqId, smallValue); 2308 totalCellByteSize.addAndGet(MutableSegment.getCellLength(cell)); 2309 store.add(cell, memStoreSizing); 2310 } 2311 } catch (Throwable exception) { 2312 exceptionRef.set(exception); 2313 2314 } 2315 }); 2316 smallCellThread.setName(MyCompactingMemStore3.SMALL_CELL_THREAD_NAME); 2317 smallCellThread.start(); 2318 2319 String oldThreadName = Thread.currentThread().getName(); 2320 try { 2321 /** 2322 * When flushByteSizeLessThanSmallAndLargeCellSize is true: 2323 * </p> 2324 * 1.smallCellThread enters MyCompactingMemStore3.checkAndAddToActiveSize first, then 2325 * largeCellThread enters MyCompactingMemStore3.checkAndAddToActiveSize, and then 2326 * largeCellThread invokes flushInMemory. 2327 * <p/> 2328 * 2. After largeCellThread finished CompactingMemStore.flushInMemory method, smallCellThread 2329 * can run into MyCompactingMemStore3.checkAndAddToActiveSize again. 2330 * <p/> 2331 * When flushByteSizeLessThanSmallAndLargeCellSize is false: smallCellThread and 2332 * largeCellThread concurrently write one cell and wait each other, and then write another 2333 * cell etc. 2334 */ 2335 Thread.currentThread().setName(MyCompactingMemStore3.LARGE_CELL_THREAD_NAME); 2336 for (int i = 1; i <= MyCompactingMemStore3.CELL_COUNT; i++) { 2337 long currentTimestamp = timestamp + i; 2338 ExtendedCell cell = createCell(qf2, currentTimestamp, seqId, largeValue); 2339 totalCellByteSize.addAndGet(MutableSegment.getCellLength(cell)); 2340 store.add(cell, memStoreSizing); 2341 } 2342 smallCellThread.join(); 2343 2344 assertTrue(exceptionRef.get() == null); 2345 assertTrue(memStoreSizing.getCellsCount() == (MyCompactingMemStore3.CELL_COUNT * 2)); 2346 assertTrue(memStoreSizing.getDataSize() == totalCellByteSize.get()); 2347 if (flushByteSizeLessThanSmallAndLargeCellSize) { 2348 assertTrue(myCompactingMemStore.flushCounter.get() == MyCompactingMemStore3.CELL_COUNT); 2349 } else { 2350 assertTrue( 2351 myCompactingMemStore.flushCounter.get() <= (MyCompactingMemStore3.CELL_COUNT - 1)); 2352 } 2353 } finally { 2354 Thread.currentThread().setName(oldThreadName); 2355 } 2356 } 2357 2358 /** 2359 * <pre> 2360 * This test is for HBASE-26384, 2361 * test {@link CompactingMemStore#flattenOneSegment} and {@link CompactingMemStore#snapshot()} 2362 * execute concurrently. 2363 * The threads sequence before HBASE-26384 is(The bug only exists for branch-2,and I add UTs 2364 * for both branch-2 and master): 2365 * 1. The {@link CompactingMemStore} size exceeds 2366 * {@link CompactingMemStore#getInmemoryFlushSize()},the write thread adds a new 2367 * {@link ImmutableSegment} to the head of {@link CompactingMemStore#pipeline},and start a 2368 * in memory compact thread to execute {@link CompactingMemStore#inMemoryCompaction}. 2369 * 2. The in memory compact thread starts and then stopping before 2370 * {@link CompactingMemStore#flattenOneSegment}. 2371 * 3. The snapshot thread starts {@link CompactingMemStore#snapshot} concurrently,after the 2372 * snapshot thread executing {@link CompactingMemStore#getImmutableSegments},the in memory 2373 * compact thread continues. 2374 * Assuming {@link VersionedSegmentsList#version} returned from 2375 * {@link CompactingMemStore#getImmutableSegments} is v. 2376 * 4. The snapshot thread stopping before {@link CompactingMemStore#swapPipelineWithNull}. 2377 * 5. The in memory compact thread completes {@link CompactingMemStore#flattenOneSegment}, 2378 * {@link CompactionPipeline#version} is still v. 2379 * 6. The snapshot thread continues {@link CompactingMemStore#swapPipelineWithNull}, and because 2380 * {@link CompactionPipeline#version} is v, {@link CompactingMemStore#swapPipelineWithNull} 2381 * thinks it is successful and continue flushing,but the {@link ImmutableSegment} in 2382 * {@link CompactionPipeline} has changed because 2383 * {@link CompactingMemStore#flattenOneSegment},so the {@link ImmutableSegment} is not 2384 * removed in fact and still remaining in {@link CompactionPipeline}. 2385 * 2386 * After HBASE-26384, the 5-6 step is changed to following, which is expected behavior: 2387 * 5. The in memory compact thread completes {@link CompactingMemStore#flattenOneSegment}, 2388 * {@link CompactingMemStore#flattenOneSegment} change {@link CompactionPipeline#version} to 2389 * v+1. 2390 * 6. The snapshot thread continues {@link CompactingMemStore#swapPipelineWithNull}, and because 2391 * {@link CompactionPipeline#version} is v+1, {@link CompactingMemStore#swapPipelineWithNull} 2392 * failed and retry the while loop in {@link CompactingMemStore#pushPipelineToSnapshot} once 2393 * again, because there is no concurrent {@link CompactingMemStore#inMemoryCompaction} now, 2394 * {@link CompactingMemStore#swapPipelineWithNull} succeeds. 2395 * </pre> 2396 */ 2397 @Test 2398 public void testFlattenAndSnapshotCompactingMemStoreConcurrently() throws Exception { 2399 Configuration conf = HBaseConfiguration.create(); 2400 2401 byte[] smallValue = new byte[3]; 2402 byte[] largeValue = new byte[9]; 2403 final long timestamp = EnvironmentEdgeManager.currentTime(); 2404 final long seqId = 100; 2405 final ExtendedCell smallCell = createCell(qf1, timestamp, seqId, smallValue); 2406 final ExtendedCell largeCell = createCell(qf2, timestamp, seqId, largeValue); 2407 int smallCellByteSize = MutableSegment.getCellLength(smallCell); 2408 int largeCellByteSize = MutableSegment.getCellLength(largeCell); 2409 int totalCellByteSize = (smallCellByteSize + largeCellByteSize); 2410 int flushByteSize = totalCellByteSize - 2; 2411 2412 // set CompactingMemStore.inmemoryFlushSize to flushByteSize. 2413 conf.set(HStore.MEMSTORE_CLASS_NAME, MyCompactingMemStore4.class.getName()); 2414 conf.setDouble(CompactingMemStore.IN_MEMORY_FLUSH_THRESHOLD_FACTOR_KEY, 0.005); 2415 conf.set(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, String.valueOf(flushByteSize * 200)); 2416 2417 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family) 2418 .setInMemoryCompaction(MemoryCompactionPolicy.BASIC).build()); 2419 2420 MyCompactingMemStore4 myCompactingMemStore = ((MyCompactingMemStore4) store.memstore); 2421 assertTrue((int) (myCompactingMemStore.getInmemoryFlushSize()) == flushByteSize); 2422 2423 store.add(smallCell, new NonThreadSafeMemStoreSizing()); 2424 store.add(largeCell, new NonThreadSafeMemStoreSizing()); 2425 2426 String oldThreadName = Thread.currentThread().getName(); 2427 try { 2428 Thread.currentThread().setName(MyCompactingMemStore4.TAKE_SNAPSHOT_THREAD_NAME); 2429 /** 2430 * {@link CompactingMemStore#snapshot} must wait the in memory compact thread enters 2431 * {@link CompactingMemStore#flattenOneSegment},because {@link CompactingMemStore#snapshot} 2432 * would invoke {@link CompactingMemStore#stopCompaction}. 2433 */ 2434 myCompactingMemStore.snapShotStartCyclicCyclicBarrier.await(); 2435 2436 MemStoreSnapshot memStoreSnapshot = myCompactingMemStore.snapshot(); 2437 myCompactingMemStore.inMemoryCompactionEndCyclicBarrier.await(); 2438 2439 assertTrue(memStoreSnapshot.getCellsCount() == 2); 2440 assertTrue(((int) (memStoreSnapshot.getDataSize())) == totalCellByteSize); 2441 VersionedSegmentsList segments = myCompactingMemStore.getImmutableSegments(); 2442 assertTrue(segments.getNumOfSegments() == 0); 2443 assertTrue(segments.getNumOfCells() == 0); 2444 assertTrue(myCompactingMemStore.setInMemoryCompactionFlagCounter.get() == 1); 2445 assertTrue(myCompactingMemStore.swapPipelineWithNullCounter.get() == 2); 2446 } finally { 2447 Thread.currentThread().setName(oldThreadName); 2448 } 2449 } 2450 2451 /** 2452 * <pre> 2453 * This test is for HBASE-26384, 2454 * test {@link CompactingMemStore#flattenOneSegment}{@link CompactingMemStore#snapshot()} 2455 * and writeMemStore execute concurrently. 2456 * The threads sequence before HBASE-26384 is(The bug only exists for branch-2,and I add UTs 2457 * for both branch-2 and master): 2458 * 1. The {@link CompactingMemStore} size exceeds 2459 * {@link CompactingMemStore#getInmemoryFlushSize()},the write thread adds a new 2460 * {@link ImmutableSegment} to the head of {@link CompactingMemStore#pipeline},and start a 2461 * in memory compact thread to execute {@link CompactingMemStore#inMemoryCompaction}. 2462 * 2. The in memory compact thread starts and then stopping before 2463 * {@link CompactingMemStore#flattenOneSegment}. 2464 * 3. The snapshot thread starts {@link CompactingMemStore#snapshot} concurrently,after the 2465 * snapshot thread executing {@link CompactingMemStore#getImmutableSegments},the in memory 2466 * compact thread continues. 2467 * Assuming {@link VersionedSegmentsList#version} returned from 2468 * {@link CompactingMemStore#getImmutableSegments} is v. 2469 * 4. The snapshot thread stopping before {@link CompactingMemStore#swapPipelineWithNull}. 2470 * 5. The in memory compact thread completes {@link CompactingMemStore#flattenOneSegment}, 2471 * {@link CompactionPipeline#version} is still v. 2472 * 6. The snapshot thread continues {@link CompactingMemStore#swapPipelineWithNull}, and because 2473 * {@link CompactionPipeline#version} is v, {@link CompactingMemStore#swapPipelineWithNull} 2474 * thinks it is successful and continue flushing,but the {@link ImmutableSegment} in 2475 * {@link CompactionPipeline} has changed because 2476 * {@link CompactingMemStore#flattenOneSegment},so the {@link ImmutableSegment} is not 2477 * removed in fact and still remaining in {@link CompactionPipeline}. 2478 * 2479 * After HBASE-26384, the 5-6 step is changed to following, which is expected behavior, 2480 * and I add step 7-8 to test there is new segment added before retry. 2481 * 5. The in memory compact thread completes {@link CompactingMemStore#flattenOneSegment}, 2482 * {@link CompactingMemStore#flattenOneSegment} change {@link CompactionPipeline#version} to 2483 * v+1. 2484 * 6. The snapshot thread continues {@link CompactingMemStore#swapPipelineWithNull}, and because 2485 * {@link CompactionPipeline#version} is v+1, {@link CompactingMemStore#swapPipelineWithNull} 2486 * failed and retry,{@link VersionedSegmentsList#version} returned from 2487 * {@link CompactingMemStore#getImmutableSegments} is v+1. 2488 * 7. The write thread continues writing to {@link CompactingMemStore} and 2489 * {@link CompactingMemStore} size exceeds {@link CompactingMemStore#getInmemoryFlushSize()}, 2490 * {@link CompactingMemStore#flushInMemory(MutableSegment)} is called and a new 2491 * {@link ImmutableSegment} is added to the head of {@link CompactingMemStore#pipeline}, 2492 * {@link CompactionPipeline#version} is still v+1. 2493 * 8. The snapshot thread continues {@link CompactingMemStore#swapPipelineWithNull}, and because 2494 * {@link CompactionPipeline#version} is still v+1, 2495 * {@link CompactingMemStore#swapPipelineWithNull} succeeds.The new {@link ImmutableSegment} 2496 * remained at the head of {@link CompactingMemStore#pipeline},the old is removed by 2497 * {@link CompactingMemStore#swapPipelineWithNull}. 2498 * </pre> 2499 */ 2500 @Test 2501 public void testFlattenSnapshotWriteCompactingMemeStoreConcurrently() throws Exception { 2502 Configuration conf = HBaseConfiguration.create(); 2503 2504 byte[] smallValue = new byte[3]; 2505 byte[] largeValue = new byte[9]; 2506 final long timestamp = EnvironmentEdgeManager.currentTime(); 2507 final long seqId = 100; 2508 final ExtendedCell smallCell = createCell(qf1, timestamp, seqId, smallValue); 2509 final ExtendedCell largeCell = createCell(qf2, timestamp, seqId, largeValue); 2510 int smallCellByteSize = MutableSegment.getCellLength(smallCell); 2511 int largeCellByteSize = MutableSegment.getCellLength(largeCell); 2512 int firstWriteCellByteSize = (smallCellByteSize + largeCellByteSize); 2513 int flushByteSize = firstWriteCellByteSize - 2; 2514 2515 // set CompactingMemStore.inmemoryFlushSize to flushByteSize. 2516 conf.set(HStore.MEMSTORE_CLASS_NAME, MyCompactingMemStore5.class.getName()); 2517 conf.setDouble(CompactingMemStore.IN_MEMORY_FLUSH_THRESHOLD_FACTOR_KEY, 0.005); 2518 conf.set(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, String.valueOf(flushByteSize * 200)); 2519 2520 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family) 2521 .setInMemoryCompaction(MemoryCompactionPolicy.BASIC).build()); 2522 2523 final MyCompactingMemStore5 myCompactingMemStore = ((MyCompactingMemStore5) store.memstore); 2524 assertTrue((int) (myCompactingMemStore.getInmemoryFlushSize()) == flushByteSize); 2525 2526 store.add(smallCell, new NonThreadSafeMemStoreSizing()); 2527 store.add(largeCell, new NonThreadSafeMemStoreSizing()); 2528 2529 final AtomicReference<Throwable> exceptionRef = new AtomicReference<Throwable>(); 2530 final ExtendedCell writeAgainCell1 = createCell(qf3, timestamp, seqId + 1, largeValue); 2531 final ExtendedCell writeAgainCell2 = createCell(qf4, timestamp, seqId + 1, largeValue); 2532 final int writeAgainCellByteSize = 2533 MutableSegment.getCellLength(writeAgainCell1) + MutableSegment.getCellLength(writeAgainCell2); 2534 final Thread writeAgainThread = new Thread(() -> { 2535 try { 2536 myCompactingMemStore.writeMemStoreAgainStartCyclicBarrier.await(); 2537 2538 store.add(writeAgainCell1, new NonThreadSafeMemStoreSizing()); 2539 store.add(writeAgainCell2, new NonThreadSafeMemStoreSizing()); 2540 2541 myCompactingMemStore.writeMemStoreAgainEndCyclicBarrier.await(); 2542 } catch (Throwable exception) { 2543 exceptionRef.set(exception); 2544 } 2545 }); 2546 writeAgainThread.setName(MyCompactingMemStore5.WRITE_AGAIN_THREAD_NAME); 2547 writeAgainThread.start(); 2548 2549 String oldThreadName = Thread.currentThread().getName(); 2550 try { 2551 Thread.currentThread().setName(MyCompactingMemStore5.TAKE_SNAPSHOT_THREAD_NAME); 2552 /** 2553 * {@link CompactingMemStore#snapshot} must wait the in memory compact thread enters 2554 * {@link CompactingMemStore#flattenOneSegment},because {@link CompactingMemStore#snapshot} 2555 * would invoke {@link CompactingMemStore#stopCompaction}. 2556 */ 2557 myCompactingMemStore.snapShotStartCyclicCyclicBarrier.await(); 2558 MemStoreSnapshot memStoreSnapshot = myCompactingMemStore.snapshot(); 2559 myCompactingMemStore.inMemoryCompactionEndCyclicBarrier.await(); 2560 writeAgainThread.join(); 2561 2562 assertTrue(memStoreSnapshot.getCellsCount() == 2); 2563 assertTrue(((int) (memStoreSnapshot.getDataSize())) == firstWriteCellByteSize); 2564 VersionedSegmentsList segments = myCompactingMemStore.getImmutableSegments(); 2565 assertTrue(segments.getNumOfSegments() == 1); 2566 assertTrue( 2567 ((int) (segments.getStoreSegments().get(0).getDataSize())) == writeAgainCellByteSize); 2568 assertTrue(segments.getNumOfCells() == 2); 2569 assertTrue(myCompactingMemStore.setInMemoryCompactionFlagCounter.get() == 2); 2570 assertTrue(exceptionRef.get() == null); 2571 assertTrue(myCompactingMemStore.swapPipelineWithNullCounter.get() == 2); 2572 } finally { 2573 Thread.currentThread().setName(oldThreadName); 2574 } 2575 } 2576 2577 /** 2578 * <pre> 2579 * This test is for HBASE-26465, 2580 * test {@link DefaultMemStore#clearSnapshot} and {@link DefaultMemStore#getScanners} execute 2581 * concurrently. The threads sequence before HBASE-26465 is: 2582 * 1.The flush thread starts {@link DefaultMemStore} flushing after some cells have be added to 2583 * {@link DefaultMemStore}. 2584 * 2.The flush thread stopping before {@link DefaultMemStore#clearSnapshot} in 2585 * {@link HStore#updateStorefiles} after completed flushing memStore to hfile. 2586 * 3.The scan thread starts and stopping after {@link DefaultMemStore#getSnapshotSegments} in 2587 * {@link DefaultMemStore#getScanners},here the scan thread gets the 2588 * {@link DefaultMemStore#snapshot} which is created by the flush thread. 2589 * 4.The flush thread continues {@link DefaultMemStore#clearSnapshot} and close 2590 * {@link DefaultMemStore#snapshot},because the reference count of the corresponding 2591 * {@link MemStoreLABImpl} is 0, the {@link Chunk}s in corresponding {@link MemStoreLABImpl} 2592 * are recycled. 2593 * 5.The scan thread continues {@link DefaultMemStore#getScanners},and create a 2594 * {@link SegmentScanner} for this {@link DefaultMemStore#snapshot}, and increase the 2595 * reference count of the corresponding {@link MemStoreLABImpl}, but {@link Chunk}s in 2596 * corresponding {@link MemStoreLABImpl} are recycled by step 4, and these {@link Chunk}s may 2597 * be overwritten by other write threads,which may cause serious problem. 2598 * After HBASE-26465,{@link DefaultMemStore#getScanners} and 2599 * {@link DefaultMemStore#clearSnapshot} could not execute concurrently. 2600 * </pre> 2601 */ 2602 @Test 2603 public void testClearSnapshotGetScannerConcurrently() throws Exception { 2604 Configuration conf = HBaseConfiguration.create(); 2605 2606 byte[] smallValue = new byte[3]; 2607 byte[] largeValue = new byte[9]; 2608 final long timestamp = EnvironmentEdgeManager.currentTime(); 2609 final long seqId = 100; 2610 final ExtendedCell smallCell = createCell(qf1, timestamp, seqId, smallValue); 2611 final ExtendedCell largeCell = createCell(qf2, timestamp, seqId, largeValue); 2612 TreeSet<byte[]> quals = new TreeSet<>(Bytes.BYTES_COMPARATOR); 2613 quals.add(qf1); 2614 quals.add(qf2); 2615 2616 conf.set(HStore.MEMSTORE_CLASS_NAME, MyDefaultMemStore.class.getName()); 2617 conf.setBoolean(WALFactory.WAL_ENABLED, false); 2618 2619 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family).build()); 2620 MyDefaultMemStore myDefaultMemStore = (MyDefaultMemStore) (store.memstore); 2621 myDefaultMemStore.store = store; 2622 2623 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 2624 store.add(smallCell, memStoreSizing); 2625 store.add(largeCell, memStoreSizing); 2626 2627 final AtomicReference<Throwable> exceptionRef = new AtomicReference<Throwable>(); 2628 final Thread flushThread = new Thread(() -> { 2629 try { 2630 flushStore(store, id++); 2631 } catch (Throwable exception) { 2632 exceptionRef.set(exception); 2633 } 2634 }); 2635 flushThread.setName(MyDefaultMemStore.FLUSH_THREAD_NAME); 2636 flushThread.start(); 2637 2638 String oldThreadName = Thread.currentThread().getName(); 2639 StoreScanner storeScanner = null; 2640 try { 2641 Thread.currentThread().setName(MyDefaultMemStore.GET_SCANNER_THREAD_NAME); 2642 2643 /** 2644 * Wait flush thread stopping before {@link DefaultMemStore#doClearSnapshot} 2645 */ 2646 myDefaultMemStore.getScannerCyclicBarrier.await(); 2647 2648 storeScanner = (StoreScanner) store.getScanner(new Scan(new Get(row)), quals, seqId + 1); 2649 flushThread.join(); 2650 2651 if (myDefaultMemStore.shouldWait) { 2652 SegmentScanner segmentScanner = getTypeKeyValueScanner(storeScanner, SegmentScanner.class); 2653 MemStoreLABImpl memStoreLAB = (MemStoreLABImpl) (segmentScanner.segment.getMemStoreLAB()); 2654 assertTrue(memStoreLAB.isClosed()); 2655 assertTrue(!memStoreLAB.chunks.isEmpty()); 2656 assertTrue(!memStoreLAB.isReclaimed()); 2657 2658 ExtendedCell cell1 = segmentScanner.next(); 2659 PrivateCellUtil.equals(smallCell, cell1); 2660 ExtendedCell cell2 = segmentScanner.next(); 2661 PrivateCellUtil.equals(largeCell, cell2); 2662 assertNull(segmentScanner.next()); 2663 } else { 2664 List<ExtendedCell> results = new ArrayList<>(); 2665 storeScanner.next(results); 2666 assertEquals(2, results.size()); 2667 PrivateCellUtil.equals(smallCell, results.get(0)); 2668 PrivateCellUtil.equals(largeCell, results.get(1)); 2669 } 2670 assertTrue(exceptionRef.get() == null); 2671 } finally { 2672 if (storeScanner != null) { 2673 storeScanner.close(); 2674 } 2675 Thread.currentThread().setName(oldThreadName); 2676 } 2677 } 2678 2679 @SuppressWarnings("unchecked") 2680 private <T> T getTypeKeyValueScanner(StoreScanner storeScanner, Class<T> keyValueScannerClass) { 2681 List<T> resultScanners = new ArrayList<T>(); 2682 for (KeyValueScanner keyValueScanner : storeScanner.currentScanners) { 2683 if (keyValueScannerClass.isInstance(keyValueScanner)) { 2684 resultScanners.add((T) keyValueScanner); 2685 } 2686 } 2687 assertTrue(resultScanners.size() == 1); 2688 return resultScanners.get(0); 2689 } 2690 2691 @Test 2692 public void testOnConfigurationChange() throws IOException { 2693 final int COMMON_MAX_FILES_TO_COMPACT = 10; 2694 final int NEW_COMMON_MAX_FILES_TO_COMPACT = 8; 2695 final int STORE_MAX_FILES_TO_COMPACT = 6; 2696 2697 // Build a table that its maxFileToCompact different from common configuration. 2698 Configuration conf = HBaseConfiguration.create(); 2699 conf.setInt(CompactionConfiguration.HBASE_HSTORE_COMPACTION_MAX_KEY, 2700 COMMON_MAX_FILES_TO_COMPACT); 2701 conf.setBoolean(CACHE_DATA_ON_READ_KEY, false); 2702 conf.setBoolean(CACHE_BLOCKS_ON_WRITE_KEY, true); 2703 conf.setBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, true); 2704 ColumnFamilyDescriptor hcd = ColumnFamilyDescriptorBuilder.newBuilder(family) 2705 .setConfiguration(CompactionConfiguration.HBASE_HSTORE_COMPACTION_MAX_KEY, 2706 String.valueOf(STORE_MAX_FILES_TO_COMPACT)) 2707 .build(); 2708 init(name, conf, hcd); 2709 2710 // After updating common configuration, the conf in HStore itself must not be changed. 2711 conf.setInt(CompactionConfiguration.HBASE_HSTORE_COMPACTION_MAX_KEY, 2712 NEW_COMMON_MAX_FILES_TO_COMPACT); 2713 this.store.onConfigurationChange(conf); 2714 2715 assertEquals(STORE_MAX_FILES_TO_COMPACT, 2716 store.getStoreEngine().getCompactionPolicy().getConf().getMaxFilesToCompact()); 2717 2718 assertEquals(conf.getBoolean(CACHE_DATA_ON_READ_KEY, DEFAULT_CACHE_DATA_ON_READ), false); 2719 assertEquals(conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_DATA_ON_WRITE), true); 2720 assertEquals(conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, DEFAULT_EVICT_ON_CLOSE), true); 2721 2722 // reset to default values 2723 conf.getBoolean(CACHE_DATA_ON_READ_KEY, DEFAULT_CACHE_DATA_ON_READ); 2724 conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_DATA_ON_WRITE); 2725 conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, DEFAULT_EVICT_ON_CLOSE); 2726 this.store.onConfigurationChange(conf); 2727 } 2728 2729 /** 2730 * This test is for HBASE-26476 2731 */ 2732 @Test 2733 public void testExtendsDefaultMemStore() throws Exception { 2734 Configuration conf = HBaseConfiguration.create(); 2735 conf.setBoolean(WALFactory.WAL_ENABLED, false); 2736 2737 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family).build()); 2738 assertTrue(this.store.memstore.getClass() == DefaultMemStore.class); 2739 tearDown(); 2740 2741 conf.set(HStore.MEMSTORE_CLASS_NAME, CustomDefaultMemStore.class.getName()); 2742 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family).build()); 2743 assertTrue(this.store.memstore.getClass() == CustomDefaultMemStore.class); 2744 } 2745 2746 static class CustomDefaultMemStore extends DefaultMemStore { 2747 2748 public CustomDefaultMemStore(Configuration conf, CellComparator c, 2749 RegionServicesForStores regionServices) { 2750 super(conf, c, regionServices); 2751 } 2752 2753 } 2754 2755 /** 2756 * This test is for HBASE-26488 2757 */ 2758 @Test 2759 public void testMemoryLeakWhenFlushMemStoreRetrying() throws Exception { 2760 Configuration conf = HBaseConfiguration.create(); 2761 2762 byte[] smallValue = new byte[3]; 2763 byte[] largeValue = new byte[9]; 2764 final long timestamp = EnvironmentEdgeManager.currentTime(); 2765 final long seqId = 100; 2766 final ExtendedCell smallCell = createCell(qf1, timestamp, seqId, smallValue); 2767 final ExtendedCell largeCell = createCell(qf2, timestamp, seqId, largeValue); 2768 TreeSet<byte[]> quals = new TreeSet<>(Bytes.BYTES_COMPARATOR); 2769 quals.add(qf1); 2770 quals.add(qf2); 2771 2772 conf.set(HStore.MEMSTORE_CLASS_NAME, MyDefaultMemStore1.class.getName()); 2773 conf.setBoolean(WALFactory.WAL_ENABLED, false); 2774 conf.set(DefaultStoreEngine.DEFAULT_STORE_FLUSHER_CLASS_KEY, 2775 MyDefaultStoreFlusher.class.getName()); 2776 2777 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family).build()); 2778 MyDefaultMemStore1 myDefaultMemStore = (MyDefaultMemStore1) (store.memstore); 2779 assertTrue((store.storeEngine.getStoreFlusher()) instanceof MyDefaultStoreFlusher); 2780 2781 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 2782 store.add(smallCell, memStoreSizing); 2783 store.add(largeCell, memStoreSizing); 2784 flushStore(store, id++); 2785 2786 MemStoreLABImpl memStoreLAB = 2787 (MemStoreLABImpl) (myDefaultMemStore.snapshotImmutableSegment.getMemStoreLAB()); 2788 assertTrue(memStoreLAB.isClosed()); 2789 assertTrue(memStoreLAB.getRefCntValue() == 0); 2790 assertTrue(memStoreLAB.isReclaimed()); 2791 assertTrue(memStoreLAB.chunks.isEmpty()); 2792 StoreScanner storeScanner = null; 2793 try { 2794 storeScanner = (StoreScanner) store.getScanner(new Scan(new Get(row)), quals, seqId + 1); 2795 assertTrue(store.storeEngine.getStoreFileManager().getStorefileCount() == 1); 2796 assertTrue(store.memstore.size().getCellsCount() == 0); 2797 assertTrue(store.memstore.getSnapshotSize().getCellsCount() == 0); 2798 assertTrue(storeScanner.currentScanners.size() == 1); 2799 assertTrue(storeScanner.currentScanners.get(0) instanceof StoreFileScanner); 2800 2801 List<ExtendedCell> results = new ArrayList<>(); 2802 storeScanner.next(results); 2803 assertEquals(2, results.size()); 2804 PrivateCellUtil.equals(smallCell, results.get(0)); 2805 PrivateCellUtil.equals(largeCell, results.get(1)); 2806 } finally { 2807 if (storeScanner != null) { 2808 storeScanner.close(); 2809 } 2810 } 2811 } 2812 2813 static class MyDefaultMemStore1 extends DefaultMemStore { 2814 2815 private ImmutableSegment snapshotImmutableSegment; 2816 2817 public MyDefaultMemStore1(Configuration conf, CellComparator c, 2818 RegionServicesForStores regionServices) { 2819 super(conf, c, regionServices); 2820 } 2821 2822 @Override 2823 public MemStoreSnapshot snapshot() { 2824 MemStoreSnapshot result = super.snapshot(); 2825 this.snapshotImmutableSegment = snapshot; 2826 return result; 2827 } 2828 2829 } 2830 2831 public static class MyDefaultStoreFlusher extends DefaultStoreFlusher { 2832 private static final AtomicInteger failCounter = new AtomicInteger(1); 2833 private static final AtomicInteger counter = new AtomicInteger(0); 2834 2835 public MyDefaultStoreFlusher(Configuration conf, HStore store) { 2836 super(conf, store); 2837 } 2838 2839 @Override 2840 public List<Path> flushSnapshot(MemStoreSnapshot snapshot, long cacheFlushId, 2841 MonitoredTask status, ThroughputController throughputController, 2842 FlushLifeCycleTracker tracker, Consumer<Path> writerCreationTracker) throws IOException { 2843 counter.incrementAndGet(); 2844 return super.flushSnapshot(snapshot, cacheFlushId, status, throughputController, tracker, 2845 writerCreationTracker); 2846 } 2847 2848 @Override 2849 protected void performFlush(InternalScanner scanner, final CellSink sink, 2850 ThroughputController throughputController) throws IOException { 2851 2852 final int currentCount = counter.get(); 2853 CellSink newCellSink = (cell) -> { 2854 if (currentCount <= failCounter.get()) { 2855 throw new IOException("Simulated exception by tests"); 2856 } 2857 sink.append(cell); 2858 }; 2859 super.performFlush(scanner, newCellSink, throughputController); 2860 } 2861 } 2862 2863 /** 2864 * This test is for HBASE-26494, test the {@link RefCnt} behaviors in {@link ImmutableMemStoreLAB} 2865 */ 2866 @Test 2867 public void testImmutableMemStoreLABRefCnt() throws Exception { 2868 Configuration conf = HBaseConfiguration.create(); 2869 2870 byte[] smallValue = new byte[3]; 2871 byte[] largeValue = new byte[9]; 2872 final long timestamp = EnvironmentEdgeManager.currentTime(); 2873 final long seqId = 100; 2874 final ExtendedCell smallCell1 = createCell(qf1, timestamp, seqId, smallValue); 2875 final ExtendedCell largeCell1 = createCell(qf2, timestamp, seqId, largeValue); 2876 final ExtendedCell smallCell2 = createCell(qf3, timestamp, seqId + 1, smallValue); 2877 final ExtendedCell largeCell2 = createCell(qf4, timestamp, seqId + 1, largeValue); 2878 final ExtendedCell smallCell3 = createCell(qf5, timestamp, seqId + 2, smallValue); 2879 final ExtendedCell largeCell3 = createCell(qf6, timestamp, seqId + 2, largeValue); 2880 2881 int smallCellByteSize = MutableSegment.getCellLength(smallCell1); 2882 int largeCellByteSize = MutableSegment.getCellLength(largeCell1); 2883 int firstWriteCellByteSize = (smallCellByteSize + largeCellByteSize); 2884 int flushByteSize = firstWriteCellByteSize - 2; 2885 2886 // set CompactingMemStore.inmemoryFlushSize to flushByteSize. 2887 conf.set(HStore.MEMSTORE_CLASS_NAME, CompactingMemStore.class.getName()); 2888 conf.setDouble(CompactingMemStore.IN_MEMORY_FLUSH_THRESHOLD_FACTOR_KEY, 0.005); 2889 conf.set(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, String.valueOf(flushByteSize * 200)); 2890 conf.setBoolean(WALFactory.WAL_ENABLED, false); 2891 2892 init(name, conf, ColumnFamilyDescriptorBuilder.newBuilder(family) 2893 .setInMemoryCompaction(MemoryCompactionPolicy.BASIC).build()); 2894 2895 final CompactingMemStore myCompactingMemStore = ((CompactingMemStore) store.memstore); 2896 assertTrue((int) (myCompactingMemStore.getInmemoryFlushSize()) == flushByteSize); 2897 myCompactingMemStore.allowCompaction.set(false); 2898 2899 NonThreadSafeMemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 2900 store.add(smallCell1, memStoreSizing); 2901 store.add(largeCell1, memStoreSizing); 2902 store.add(smallCell2, memStoreSizing); 2903 store.add(largeCell2, memStoreSizing); 2904 store.add(smallCell3, memStoreSizing); 2905 store.add(largeCell3, memStoreSizing); 2906 VersionedSegmentsList versionedSegmentsList = myCompactingMemStore.getImmutableSegments(); 2907 assertTrue(versionedSegmentsList.getNumOfSegments() == 3); 2908 List<ImmutableSegment> segments = versionedSegmentsList.getStoreSegments(); 2909 List<MemStoreLABImpl> memStoreLABs = new ArrayList<MemStoreLABImpl>(segments.size()); 2910 for (ImmutableSegment segment : segments) { 2911 memStoreLABs.add((MemStoreLABImpl) segment.getMemStoreLAB()); 2912 } 2913 List<KeyValueScanner> scanners1 = myCompactingMemStore.getScanners(Long.MAX_VALUE); 2914 for (MemStoreLABImpl memStoreLAB : memStoreLABs) { 2915 assertTrue(memStoreLAB.getRefCntValue() == 2); 2916 } 2917 2918 myCompactingMemStore.allowCompaction.set(true); 2919 myCompactingMemStore.flushInMemory(); 2920 2921 versionedSegmentsList = myCompactingMemStore.getImmutableSegments(); 2922 assertTrue(versionedSegmentsList.getNumOfSegments() == 1); 2923 ImmutableMemStoreLAB immutableMemStoreLAB = 2924 (ImmutableMemStoreLAB) (versionedSegmentsList.getStoreSegments().get(0).getMemStoreLAB()); 2925 for (MemStoreLABImpl memStoreLAB : memStoreLABs) { 2926 assertTrue(memStoreLAB.getRefCntValue() == 2); 2927 } 2928 2929 List<KeyValueScanner> scanners2 = myCompactingMemStore.getScanners(Long.MAX_VALUE); 2930 for (MemStoreLABImpl memStoreLAB : memStoreLABs) { 2931 assertTrue(memStoreLAB.getRefCntValue() == 2); 2932 } 2933 assertTrue(immutableMemStoreLAB.getRefCntValue() == 2); 2934 for (KeyValueScanner scanner : scanners1) { 2935 scanner.close(); 2936 } 2937 for (MemStoreLABImpl memStoreLAB : memStoreLABs) { 2938 assertTrue(memStoreLAB.getRefCntValue() == 1); 2939 } 2940 for (KeyValueScanner scanner : scanners2) { 2941 scanner.close(); 2942 } 2943 for (MemStoreLABImpl memStoreLAB : memStoreLABs) { 2944 assertTrue(memStoreLAB.getRefCntValue() == 1); 2945 } 2946 assertTrue(immutableMemStoreLAB.getRefCntValue() == 1); 2947 flushStore(store, id++); 2948 for (MemStoreLABImpl memStoreLAB : memStoreLABs) { 2949 assertTrue(memStoreLAB.getRefCntValue() == 0); 2950 } 2951 assertTrue(immutableMemStoreLAB.getRefCntValue() == 0); 2952 assertTrue(immutableMemStoreLAB.isClosed()); 2953 for (MemStoreLABImpl memStoreLAB : memStoreLABs) { 2954 assertTrue(memStoreLAB.isClosed()); 2955 assertTrue(memStoreLAB.isReclaimed()); 2956 assertTrue(memStoreLAB.chunks.isEmpty()); 2957 } 2958 } 2959 2960 private HStoreFile mockStoreFileWithLength(long length) { 2961 HStoreFile sf = mock(HStoreFile.class); 2962 StoreFileReader sfr = mock(StoreFileReader.class); 2963 when(sf.isHFile()).thenReturn(true); 2964 when(sf.getReader()).thenReturn(sfr); 2965 when(sfr.length()).thenReturn(length); 2966 return sf; 2967 } 2968 2969 private static class MyThread extends Thread { 2970 private StoreScanner scanner; 2971 private KeyValueHeap heap; 2972 2973 public MyThread(StoreScanner scanner) { 2974 this.scanner = scanner; 2975 } 2976 2977 public KeyValueHeap getHeap() { 2978 return this.heap; 2979 } 2980 2981 @Override 2982 public void run() { 2983 scanner.trySwitchToStreamRead(); 2984 heap = scanner.heap; 2985 } 2986 } 2987 2988 private static class MyMemStoreCompactor extends MemStoreCompactor { 2989 private static final AtomicInteger RUNNER_COUNT = new AtomicInteger(0); 2990 private static final CountDownLatch START_COMPACTOR_LATCH = new CountDownLatch(1); 2991 2992 public MyMemStoreCompactor(CompactingMemStore compactingMemStore, 2993 MemoryCompactionPolicy compactionPolicy) throws IllegalArgumentIOException { 2994 super(compactingMemStore, compactionPolicy); 2995 } 2996 2997 @Override 2998 public boolean start() throws IOException { 2999 boolean isFirst = RUNNER_COUNT.getAndIncrement() == 0; 3000 if (isFirst) { 3001 try { 3002 START_COMPACTOR_LATCH.await(); 3003 return super.start(); 3004 } catch (InterruptedException ex) { 3005 throw new RuntimeException(ex); 3006 } 3007 } 3008 return super.start(); 3009 } 3010 } 3011 3012 public static class MyCompactingMemStoreWithCustomCompactor extends CompactingMemStore { 3013 private static final AtomicInteger RUNNER_COUNT = new AtomicInteger(0); 3014 3015 public MyCompactingMemStoreWithCustomCompactor(Configuration conf, CellComparatorImpl c, 3016 HStore store, RegionServicesForStores regionServices, MemoryCompactionPolicy compactionPolicy) 3017 throws IOException { 3018 super(conf, c, store, regionServices, compactionPolicy); 3019 } 3020 3021 @Override 3022 protected MemStoreCompactor createMemStoreCompactor(MemoryCompactionPolicy compactionPolicy) 3023 throws IllegalArgumentIOException { 3024 return new MyMemStoreCompactor(this, compactionPolicy); 3025 } 3026 3027 @Override 3028 protected boolean setInMemoryCompactionFlag() { 3029 boolean rval = super.setInMemoryCompactionFlag(); 3030 if (rval) { 3031 RUNNER_COUNT.incrementAndGet(); 3032 if (LOG.isDebugEnabled()) { 3033 LOG.debug("runner count: " + RUNNER_COUNT.get()); 3034 } 3035 } 3036 return rval; 3037 } 3038 } 3039 3040 public static class MyCompactingMemStore extends CompactingMemStore { 3041 private static final AtomicBoolean START_TEST = new AtomicBoolean(false); 3042 private final CountDownLatch getScannerLatch = new CountDownLatch(1); 3043 private final CountDownLatch snapshotLatch = new CountDownLatch(1); 3044 3045 public MyCompactingMemStore(Configuration conf, CellComparatorImpl c, HStore store, 3046 RegionServicesForStores regionServices, MemoryCompactionPolicy compactionPolicy) 3047 throws IOException { 3048 super(conf, c, store, regionServices, compactionPolicy); 3049 } 3050 3051 @Override 3052 protected List<KeyValueScanner> createList(int capacity) { 3053 if (START_TEST.get()) { 3054 try { 3055 getScannerLatch.countDown(); 3056 snapshotLatch.await(); 3057 } catch (InterruptedException e) { 3058 throw new RuntimeException(e); 3059 } 3060 } 3061 return new ArrayList<>(capacity); 3062 } 3063 3064 @Override 3065 protected void pushActiveToPipeline(MutableSegment active, boolean checkEmpty) { 3066 if (START_TEST.get()) { 3067 try { 3068 getScannerLatch.await(); 3069 } catch (InterruptedException e) { 3070 throw new RuntimeException(e); 3071 } 3072 } 3073 3074 super.pushActiveToPipeline(active, checkEmpty); 3075 if (START_TEST.get()) { 3076 snapshotLatch.countDown(); 3077 } 3078 } 3079 } 3080 3081 interface MyListHook { 3082 void hook(int currentSize); 3083 } 3084 3085 private static class MyList<T> implements List<T> { 3086 private final List<T> delegatee = new ArrayList<>(); 3087 private final MyListHook hookAtAdd; 3088 3089 MyList(final MyListHook hookAtAdd) { 3090 this.hookAtAdd = hookAtAdd; 3091 } 3092 3093 @Override 3094 public int size() { 3095 return delegatee.size(); 3096 } 3097 3098 @Override 3099 public boolean isEmpty() { 3100 return delegatee.isEmpty(); 3101 } 3102 3103 @Override 3104 public boolean contains(Object o) { 3105 return delegatee.contains(o); 3106 } 3107 3108 @Override 3109 public Iterator<T> iterator() { 3110 return delegatee.iterator(); 3111 } 3112 3113 @Override 3114 public Object[] toArray() { 3115 return delegatee.toArray(); 3116 } 3117 3118 @Override 3119 public <R> R[] toArray(R[] a) { 3120 return delegatee.toArray(a); 3121 } 3122 3123 @Override 3124 public boolean add(T e) { 3125 hookAtAdd.hook(size()); 3126 return delegatee.add(e); 3127 } 3128 3129 @Override 3130 public boolean remove(Object o) { 3131 return delegatee.remove(o); 3132 } 3133 3134 @Override 3135 public boolean containsAll(Collection<?> c) { 3136 return delegatee.containsAll(c); 3137 } 3138 3139 @Override 3140 public boolean addAll(Collection<? extends T> c) { 3141 return delegatee.addAll(c); 3142 } 3143 3144 @Override 3145 public boolean addAll(int index, Collection<? extends T> c) { 3146 return delegatee.addAll(index, c); 3147 } 3148 3149 @Override 3150 public boolean removeAll(Collection<?> c) { 3151 return delegatee.removeAll(c); 3152 } 3153 3154 @Override 3155 public boolean retainAll(Collection<?> c) { 3156 return delegatee.retainAll(c); 3157 } 3158 3159 @Override 3160 public void clear() { 3161 delegatee.clear(); 3162 } 3163 3164 @Override 3165 public T get(int index) { 3166 return delegatee.get(index); 3167 } 3168 3169 @Override 3170 public T set(int index, T element) { 3171 return delegatee.set(index, element); 3172 } 3173 3174 @Override 3175 public void add(int index, T element) { 3176 delegatee.add(index, element); 3177 } 3178 3179 @Override 3180 public T remove(int index) { 3181 return delegatee.remove(index); 3182 } 3183 3184 @Override 3185 public int indexOf(Object o) { 3186 return delegatee.indexOf(o); 3187 } 3188 3189 @Override 3190 public int lastIndexOf(Object o) { 3191 return delegatee.lastIndexOf(o); 3192 } 3193 3194 @Override 3195 public ListIterator<T> listIterator() { 3196 return delegatee.listIterator(); 3197 } 3198 3199 @Override 3200 public ListIterator<T> listIterator(int index) { 3201 return delegatee.listIterator(index); 3202 } 3203 3204 @Override 3205 public List<T> subList(int fromIndex, int toIndex) { 3206 return delegatee.subList(fromIndex, toIndex); 3207 } 3208 } 3209 3210 private interface MyKeyValueHeapHook { 3211 void onRecordBlockSize(int recordBlockSizeCallCount); 3212 } 3213 3214 private static class MyKeyValueHeap extends KeyValueHeap { 3215 private final MyKeyValueHeapHook hook; 3216 private int recordBlockSizeCallCount; 3217 3218 public MyKeyValueHeap(List<? extends KeyValueScanner> scanners, CellComparator comparator, 3219 MyKeyValueHeapHook hook) throws IOException { 3220 super(scanners, comparator); 3221 this.hook = hook; 3222 } 3223 3224 @Override 3225 public void recordBlockSize(IntConsumer blockSizeConsumer) { 3226 recordBlockSizeCallCount++; 3227 hook.onRecordBlockSize(recordBlockSizeCallCount); 3228 super.recordBlockSize(blockSizeConsumer); 3229 } 3230 } 3231 3232 public static class MyCompactingMemStore2 extends CompactingMemStore { 3233 private static final String LARGE_CELL_THREAD_NAME = "largeCellThread"; 3234 private static final String SMALL_CELL_THREAD_NAME = "smallCellThread"; 3235 private final CyclicBarrier preCyclicBarrier = new CyclicBarrier(2); 3236 private final CyclicBarrier postCyclicBarrier = new CyclicBarrier(2); 3237 private final AtomicInteger largeCellPreUpdateCounter = new AtomicInteger(0); 3238 private final AtomicInteger smallCellPreUpdateCounter = new AtomicInteger(0); 3239 3240 public MyCompactingMemStore2(Configuration conf, CellComparatorImpl cellComparator, 3241 HStore store, RegionServicesForStores regionServices, MemoryCompactionPolicy compactionPolicy) 3242 throws IOException { 3243 super(conf, cellComparator, store, regionServices, compactionPolicy); 3244 } 3245 3246 @Override 3247 protected boolean checkAndAddToActiveSize(MutableSegment currActive, Cell cellToAdd, 3248 MemStoreSizing memstoreSizing) { 3249 if (Thread.currentThread().getName().equals(LARGE_CELL_THREAD_NAME)) { 3250 int currentCount = largeCellPreUpdateCounter.incrementAndGet(); 3251 if (currentCount <= 1) { 3252 try { 3253 /** 3254 * smallCellThread enters CompactingMemStore.checkAndAddToActiveSize first, then 3255 * largeCellThread enters CompactingMemStore.checkAndAddToActiveSize, and then 3256 * largeCellThread invokes flushInMemory. 3257 */ 3258 preCyclicBarrier.await(); 3259 } catch (Throwable e) { 3260 throw new RuntimeException(e); 3261 } 3262 } 3263 } 3264 3265 boolean returnValue = super.checkAndAddToActiveSize(currActive, cellToAdd, memstoreSizing); 3266 if (Thread.currentThread().getName().equals(SMALL_CELL_THREAD_NAME)) { 3267 try { 3268 preCyclicBarrier.await(); 3269 } catch (Throwable e) { 3270 throw new RuntimeException(e); 3271 } 3272 } 3273 return returnValue; 3274 } 3275 3276 @Override 3277 protected void doAdd(MutableSegment currentActive, ExtendedCell cell, 3278 MemStoreSizing memstoreSizing) { 3279 if (Thread.currentThread().getName().equals(SMALL_CELL_THREAD_NAME)) { 3280 try { 3281 /** 3282 * After largeCellThread finished flushInMemory method, smallCellThread can add cell to 3283 * currentActive . That is to say when largeCellThread called flushInMemory method, 3284 * currentActive has no cell. 3285 */ 3286 postCyclicBarrier.await(); 3287 } catch (Throwable e) { 3288 throw new RuntimeException(e); 3289 } 3290 } 3291 super.doAdd(currentActive, cell, memstoreSizing); 3292 } 3293 3294 @Override 3295 protected void flushInMemory(MutableSegment currentActiveMutableSegment) { 3296 super.flushInMemory(currentActiveMutableSegment); 3297 if (Thread.currentThread().getName().equals(LARGE_CELL_THREAD_NAME)) { 3298 if (largeCellPreUpdateCounter.get() <= 1) { 3299 try { 3300 postCyclicBarrier.await(); 3301 } catch (Throwable e) { 3302 throw new RuntimeException(e); 3303 } 3304 } 3305 } 3306 } 3307 3308 } 3309 3310 public static class MyCompactingMemStore3 extends CompactingMemStore { 3311 private static final String LARGE_CELL_THREAD_NAME = "largeCellThread"; 3312 private static final String SMALL_CELL_THREAD_NAME = "smallCellThread"; 3313 3314 private final CyclicBarrier preCyclicBarrier = new CyclicBarrier(2); 3315 private final CyclicBarrier postCyclicBarrier = new CyclicBarrier(2); 3316 private final AtomicInteger flushCounter = new AtomicInteger(0); 3317 private static final int CELL_COUNT = 5; 3318 private boolean flushByteSizeLessThanSmallAndLargeCellSize = true; 3319 3320 public MyCompactingMemStore3(Configuration conf, CellComparatorImpl cellComparator, 3321 HStore store, RegionServicesForStores regionServices, MemoryCompactionPolicy compactionPolicy) 3322 throws IOException { 3323 super(conf, cellComparator, store, regionServices, compactionPolicy); 3324 } 3325 3326 @Override 3327 protected boolean checkAndAddToActiveSize(MutableSegment currActive, Cell cellToAdd, 3328 MemStoreSizing memstoreSizing) { 3329 if (!flushByteSizeLessThanSmallAndLargeCellSize) { 3330 return super.checkAndAddToActiveSize(currActive, cellToAdd, memstoreSizing); 3331 } 3332 if (Thread.currentThread().getName().equals(LARGE_CELL_THREAD_NAME)) { 3333 try { 3334 preCyclicBarrier.await(); 3335 } catch (Throwable e) { 3336 throw new RuntimeException(e); 3337 } 3338 } 3339 3340 boolean returnValue = super.checkAndAddToActiveSize(currActive, cellToAdd, memstoreSizing); 3341 if (Thread.currentThread().getName().equals(SMALL_CELL_THREAD_NAME)) { 3342 try { 3343 preCyclicBarrier.await(); 3344 } catch (Throwable e) { 3345 throw new RuntimeException(e); 3346 } 3347 } 3348 return returnValue; 3349 } 3350 3351 @Override 3352 protected void postUpdate(MutableSegment currentActiveMutableSegment) { 3353 super.postUpdate(currentActiveMutableSegment); 3354 if (!flushByteSizeLessThanSmallAndLargeCellSize) { 3355 try { 3356 postCyclicBarrier.await(); 3357 } catch (Throwable e) { 3358 throw new RuntimeException(e); 3359 } 3360 return; 3361 } 3362 3363 if (Thread.currentThread().getName().equals(SMALL_CELL_THREAD_NAME)) { 3364 try { 3365 postCyclicBarrier.await(); 3366 } catch (Throwable e) { 3367 throw new RuntimeException(e); 3368 } 3369 } 3370 } 3371 3372 @Override 3373 protected void flushInMemory(MutableSegment currentActiveMutableSegment) { 3374 super.flushInMemory(currentActiveMutableSegment); 3375 flushCounter.incrementAndGet(); 3376 if (!flushByteSizeLessThanSmallAndLargeCellSize) { 3377 return; 3378 } 3379 3380 assertTrue(Thread.currentThread().getName().equals(LARGE_CELL_THREAD_NAME)); 3381 try { 3382 postCyclicBarrier.await(); 3383 } catch (Throwable e) { 3384 throw new RuntimeException(e); 3385 } 3386 3387 } 3388 3389 void disableCompaction() { 3390 allowCompaction.set(false); 3391 } 3392 3393 void enableCompaction() { 3394 allowCompaction.set(true); 3395 } 3396 3397 } 3398 3399 public static class MyCompactingMemStore4 extends CompactingMemStore { 3400 private static final String TAKE_SNAPSHOT_THREAD_NAME = "takeSnapShotThread"; 3401 /** 3402 * {@link CompactingMemStore#flattenOneSegment} must execute after 3403 * {@link CompactingMemStore#getImmutableSegments} 3404 */ 3405 private final CyclicBarrier flattenOneSegmentPreCyclicBarrier = new CyclicBarrier(2); 3406 /** 3407 * Only after {@link CompactingMemStore#flattenOneSegment} completed, 3408 * {@link CompactingMemStore#swapPipelineWithNull} could execute. 3409 */ 3410 private final CyclicBarrier flattenOneSegmentPostCyclicBarrier = new CyclicBarrier(2); 3411 /** 3412 * Only the in memory compact thread enters {@link CompactingMemStore#flattenOneSegment},the 3413 * snapshot thread starts {@link CompactingMemStore#snapshot},because 3414 * {@link CompactingMemStore#snapshot} would invoke {@link CompactingMemStore#stopCompaction}. 3415 */ 3416 private final CyclicBarrier snapShotStartCyclicCyclicBarrier = new CyclicBarrier(2); 3417 /** 3418 * To wait for {@link CompactingMemStore.InMemoryCompactionRunnable} stopping. 3419 */ 3420 private final CyclicBarrier inMemoryCompactionEndCyclicBarrier = new CyclicBarrier(2); 3421 private final AtomicInteger getImmutableSegmentsListCounter = new AtomicInteger(0); 3422 private final AtomicInteger swapPipelineWithNullCounter = new AtomicInteger(0); 3423 private final AtomicInteger flattenOneSegmentCounter = new AtomicInteger(0); 3424 private final AtomicInteger setInMemoryCompactionFlagCounter = new AtomicInteger(0); 3425 3426 public MyCompactingMemStore4(Configuration conf, CellComparatorImpl cellComparator, 3427 HStore store, RegionServicesForStores regionServices, MemoryCompactionPolicy compactionPolicy) 3428 throws IOException { 3429 super(conf, cellComparator, store, regionServices, compactionPolicy); 3430 } 3431 3432 @Override 3433 public VersionedSegmentsList getImmutableSegments() { 3434 VersionedSegmentsList result = super.getImmutableSegments(); 3435 if (Thread.currentThread().getName().equals(TAKE_SNAPSHOT_THREAD_NAME)) { 3436 int currentCount = getImmutableSegmentsListCounter.incrementAndGet(); 3437 if (currentCount <= 1) { 3438 try { 3439 flattenOneSegmentPreCyclicBarrier.await(); 3440 } catch (Throwable e) { 3441 throw new RuntimeException(e); 3442 } 3443 } 3444 } 3445 return result; 3446 } 3447 3448 @Override 3449 protected boolean swapPipelineWithNull(VersionedSegmentsList segments) { 3450 if (Thread.currentThread().getName().equals(TAKE_SNAPSHOT_THREAD_NAME)) { 3451 int currentCount = swapPipelineWithNullCounter.incrementAndGet(); 3452 if (currentCount <= 1) { 3453 try { 3454 flattenOneSegmentPostCyclicBarrier.await(); 3455 } catch (Throwable e) { 3456 throw new RuntimeException(e); 3457 } 3458 } 3459 } 3460 boolean result = super.swapPipelineWithNull(segments); 3461 if (Thread.currentThread().getName().equals(TAKE_SNAPSHOT_THREAD_NAME)) { 3462 int currentCount = swapPipelineWithNullCounter.get(); 3463 if (currentCount <= 1) { 3464 assertTrue(!result); 3465 } 3466 if (currentCount == 2) { 3467 assertTrue(result); 3468 } 3469 } 3470 return result; 3471 3472 } 3473 3474 @Override 3475 public void flattenOneSegment(long requesterVersion, Action action) { 3476 int currentCount = flattenOneSegmentCounter.incrementAndGet(); 3477 if (currentCount <= 1) { 3478 try { 3479 /** 3480 * {@link CompactingMemStore#snapshot} could start. 3481 */ 3482 snapShotStartCyclicCyclicBarrier.await(); 3483 flattenOneSegmentPreCyclicBarrier.await(); 3484 } catch (Throwable e) { 3485 throw new RuntimeException(e); 3486 } 3487 } 3488 super.flattenOneSegment(requesterVersion, action); 3489 if (currentCount <= 1) { 3490 try { 3491 flattenOneSegmentPostCyclicBarrier.await(); 3492 } catch (Throwable e) { 3493 throw new RuntimeException(e); 3494 } 3495 } 3496 } 3497 3498 @Override 3499 protected boolean setInMemoryCompactionFlag() { 3500 boolean result = super.setInMemoryCompactionFlag(); 3501 assertTrue(result); 3502 setInMemoryCompactionFlagCounter.incrementAndGet(); 3503 return result; 3504 } 3505 3506 @Override 3507 void inMemoryCompaction() { 3508 try { 3509 super.inMemoryCompaction(); 3510 } finally { 3511 try { 3512 inMemoryCompactionEndCyclicBarrier.await(); 3513 } catch (Throwable e) { 3514 throw new RuntimeException(e); 3515 } 3516 3517 } 3518 } 3519 3520 } 3521 3522 public static class MyCompactingMemStore5 extends CompactingMemStore { 3523 private static final String TAKE_SNAPSHOT_THREAD_NAME = "takeSnapShotThread"; 3524 private static final String WRITE_AGAIN_THREAD_NAME = "writeAgainThread"; 3525 /** 3526 * {@link CompactingMemStore#flattenOneSegment} must execute after 3527 * {@link CompactingMemStore#getImmutableSegments} 3528 */ 3529 private final CyclicBarrier flattenOneSegmentPreCyclicBarrier = new CyclicBarrier(2); 3530 /** 3531 * Only after {@link CompactingMemStore#flattenOneSegment} completed, 3532 * {@link CompactingMemStore#swapPipelineWithNull} could execute. 3533 */ 3534 private final CyclicBarrier flattenOneSegmentPostCyclicBarrier = new CyclicBarrier(2); 3535 /** 3536 * Only the in memory compact thread enters {@link CompactingMemStore#flattenOneSegment},the 3537 * snapshot thread starts {@link CompactingMemStore#snapshot},because 3538 * {@link CompactingMemStore#snapshot} would invoke {@link CompactingMemStore#stopCompaction}. 3539 */ 3540 private final CyclicBarrier snapShotStartCyclicCyclicBarrier = new CyclicBarrier(2); 3541 /** 3542 * To wait for {@link CompactingMemStore.InMemoryCompactionRunnable} stopping. 3543 */ 3544 private final CyclicBarrier inMemoryCompactionEndCyclicBarrier = new CyclicBarrier(2); 3545 private final AtomicInteger getImmutableSegmentsListCounter = new AtomicInteger(0); 3546 private final AtomicInteger swapPipelineWithNullCounter = new AtomicInteger(0); 3547 private final AtomicInteger flattenOneSegmentCounter = new AtomicInteger(0); 3548 private final AtomicInteger setInMemoryCompactionFlagCounter = new AtomicInteger(0); 3549 /** 3550 * Only the snapshot thread retry {@link CompactingMemStore#swapPipelineWithNull}, writeAgain 3551 * thread could start. 3552 */ 3553 private final CyclicBarrier writeMemStoreAgainStartCyclicBarrier = new CyclicBarrier(2); 3554 /** 3555 * This is used for snapshot thread,writeAgain thread and in memory compact thread. Only the 3556 * writeAgain thread completes, {@link CompactingMemStore#swapPipelineWithNull} would 3557 * execute,and in memory compact thread would exit,because we expect that in memory compact 3558 * executing only once. 3559 */ 3560 private final CyclicBarrier writeMemStoreAgainEndCyclicBarrier = new CyclicBarrier(3); 3561 3562 public MyCompactingMemStore5(Configuration conf, CellComparatorImpl cellComparator, 3563 HStore store, RegionServicesForStores regionServices, MemoryCompactionPolicy compactionPolicy) 3564 throws IOException { 3565 super(conf, cellComparator, store, regionServices, compactionPolicy); 3566 } 3567 3568 @Override 3569 public VersionedSegmentsList getImmutableSegments() { 3570 VersionedSegmentsList result = super.getImmutableSegments(); 3571 if (Thread.currentThread().getName().equals(TAKE_SNAPSHOT_THREAD_NAME)) { 3572 int currentCount = getImmutableSegmentsListCounter.incrementAndGet(); 3573 if (currentCount <= 1) { 3574 try { 3575 flattenOneSegmentPreCyclicBarrier.await(); 3576 } catch (Throwable e) { 3577 throw new RuntimeException(e); 3578 } 3579 } 3580 3581 } 3582 3583 return result; 3584 } 3585 3586 @Override 3587 protected boolean swapPipelineWithNull(VersionedSegmentsList segments) { 3588 if (Thread.currentThread().getName().equals(TAKE_SNAPSHOT_THREAD_NAME)) { 3589 int currentCount = swapPipelineWithNullCounter.incrementAndGet(); 3590 if (currentCount <= 1) { 3591 try { 3592 flattenOneSegmentPostCyclicBarrier.await(); 3593 } catch (Throwable e) { 3594 throw new RuntimeException(e); 3595 } 3596 } 3597 3598 if (currentCount == 2) { 3599 try { 3600 /** 3601 * Only the snapshot thread retry {@link CompactingMemStore#swapPipelineWithNull}, 3602 * writeAgain thread could start. 3603 */ 3604 writeMemStoreAgainStartCyclicBarrier.await(); 3605 /** 3606 * Only the writeAgain thread completes, retry 3607 * {@link CompactingMemStore#swapPipelineWithNull} would execute. 3608 */ 3609 writeMemStoreAgainEndCyclicBarrier.await(); 3610 } catch (Throwable e) { 3611 throw new RuntimeException(e); 3612 } 3613 } 3614 3615 } 3616 boolean result = super.swapPipelineWithNull(segments); 3617 if (Thread.currentThread().getName().equals(TAKE_SNAPSHOT_THREAD_NAME)) { 3618 int currentCount = swapPipelineWithNullCounter.get(); 3619 if (currentCount <= 1) { 3620 assertTrue(!result); 3621 } 3622 if (currentCount == 2) { 3623 assertTrue(result); 3624 } 3625 } 3626 return result; 3627 3628 } 3629 3630 @Override 3631 public void flattenOneSegment(long requesterVersion, Action action) { 3632 int currentCount = flattenOneSegmentCounter.incrementAndGet(); 3633 if (currentCount <= 1) { 3634 try { 3635 /** 3636 * {@link CompactingMemStore#snapshot} could start. 3637 */ 3638 snapShotStartCyclicCyclicBarrier.await(); 3639 flattenOneSegmentPreCyclicBarrier.await(); 3640 } catch (Throwable e) { 3641 throw new RuntimeException(e); 3642 } 3643 } 3644 super.flattenOneSegment(requesterVersion, action); 3645 if (currentCount <= 1) { 3646 try { 3647 flattenOneSegmentPostCyclicBarrier.await(); 3648 /** 3649 * Only the writeAgain thread completes, in memory compact thread would exit,because we 3650 * expect that in memory compact executing only once. 3651 */ 3652 writeMemStoreAgainEndCyclicBarrier.await(); 3653 } catch (Throwable e) { 3654 throw new RuntimeException(e); 3655 } 3656 3657 } 3658 } 3659 3660 @Override 3661 protected boolean setInMemoryCompactionFlag() { 3662 boolean result = super.setInMemoryCompactionFlag(); 3663 int count = setInMemoryCompactionFlagCounter.incrementAndGet(); 3664 if (count <= 1) { 3665 assertTrue(result); 3666 } 3667 if (count == 2) { 3668 assertTrue(!result); 3669 } 3670 return result; 3671 } 3672 3673 @Override 3674 void inMemoryCompaction() { 3675 try { 3676 super.inMemoryCompaction(); 3677 } finally { 3678 try { 3679 inMemoryCompactionEndCyclicBarrier.await(); 3680 } catch (Throwable e) { 3681 throw new RuntimeException(e); 3682 } 3683 3684 } 3685 } 3686 } 3687 3688 public static class MyCompactingMemStore6 extends CompactingMemStore { 3689 private final CyclicBarrier inMemoryCompactionEndCyclicBarrier = new CyclicBarrier(2); 3690 3691 public MyCompactingMemStore6(Configuration conf, CellComparatorImpl cellComparator, 3692 HStore store, RegionServicesForStores regionServices, MemoryCompactionPolicy compactionPolicy) 3693 throws IOException { 3694 super(conf, cellComparator, store, regionServices, compactionPolicy); 3695 } 3696 3697 @Override 3698 void inMemoryCompaction() { 3699 try { 3700 super.inMemoryCompaction(); 3701 } finally { 3702 try { 3703 inMemoryCompactionEndCyclicBarrier.await(); 3704 } catch (Throwable e) { 3705 throw new RuntimeException(e); 3706 } 3707 3708 } 3709 } 3710 } 3711 3712 public static class MyDefaultMemStore extends DefaultMemStore { 3713 private static final String GET_SCANNER_THREAD_NAME = "getScannerMyThread"; 3714 private static final String FLUSH_THREAD_NAME = "flushMyThread"; 3715 /** 3716 * Only when flush thread enters {@link DefaultMemStore#doClearSnapShot}, getScanner thread 3717 * could start. 3718 */ 3719 private final CyclicBarrier getScannerCyclicBarrier = new CyclicBarrier(2); 3720 /** 3721 * Used by getScanner thread notifies flush thread {@link DefaultMemStore#getSnapshotSegments} 3722 * completed, {@link DefaultMemStore#doClearSnapShot} could continue. 3723 */ 3724 private final CyclicBarrier preClearSnapShotCyclicBarrier = new CyclicBarrier(2); 3725 /** 3726 * Used by flush thread notifies getScanner thread {@link DefaultMemStore#doClearSnapShot} 3727 * completed, {@link DefaultMemStore#getScanners} could continue. 3728 */ 3729 private final CyclicBarrier postClearSnapShotCyclicBarrier = new CyclicBarrier(2); 3730 private final AtomicInteger getSnapshotSegmentsCounter = new AtomicInteger(0); 3731 private final AtomicInteger clearSnapshotCounter = new AtomicInteger(0); 3732 private volatile boolean shouldWait = true; 3733 private volatile HStore store = null; 3734 3735 public MyDefaultMemStore(Configuration conf, CellComparator cellComparator, 3736 RegionServicesForStores regionServices) throws IOException { 3737 super(conf, cellComparator, regionServices); 3738 } 3739 3740 @Override 3741 protected List<Segment> getSnapshotSegments() { 3742 3743 List<Segment> result = super.getSnapshotSegments(); 3744 3745 if (Thread.currentThread().getName().equals(GET_SCANNER_THREAD_NAME)) { 3746 int currentCount = getSnapshotSegmentsCounter.incrementAndGet(); 3747 if (currentCount == 1) { 3748 if (this.shouldWait) { 3749 try { 3750 /** 3751 * Notify flush thread {@link DefaultMemStore#getSnapshotSegments} completed, 3752 * {@link DefaultMemStore#doClearSnapShot} could continue. 3753 */ 3754 preClearSnapShotCyclicBarrier.await(); 3755 /** 3756 * Wait for {@link DefaultMemStore#doClearSnapShot} completed. 3757 */ 3758 postClearSnapShotCyclicBarrier.await(); 3759 3760 } catch (Throwable e) { 3761 throw new RuntimeException(e); 3762 } 3763 } 3764 } 3765 } 3766 return result; 3767 } 3768 3769 @Override 3770 protected void doClearSnapShot() { 3771 if (Thread.currentThread().getName().equals(FLUSH_THREAD_NAME)) { 3772 int currentCount = clearSnapshotCounter.incrementAndGet(); 3773 if (currentCount == 1) { 3774 try { 3775 if ( 3776 ((ReentrantReadWriteLock) store.getStoreEngine().getLock()) 3777 .isWriteLockedByCurrentThread() 3778 ) { 3779 shouldWait = false; 3780 } 3781 /** 3782 * Only when flush thread enters {@link DefaultMemStore#doClearSnapShot}, getScanner 3783 * thread could start. 3784 */ 3785 getScannerCyclicBarrier.await(); 3786 3787 if (shouldWait) { 3788 /** 3789 * Wait for {@link DefaultMemStore#getSnapshotSegments} completed. 3790 */ 3791 preClearSnapShotCyclicBarrier.await(); 3792 } 3793 } catch (Throwable e) { 3794 throw new RuntimeException(e); 3795 } 3796 } 3797 } 3798 super.doClearSnapShot(); 3799 3800 if (Thread.currentThread().getName().equals(FLUSH_THREAD_NAME)) { 3801 int currentCount = clearSnapshotCounter.get(); 3802 if (currentCount == 1) { 3803 if (shouldWait) { 3804 try { 3805 /** 3806 * Notify getScanner thread {@link DefaultMemStore#doClearSnapShot} completed, 3807 * {@link DefaultMemStore#getScanners} could continue. 3808 */ 3809 postClearSnapShotCyclicBarrier.await(); 3810 } catch (Throwable e) { 3811 throw new RuntimeException(e); 3812 } 3813 } 3814 } 3815 } 3816 } 3817 } 3818}