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.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023import static org.junit.Assert.fail; 024import static org.mockito.ArgumentMatchers.any; 025import static org.mockito.Mockito.mock; 026import static org.mockito.Mockito.spy; 027import static org.mockito.Mockito.times; 028import static org.mockito.Mockito.verify; 029import static org.mockito.Mockito.when; 030 031import java.io.IOException; 032import java.lang.ref.SoftReference; 033import java.security.PrivilegedExceptionAction; 034import java.util.ArrayList; 035import java.util.Arrays; 036import java.util.Collection; 037import java.util.Collections; 038import java.util.Iterator; 039import java.util.List; 040import java.util.ListIterator; 041import java.util.NavigableSet; 042import java.util.TreeSet; 043import java.util.concurrent.ConcurrentSkipListSet; 044import java.util.concurrent.CountDownLatch; 045import java.util.concurrent.ExecutorService; 046import java.util.concurrent.Executors; 047import java.util.concurrent.TimeUnit; 048import java.util.concurrent.atomic.AtomicBoolean; 049import java.util.concurrent.atomic.AtomicInteger; 050import org.apache.hadoop.conf.Configuration; 051import org.apache.hadoop.fs.FSDataOutputStream; 052import org.apache.hadoop.fs.FileStatus; 053import org.apache.hadoop.fs.FileSystem; 054import org.apache.hadoop.fs.FilterFileSystem; 055import org.apache.hadoop.fs.LocalFileSystem; 056import org.apache.hadoop.fs.Path; 057import org.apache.hadoop.fs.permission.FsPermission; 058import org.apache.hadoop.hbase.Cell; 059import org.apache.hadoop.hbase.CellBuilderFactory; 060import org.apache.hadoop.hbase.CellBuilderType; 061import org.apache.hadoop.hbase.CellComparator; 062import org.apache.hadoop.hbase.CellComparatorImpl; 063import org.apache.hadoop.hbase.CellUtil; 064import org.apache.hadoop.hbase.HBaseClassTestRule; 065import org.apache.hadoop.hbase.HBaseConfiguration; 066import org.apache.hadoop.hbase.HBaseTestingUtility; 067import org.apache.hadoop.hbase.HConstants; 068import org.apache.hadoop.hbase.KeyValue; 069import org.apache.hadoop.hbase.MemoryCompactionPolicy; 070import org.apache.hadoop.hbase.PrivateCellUtil; 071import org.apache.hadoop.hbase.TableName; 072import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 073import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 074import org.apache.hadoop.hbase.client.Get; 075import org.apache.hadoop.hbase.client.RegionInfo; 076import org.apache.hadoop.hbase.client.RegionInfoBuilder; 077import org.apache.hadoop.hbase.client.Scan; 078import org.apache.hadoop.hbase.client.TableDescriptor; 079import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 080import org.apache.hadoop.hbase.exceptions.IllegalArgumentIOException; 081import org.apache.hadoop.hbase.filter.Filter; 082import org.apache.hadoop.hbase.filter.FilterBase; 083import org.apache.hadoop.hbase.io.compress.Compression; 084import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding; 085import org.apache.hadoop.hbase.io.hfile.CacheConfig; 086import org.apache.hadoop.hbase.io.hfile.HFile; 087import org.apache.hadoop.hbase.io.hfile.HFileContext; 088import org.apache.hadoop.hbase.io.hfile.HFileContextBuilder; 089import org.apache.hadoop.hbase.monitoring.MonitoredTask; 090import org.apache.hadoop.hbase.regionserver.compactions.CompactionConfiguration; 091import org.apache.hadoop.hbase.regionserver.compactions.DefaultCompactor; 092import org.apache.hadoop.hbase.regionserver.querymatcher.ScanQueryMatcher; 093import org.apache.hadoop.hbase.regionserver.throttle.NoLimitThroughputController; 094import org.apache.hadoop.hbase.security.User; 095import org.apache.hadoop.hbase.testclassification.MediumTests; 096import org.apache.hadoop.hbase.testclassification.RegionServerTests; 097import org.apache.hadoop.hbase.util.Bytes; 098import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 099import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper; 100import org.apache.hadoop.hbase.util.FSUtils; 101import org.apache.hadoop.hbase.util.IncrementingEnvironmentEdge; 102import org.apache.hadoop.hbase.util.ManualEnvironmentEdge; 103import org.apache.hadoop.hbase.wal.AbstractFSWALProvider; 104import org.apache.hadoop.hbase.wal.WALFactory; 105import org.apache.hadoop.util.Progressable; 106import org.junit.After; 107import org.junit.AfterClass; 108import org.junit.Before; 109import org.junit.ClassRule; 110import org.junit.Rule; 111import org.junit.Test; 112import org.junit.experimental.categories.Category; 113import org.junit.rules.TestName; 114import org.mockito.Mockito; 115import org.slf4j.Logger; 116import org.slf4j.LoggerFactory; 117 118import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 119 120/** 121 * Test class for the HStore 122 */ 123@Category({ RegionServerTests.class, MediumTests.class }) 124public class TestHStore { 125 126 @ClassRule 127 public static final HBaseClassTestRule CLASS_RULE = 128 HBaseClassTestRule.forClass(TestHStore.class); 129 130 private static final Logger LOG = LoggerFactory.getLogger(TestHStore.class); 131 @Rule 132 public TestName name = new TestName(); 133 134 HRegion region; 135 HStore store; 136 byte [] table = Bytes.toBytes("table"); 137 byte [] family = Bytes.toBytes("family"); 138 139 byte [] row = Bytes.toBytes("row"); 140 byte [] row2 = Bytes.toBytes("row2"); 141 byte [] qf1 = Bytes.toBytes("qf1"); 142 byte [] qf2 = Bytes.toBytes("qf2"); 143 byte [] qf3 = Bytes.toBytes("qf3"); 144 byte [] qf4 = Bytes.toBytes("qf4"); 145 byte [] qf5 = Bytes.toBytes("qf5"); 146 byte [] qf6 = Bytes.toBytes("qf6"); 147 148 NavigableSet<byte[]> qualifiers = new ConcurrentSkipListSet<>(Bytes.BYTES_COMPARATOR); 149 150 List<Cell> expected = new ArrayList<>(); 151 List<Cell> result = new ArrayList<>(); 152 153 long id = System.currentTimeMillis(); 154 Get get = new Get(row); 155 156 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 157 private static final String DIR = TEST_UTIL.getDataTestDir("TestStore").toString(); 158 159 160 /** 161 * Setup 162 * @throws IOException 163 */ 164 @Before 165 public void setUp() throws IOException { 166 qualifiers.add(qf1); 167 qualifiers.add(qf3); 168 qualifiers.add(qf5); 169 170 Iterator<byte[]> iter = qualifiers.iterator(); 171 while(iter.hasNext()){ 172 byte [] next = iter.next(); 173 expected.add(new KeyValue(row, family, next, 1, (byte[])null)); 174 get.addColumn(family, next); 175 } 176 } 177 178 private void init(String methodName) throws IOException { 179 init(methodName, TEST_UTIL.getConfiguration()); 180 } 181 182 private HStore init(String methodName, Configuration conf) throws IOException { 183 // some of the tests write 4 versions and then flush 184 // (with HBASE-4241, lower versions are collected on flush) 185 return init(methodName, conf, 186 ColumnFamilyDescriptorBuilder.newBuilder(family).setMaxVersions(4).build()); 187 } 188 189 private HStore init(String methodName, Configuration conf, ColumnFamilyDescriptor hcd) 190 throws IOException { 191 return init(methodName, conf, TableDescriptorBuilder.newBuilder(TableName.valueOf(table)), hcd); 192 } 193 194 private HStore init(String methodName, Configuration conf, TableDescriptorBuilder builder, 195 ColumnFamilyDescriptor hcd) throws IOException { 196 return init(methodName, conf, builder, hcd, null); 197 } 198 199 private HStore init(String methodName, Configuration conf, TableDescriptorBuilder builder, 200 ColumnFamilyDescriptor hcd, MyStoreHook hook) throws IOException { 201 return init(methodName, conf, builder, hcd, hook, false); 202 } 203 204 private void initHRegion(String methodName, Configuration conf, TableDescriptorBuilder builder, 205 ColumnFamilyDescriptor hcd, MyStoreHook hook, boolean switchToPread) throws IOException { 206 TableDescriptor htd = builder.setColumnFamily(hcd).build(); 207 Path basedir = new Path(DIR + methodName); 208 Path tableDir = FSUtils.getTableDir(basedir, htd.getTableName()); 209 final Path logdir = new Path(basedir, AbstractFSWALProvider.getWALDirectoryName(methodName)); 210 211 FileSystem fs = FileSystem.get(conf); 212 213 fs.delete(logdir, true); 214 ChunkCreator.initialize(MemStoreLABImpl.CHUNK_SIZE_DEFAULT, false, 215 MemStoreLABImpl.CHUNK_SIZE_DEFAULT, 1, 0, null); 216 RegionInfo info = RegionInfoBuilder.newBuilder(htd.getTableName()).build(); 217 Configuration walConf = new Configuration(conf); 218 FSUtils.setRootDir(walConf, basedir); 219 WALFactory wals = new WALFactory(walConf, methodName); 220 region = new HRegion(new HRegionFileSystem(conf, fs, tableDir, info), wals.getWAL(info), conf, 221 htd, null); 222 } 223 224 private HStore init(String methodName, Configuration conf, TableDescriptorBuilder builder, 225 ColumnFamilyDescriptor hcd, MyStoreHook hook, boolean switchToPread) throws IOException { 226 initHRegion(methodName, conf, builder, hcd, hook, switchToPread); 227 if (hook == null) { 228 store = new HStore(region, hcd, conf); 229 } else { 230 store = new MyStore(region, hcd, conf, hook, switchToPread); 231 } 232 return store; 233 } 234 235 /** 236 * Test we do not lose data if we fail a flush and then close. 237 * Part of HBase-10466 238 * @throws Exception 239 */ 240 @Test 241 public void testFlushSizeSizing() throws Exception { 242 LOG.info("Setting up a faulty file system that cannot write in " + this.name.getMethodName()); 243 final Configuration conf = HBaseConfiguration.create(TEST_UTIL.getConfiguration()); 244 // Only retry once. 245 conf.setInt("hbase.hstore.flush.retries.number", 1); 246 User user = User.createUserForTesting(conf, this.name.getMethodName(), 247 new String[]{"foo"}); 248 // Inject our faulty LocalFileSystem 249 conf.setClass("fs.file.impl", FaultyFileSystem.class, FileSystem.class); 250 user.runAs(new PrivilegedExceptionAction<Object>() { 251 @Override 252 public Object run() throws Exception { 253 // Make sure it worked (above is sensitive to caching details in hadoop core) 254 FileSystem fs = FileSystem.get(conf); 255 assertEquals(FaultyFileSystem.class, fs.getClass()); 256 FaultyFileSystem ffs = (FaultyFileSystem)fs; 257 258 // Initialize region 259 init(name.getMethodName(), conf); 260 261 MemStoreSize mss = store.memstore.getFlushableSize(); 262 assertEquals(0, mss.getDataSize()); 263 LOG.info("Adding some data"); 264 MemStoreSizing kvSize = new NonThreadSafeMemStoreSizing(); 265 store.add(new KeyValue(row, family, qf1, 1, (byte[]) null), kvSize); 266 // add the heap size of active (mutable) segment 267 kvSize.incMemStoreSize(0, MutableSegment.DEEP_OVERHEAD, 0, 0); 268 mss = store.memstore.getFlushableSize(); 269 assertEquals(kvSize.getMemStoreSize(), mss); 270 // Flush. Bug #1 from HBASE-10466. Make sure size calculation on failed flush is right. 271 try { 272 LOG.info("Flushing"); 273 flushStore(store, id++); 274 fail("Didn't bubble up IOE!"); 275 } catch (IOException ioe) { 276 assertTrue(ioe.getMessage().contains("Fault injected")); 277 } 278 // due to snapshot, change mutable to immutable segment 279 kvSize.incMemStoreSize(0, 280 CSLMImmutableSegment.DEEP_OVERHEAD_CSLM - MutableSegment.DEEP_OVERHEAD, 0, 0); 281 mss = store.memstore.getFlushableSize(); 282 assertEquals(kvSize.getMemStoreSize(), mss); 283 MemStoreSizing kvSize2 = new NonThreadSafeMemStoreSizing(); 284 store.add(new KeyValue(row, family, qf2, 2, (byte[]) null), kvSize2); 285 kvSize2.incMemStoreSize(0, MutableSegment.DEEP_OVERHEAD, 0, 0); 286 // Even though we add a new kv, we expect the flushable size to be 'same' since we have 287 // not yet cleared the snapshot -- the above flush failed. 288 assertEquals(kvSize.getMemStoreSize(), mss); 289 ffs.fault.set(false); 290 flushStore(store, id++); 291 mss = store.memstore.getFlushableSize(); 292 // Size should be the foreground kv size. 293 assertEquals(kvSize2.getMemStoreSize(), mss); 294 flushStore(store, id++); 295 mss = store.memstore.getFlushableSize(); 296 assertEquals(0, mss.getDataSize()); 297 assertEquals(MutableSegment.DEEP_OVERHEAD, mss.getHeapSize()); 298 return null; 299 } 300 }); 301 } 302 303 /** 304 * Verify that compression and data block encoding are respected by the 305 * Store.createWriterInTmp() method, used on store flush. 306 */ 307 @Test 308 public void testCreateWriter() throws Exception { 309 Configuration conf = HBaseConfiguration.create(); 310 FileSystem fs = FileSystem.get(conf); 311 312 ColumnFamilyDescriptor hcd = ColumnFamilyDescriptorBuilder.newBuilder(family) 313 .setCompressionType(Compression.Algorithm.GZ).setDataBlockEncoding(DataBlockEncoding.DIFF) 314 .build(); 315 init(name.getMethodName(), conf, hcd); 316 317 // Test createWriterInTmp() 318 StoreFileWriter writer = 319 store.createWriterInTmp(4, hcd.getCompressionType(), false, true, false, false); 320 Path path = writer.getPath(); 321 writer.append(new KeyValue(row, family, qf1, Bytes.toBytes(1))); 322 writer.append(new KeyValue(row, family, qf2, Bytes.toBytes(2))); 323 writer.append(new KeyValue(row2, family, qf1, Bytes.toBytes(3))); 324 writer.append(new KeyValue(row2, family, qf2, Bytes.toBytes(4))); 325 writer.close(); 326 327 // Verify that compression and encoding settings are respected 328 HFile.Reader reader = HFile.createReader(fs, path, new CacheConfig(conf), true, conf); 329 assertEquals(hcd.getCompressionType(), reader.getCompressionAlgorithm()); 330 assertEquals(hcd.getDataBlockEncoding(), reader.getDataBlockEncoding()); 331 reader.close(); 332 } 333 334 @Test 335 public void testDeleteExpiredStoreFiles() throws Exception { 336 testDeleteExpiredStoreFiles(0); 337 testDeleteExpiredStoreFiles(1); 338 } 339 340 /* 341 * @param minVersions the MIN_VERSIONS for the column family 342 */ 343 public void testDeleteExpiredStoreFiles(int minVersions) throws Exception { 344 int storeFileNum = 4; 345 int ttl = 4; 346 IncrementingEnvironmentEdge edge = new IncrementingEnvironmentEdge(); 347 EnvironmentEdgeManagerTestHelper.injectEdge(edge); 348 349 Configuration conf = HBaseConfiguration.create(); 350 // Enable the expired store file deletion 351 conf.setBoolean("hbase.store.delete.expired.storefile", true); 352 // Set the compaction threshold higher to avoid normal compactions. 353 conf.setInt(CompactionConfiguration.HBASE_HSTORE_COMPACTION_MIN_KEY, 5); 354 355 init(name.getMethodName() + "-" + minVersions, conf, ColumnFamilyDescriptorBuilder 356 .newBuilder(family).setMinVersions(minVersions).setTimeToLive(ttl).build()); 357 358 long storeTtl = this.store.getScanInfo().getTtl(); 359 long sleepTime = storeTtl / storeFileNum; 360 long timeStamp; 361 // There are 4 store files and the max time stamp difference among these 362 // store files will be (this.store.ttl / storeFileNum) 363 for (int i = 1; i <= storeFileNum; i++) { 364 LOG.info("Adding some data for the store file #" + i); 365 timeStamp = EnvironmentEdgeManager.currentTime(); 366 this.store.add(new KeyValue(row, family, qf1, timeStamp, (byte[]) null), null); 367 this.store.add(new KeyValue(row, family, qf2, timeStamp, (byte[]) null), null); 368 this.store.add(new KeyValue(row, family, qf3, timeStamp, (byte[]) null), null); 369 flush(i); 370 edge.incrementTime(sleepTime); 371 } 372 373 // Verify the total number of store files 374 assertEquals(storeFileNum, this.store.getStorefiles().size()); 375 376 // Each call will find one expired store file and delete it before compaction happens. 377 // There will be no compaction due to threshold above. Last file will not be replaced. 378 for (int i = 1; i <= storeFileNum - 1; i++) { 379 // verify the expired store file. 380 assertFalse(this.store.requestCompaction().isPresent()); 381 Collection<HStoreFile> sfs = this.store.getStorefiles(); 382 // Ensure i files are gone. 383 if (minVersions == 0) { 384 assertEquals(storeFileNum - i, sfs.size()); 385 // Ensure only non-expired files remain. 386 for (HStoreFile sf : sfs) { 387 assertTrue(sf.getReader().getMaxTimestamp() >= (edge.currentTime() - storeTtl)); 388 } 389 } else { 390 assertEquals(storeFileNum, sfs.size()); 391 } 392 // Let the next store file expired. 393 edge.incrementTime(sleepTime); 394 } 395 assertFalse(this.store.requestCompaction().isPresent()); 396 397 Collection<HStoreFile> sfs = this.store.getStorefiles(); 398 // Assert the last expired file is not removed. 399 if (minVersions == 0) { 400 assertEquals(1, sfs.size()); 401 } 402 long ts = sfs.iterator().next().getReader().getMaxTimestamp(); 403 assertTrue(ts < (edge.currentTime() - storeTtl)); 404 405 for (HStoreFile sf : sfs) { 406 sf.closeStoreFile(true); 407 } 408 } 409 410 @Test 411 public void testLowestModificationTime() throws Exception { 412 Configuration conf = HBaseConfiguration.create(); 413 FileSystem fs = FileSystem.get(conf); 414 // Initialize region 415 init(name.getMethodName(), conf); 416 417 int storeFileNum = 4; 418 for (int i = 1; i <= storeFileNum; i++) { 419 LOG.info("Adding some data for the store file #"+i); 420 this.store.add(new KeyValue(row, family, qf1, i, (byte[])null), null); 421 this.store.add(new KeyValue(row, family, qf2, i, (byte[])null), null); 422 this.store.add(new KeyValue(row, family, qf3, i, (byte[])null), null); 423 flush(i); 424 } 425 // after flush; check the lowest time stamp 426 long lowestTimeStampFromManager = StoreUtils.getLowestTimestamp(store.getStorefiles()); 427 long lowestTimeStampFromFS = getLowestTimeStampFromFS(fs, store.getStorefiles()); 428 assertEquals(lowestTimeStampFromManager,lowestTimeStampFromFS); 429 430 // after compact; check the lowest time stamp 431 store.compact(store.requestCompaction().get(), NoLimitThroughputController.INSTANCE, null); 432 lowestTimeStampFromManager = StoreUtils.getLowestTimestamp(store.getStorefiles()); 433 lowestTimeStampFromFS = getLowestTimeStampFromFS(fs, store.getStorefiles()); 434 assertEquals(lowestTimeStampFromManager, lowestTimeStampFromFS); 435 } 436 437 private static long getLowestTimeStampFromFS(FileSystem fs, 438 final Collection<HStoreFile> candidates) throws IOException { 439 long minTs = Long.MAX_VALUE; 440 if (candidates.isEmpty()) { 441 return minTs; 442 } 443 Path[] p = new Path[candidates.size()]; 444 int i = 0; 445 for (HStoreFile sf : candidates) { 446 p[i] = sf.getPath(); 447 ++i; 448 } 449 450 FileStatus[] stats = fs.listStatus(p); 451 if (stats == null || stats.length == 0) { 452 return minTs; 453 } 454 for (FileStatus s : stats) { 455 minTs = Math.min(minTs, s.getModificationTime()); 456 } 457 return minTs; 458 } 459 460 ////////////////////////////////////////////////////////////////////////////// 461 // Get tests 462 ////////////////////////////////////////////////////////////////////////////// 463 464 private static final int BLOCKSIZE_SMALL = 8192; 465 /** 466 * Test for hbase-1686. 467 * @throws IOException 468 */ 469 @Test 470 public void testEmptyStoreFile() throws IOException { 471 init(this.name.getMethodName()); 472 // Write a store file. 473 this.store.add(new KeyValue(row, family, qf1, 1, (byte[])null), null); 474 this.store.add(new KeyValue(row, family, qf2, 1, (byte[])null), null); 475 flush(1); 476 // Now put in place an empty store file. Its a little tricky. Have to 477 // do manually with hacked in sequence id. 478 HStoreFile f = this.store.getStorefiles().iterator().next(); 479 Path storedir = f.getPath().getParent(); 480 long seqid = f.getMaxSequenceId(); 481 Configuration c = HBaseConfiguration.create(); 482 FileSystem fs = FileSystem.get(c); 483 HFileContext meta = new HFileContextBuilder().withBlockSize(BLOCKSIZE_SMALL).build(); 484 StoreFileWriter w = new StoreFileWriter.Builder(c, new CacheConfig(c), 485 fs) 486 .withOutputDir(storedir) 487 .withFileContext(meta) 488 .build(); 489 w.appendMetadata(seqid + 1, false); 490 w.close(); 491 this.store.close(); 492 // Reopen it... should pick up two files 493 this.store = new HStore(this.store.getHRegion(), this.store.getColumnFamilyDescriptor(), c); 494 assertEquals(2, this.store.getStorefilesCount()); 495 496 result = HBaseTestingUtility.getFromStoreFile(store, 497 get.getRow(), 498 qualifiers); 499 assertEquals(1, result.size()); 500 } 501 502 /** 503 * Getting data from memstore only 504 * @throws IOException 505 */ 506 @Test 507 public void testGet_FromMemStoreOnly() throws IOException { 508 init(this.name.getMethodName()); 509 510 //Put data in memstore 511 this.store.add(new KeyValue(row, family, qf1, 1, (byte[])null), null); 512 this.store.add(new KeyValue(row, family, qf2, 1, (byte[])null), null); 513 this.store.add(new KeyValue(row, family, qf3, 1, (byte[])null), null); 514 this.store.add(new KeyValue(row, family, qf4, 1, (byte[])null), null); 515 this.store.add(new KeyValue(row, family, qf5, 1, (byte[])null), null); 516 this.store.add(new KeyValue(row, family, qf6, 1, (byte[])null), null); 517 518 //Get 519 result = HBaseTestingUtility.getFromStoreFile(store, 520 get.getRow(), qualifiers); 521 522 //Compare 523 assertCheck(); 524 } 525 526 @Test 527 public void testTimeRangeIfSomeCellsAreDroppedInFlush() throws IOException { 528 testTimeRangeIfSomeCellsAreDroppedInFlush(1); 529 testTimeRangeIfSomeCellsAreDroppedInFlush(3); 530 testTimeRangeIfSomeCellsAreDroppedInFlush(5); 531 } 532 533 private void testTimeRangeIfSomeCellsAreDroppedInFlush(int maxVersion) throws IOException { 534 init(this.name.getMethodName(), TEST_UTIL.getConfiguration(), 535 ColumnFamilyDescriptorBuilder.newBuilder(family).setMaxVersions(maxVersion).build()); 536 long currentTs = 100; 537 long minTs = currentTs; 538 // the extra cell won't be flushed to disk, 539 // so the min of timerange will be different between memStore and hfile. 540 for (int i = 0; i != (maxVersion + 1); ++i) { 541 this.store.add(new KeyValue(row, family, qf1, ++currentTs, (byte[])null), null); 542 if (i == 1) { 543 minTs = currentTs; 544 } 545 } 546 flushStore(store, id++); 547 548 Collection<HStoreFile> files = store.getStorefiles(); 549 assertEquals(1, files.size()); 550 HStoreFile f = files.iterator().next(); 551 f.initReader(); 552 StoreFileReader reader = f.getReader(); 553 assertEquals(minTs, reader.timeRange.getMin()); 554 assertEquals(currentTs, reader.timeRange.getMax()); 555 } 556 557 /** 558 * Getting data from files only 559 * @throws IOException 560 */ 561 @Test 562 public void testGet_FromFilesOnly() throws IOException { 563 init(this.name.getMethodName()); 564 565 //Put data in memstore 566 this.store.add(new KeyValue(row, family, qf1, 1, (byte[])null), null); 567 this.store.add(new KeyValue(row, family, qf2, 1, (byte[])null), null); 568 //flush 569 flush(1); 570 571 //Add more data 572 this.store.add(new KeyValue(row, family, qf3, 1, (byte[])null), null); 573 this.store.add(new KeyValue(row, family, qf4, 1, (byte[])null), null); 574 //flush 575 flush(2); 576 577 //Add more data 578 this.store.add(new KeyValue(row, family, qf5, 1, (byte[])null), null); 579 this.store.add(new KeyValue(row, family, qf6, 1, (byte[])null), null); 580 //flush 581 flush(3); 582 583 //Get 584 result = HBaseTestingUtility.getFromStoreFile(store, 585 get.getRow(), 586 qualifiers); 587 //this.store.get(get, qualifiers, result); 588 589 //Need to sort the result since multiple files 590 Collections.sort(result, CellComparatorImpl.COMPARATOR); 591 592 //Compare 593 assertCheck(); 594 } 595 596 /** 597 * Getting data from memstore and files 598 * @throws IOException 599 */ 600 @Test 601 public void testGet_FromMemStoreAndFiles() throws IOException { 602 init(this.name.getMethodName()); 603 604 //Put data in memstore 605 this.store.add(new KeyValue(row, family, qf1, 1, (byte[])null), null); 606 this.store.add(new KeyValue(row, family, qf2, 1, (byte[])null), null); 607 //flush 608 flush(1); 609 610 //Add more data 611 this.store.add(new KeyValue(row, family, qf3, 1, (byte[])null), null); 612 this.store.add(new KeyValue(row, family, qf4, 1, (byte[])null), null); 613 //flush 614 flush(2); 615 616 //Add more data 617 this.store.add(new KeyValue(row, family, qf5, 1, (byte[])null), null); 618 this.store.add(new KeyValue(row, family, qf6, 1, (byte[])null), null); 619 620 //Get 621 result = HBaseTestingUtility.getFromStoreFile(store, 622 get.getRow(), qualifiers); 623 624 //Need to sort the result since multiple files 625 Collections.sort(result, CellComparatorImpl.COMPARATOR); 626 627 //Compare 628 assertCheck(); 629 } 630 631 private void flush(int storeFilessize) throws IOException{ 632 this.store.snapshot(); 633 flushStore(store, id++); 634 assertEquals(storeFilessize, this.store.getStorefiles().size()); 635 assertEquals(0, ((AbstractMemStore)this.store.memstore).getActive().getCellsCount()); 636 } 637 638 private void assertCheck() { 639 assertEquals(expected.size(), result.size()); 640 for(int i=0; i<expected.size(); i++) { 641 assertEquals(expected.get(i), result.get(i)); 642 } 643 } 644 645 @After 646 public void tearDown() throws Exception { 647 EnvironmentEdgeManagerTestHelper.reset(); 648 if (store != null) { 649 try { 650 store.close(); 651 } catch (IOException e) { 652 } 653 store = null; 654 } 655 if (region != null) { 656 region.close(); 657 region = null; 658 } 659 } 660 661 @AfterClass 662 public static void tearDownAfterClass() throws IOException { 663 TEST_UTIL.cleanupTestDir(); 664 } 665 666 @Test 667 public void testHandleErrorsInFlush() throws Exception { 668 LOG.info("Setting up a faulty file system that cannot write"); 669 670 final Configuration conf = HBaseConfiguration.create(TEST_UTIL.getConfiguration()); 671 User user = User.createUserForTesting(conf, 672 "testhandleerrorsinflush", new String[]{"foo"}); 673 // Inject our faulty LocalFileSystem 674 conf.setClass("fs.file.impl", FaultyFileSystem.class, 675 FileSystem.class); 676 user.runAs(new PrivilegedExceptionAction<Object>() { 677 @Override 678 public Object run() throws Exception { 679 // Make sure it worked (above is sensitive to caching details in hadoop core) 680 FileSystem fs = FileSystem.get(conf); 681 assertEquals(FaultyFileSystem.class, fs.getClass()); 682 683 // Initialize region 684 init(name.getMethodName(), conf); 685 686 LOG.info("Adding some data"); 687 store.add(new KeyValue(row, family, qf1, 1, (byte[])null), null); 688 store.add(new KeyValue(row, family, qf2, 1, (byte[])null), null); 689 store.add(new KeyValue(row, family, qf3, 1, (byte[])null), null); 690 691 LOG.info("Before flush, we should have no files"); 692 693 Collection<StoreFileInfo> files = 694 store.getRegionFileSystem().getStoreFiles(store.getColumnFamilyName()); 695 assertEquals(0, files != null ? files.size() : 0); 696 697 //flush 698 try { 699 LOG.info("Flushing"); 700 flush(1); 701 fail("Didn't bubble up IOE!"); 702 } catch (IOException ioe) { 703 assertTrue(ioe.getMessage().contains("Fault injected")); 704 } 705 706 LOG.info("After failed flush, we should still have no files!"); 707 files = store.getRegionFileSystem().getStoreFiles(store.getColumnFamilyName()); 708 assertEquals(0, files != null ? files.size() : 0); 709 store.getHRegion().getWAL().close(); 710 return null; 711 } 712 }); 713 FileSystem.closeAllForUGI(user.getUGI()); 714 } 715 716 /** 717 * Faulty file system that will fail if you write past its fault position the FIRST TIME 718 * only; thereafter it will succeed. Used by {@link TestHRegion} too. 719 */ 720 static class FaultyFileSystem extends FilterFileSystem { 721 List<SoftReference<FaultyOutputStream>> outStreams = new ArrayList<>(); 722 private long faultPos = 200; 723 AtomicBoolean fault = new AtomicBoolean(true); 724 725 public FaultyFileSystem() { 726 super(new LocalFileSystem()); 727 System.err.println("Creating faulty!"); 728 } 729 730 @Override 731 public FSDataOutputStream create(Path p) throws IOException { 732 return new FaultyOutputStream(super.create(p), faultPos, fault); 733 } 734 735 @Override 736 public FSDataOutputStream create(Path f, FsPermission permission, 737 boolean overwrite, int bufferSize, short replication, long blockSize, 738 Progressable progress) throws IOException { 739 return new FaultyOutputStream(super.create(f, permission, 740 overwrite, bufferSize, replication, blockSize, progress), faultPos, fault); 741 } 742 743 @Override 744 public FSDataOutputStream createNonRecursive(Path f, boolean overwrite, 745 int bufferSize, short replication, long blockSize, Progressable progress) 746 throws IOException { 747 // Fake it. Call create instead. The default implementation throws an IOE 748 // that this is not supported. 749 return create(f, overwrite, bufferSize, replication, blockSize, progress); 750 } 751 } 752 753 static class FaultyOutputStream extends FSDataOutputStream { 754 volatile long faultPos = Long.MAX_VALUE; 755 private final AtomicBoolean fault; 756 757 public FaultyOutputStream(FSDataOutputStream out, long faultPos, final AtomicBoolean fault) 758 throws IOException { 759 super(out, null); 760 this.faultPos = faultPos; 761 this.fault = fault; 762 } 763 764 @Override 765 public synchronized void write(byte[] buf, int offset, int length) throws IOException { 766 System.err.println("faulty stream write at pos " + getPos()); 767 injectFault(); 768 super.write(buf, offset, length); 769 } 770 771 private void injectFault() throws IOException { 772 if (this.fault.get() && getPos() >= faultPos) { 773 throw new IOException("Fault injected"); 774 } 775 } 776 } 777 778 private static void flushStore(HStore store, long id) throws IOException { 779 StoreFlushContext storeFlushCtx = store.createFlushContext(id, FlushLifeCycleTracker.DUMMY); 780 storeFlushCtx.prepare(); 781 storeFlushCtx.flushCache(Mockito.mock(MonitoredTask.class)); 782 storeFlushCtx.commit(Mockito.mock(MonitoredTask.class)); 783 } 784 785 /** 786 * Generate a list of KeyValues for testing based on given parameters 787 * @param timestamps 788 * @param numRows 789 * @param qualifier 790 * @param family 791 * @return 792 */ 793 List<Cell> getKeyValueSet(long[] timestamps, int numRows, 794 byte[] qualifier, byte[] family) { 795 List<Cell> kvList = new ArrayList<>(); 796 for (int i=1;i<=numRows;i++) { 797 byte[] b = Bytes.toBytes(i); 798 for (long timestamp: timestamps) { 799 kvList.add(new KeyValue(b, family, qualifier, timestamp, b)); 800 } 801 } 802 return kvList; 803 } 804 805 /** 806 * Test to ensure correctness when using Stores with multiple timestamps 807 * @throws IOException 808 */ 809 @Test 810 public void testMultipleTimestamps() throws IOException { 811 int numRows = 1; 812 long[] timestamps1 = new long[] {1,5,10,20}; 813 long[] timestamps2 = new long[] {30,80}; 814 815 init(this.name.getMethodName()); 816 817 List<Cell> kvList1 = getKeyValueSet(timestamps1,numRows, qf1, family); 818 for (Cell kv : kvList1) { 819 this.store.add(kv, null); 820 } 821 822 this.store.snapshot(); 823 flushStore(store, id++); 824 825 List<Cell> kvList2 = getKeyValueSet(timestamps2,numRows, qf1, family); 826 for(Cell kv : kvList2) { 827 this.store.add(kv, null); 828 } 829 830 List<Cell> result; 831 Get get = new Get(Bytes.toBytes(1)); 832 get.addColumn(family,qf1); 833 834 get.setTimeRange(0,15); 835 result = HBaseTestingUtility.getFromStoreFile(store, get); 836 assertTrue(result.size()>0); 837 838 get.setTimeRange(40,90); 839 result = HBaseTestingUtility.getFromStoreFile(store, get); 840 assertTrue(result.size()>0); 841 842 get.setTimeRange(10,45); 843 result = HBaseTestingUtility.getFromStoreFile(store, get); 844 assertTrue(result.size()>0); 845 846 get.setTimeRange(80,145); 847 result = HBaseTestingUtility.getFromStoreFile(store, get); 848 assertTrue(result.size()>0); 849 850 get.setTimeRange(1,2); 851 result = HBaseTestingUtility.getFromStoreFile(store, get); 852 assertTrue(result.size()>0); 853 854 get.setTimeRange(90,200); 855 result = HBaseTestingUtility.getFromStoreFile(store, get); 856 assertTrue(result.size()==0); 857 } 858 859 /** 860 * Test for HBASE-3492 - Test split on empty colfam (no store files). 861 * 862 * @throws IOException When the IO operations fail. 863 */ 864 @Test 865 public void testSplitWithEmptyColFam() throws IOException { 866 init(this.name.getMethodName()); 867 assertFalse(store.getSplitPoint().isPresent()); 868 store.getHRegion().forceSplit(null); 869 assertFalse(store.getSplitPoint().isPresent()); 870 store.getHRegion().clearSplit(); 871 } 872 873 @Test 874 public void testStoreUsesConfigurationFromHcdAndHtd() throws Exception { 875 final String CONFIG_KEY = "hbase.regionserver.thread.compaction.throttle"; 876 long anyValue = 10; 877 878 // We'll check that it uses correct config and propagates it appropriately by going thru 879 // the simplest "real" path I can find - "throttleCompaction", which just checks whether 880 // a number we pass in is higher than some config value, inside compactionPolicy. 881 Configuration conf = HBaseConfiguration.create(); 882 conf.setLong(CONFIG_KEY, anyValue); 883 init(name.getMethodName() + "-xml", conf); 884 assertTrue(store.throttleCompaction(anyValue + 1)); 885 assertFalse(store.throttleCompaction(anyValue)); 886 887 // HTD overrides XML. 888 --anyValue; 889 init(name.getMethodName() + "-htd", conf, TableDescriptorBuilder 890 .newBuilder(TableName.valueOf(table)).setValue(CONFIG_KEY, Long.toString(anyValue)), 891 ColumnFamilyDescriptorBuilder.of(family)); 892 assertTrue(store.throttleCompaction(anyValue + 1)); 893 assertFalse(store.throttleCompaction(anyValue)); 894 895 // HCD overrides them both. 896 --anyValue; 897 init(name.getMethodName() + "-hcd", conf, 898 TableDescriptorBuilder.newBuilder(TableName.valueOf(table)).setValue(CONFIG_KEY, 899 Long.toString(anyValue)), 900 ColumnFamilyDescriptorBuilder.newBuilder(family).setValue(CONFIG_KEY, Long.toString(anyValue)) 901 .build()); 902 assertTrue(store.throttleCompaction(anyValue + 1)); 903 assertFalse(store.throttleCompaction(anyValue)); 904 } 905 906 public static class DummyStoreEngine extends DefaultStoreEngine { 907 public static DefaultCompactor lastCreatedCompactor = null; 908 909 @Override 910 protected void createComponents(Configuration conf, HStore store, CellComparator comparator) 911 throws IOException { 912 super.createComponents(conf, store, comparator); 913 lastCreatedCompactor = this.compactor; 914 } 915 } 916 917 @Test 918 public void testStoreUsesSearchEngineOverride() throws Exception { 919 Configuration conf = HBaseConfiguration.create(); 920 conf.set(StoreEngine.STORE_ENGINE_CLASS_KEY, DummyStoreEngine.class.getName()); 921 init(this.name.getMethodName(), conf); 922 assertEquals(DummyStoreEngine.lastCreatedCompactor, 923 this.store.storeEngine.getCompactor()); 924 } 925 926 private void addStoreFile() throws IOException { 927 HStoreFile f = this.store.getStorefiles().iterator().next(); 928 Path storedir = f.getPath().getParent(); 929 long seqid = this.store.getMaxSequenceId().orElse(0L); 930 Configuration c = TEST_UTIL.getConfiguration(); 931 FileSystem fs = FileSystem.get(c); 932 HFileContext fileContext = new HFileContextBuilder().withBlockSize(BLOCKSIZE_SMALL).build(); 933 StoreFileWriter w = new StoreFileWriter.Builder(c, new CacheConfig(c), 934 fs) 935 .withOutputDir(storedir) 936 .withFileContext(fileContext) 937 .build(); 938 w.appendMetadata(seqid + 1, false); 939 w.close(); 940 LOG.info("Added store file:" + w.getPath()); 941 } 942 943 private void archiveStoreFile(int index) throws IOException { 944 Collection<HStoreFile> files = this.store.getStorefiles(); 945 HStoreFile sf = null; 946 Iterator<HStoreFile> it = files.iterator(); 947 for (int i = 0; i <= index; i++) { 948 sf = it.next(); 949 } 950 store.getRegionFileSystem().removeStoreFiles(store.getColumnFamilyName(), Lists.newArrayList(sf)); 951 } 952 953 private void closeCompactedFile(int index) throws IOException { 954 Collection<HStoreFile> files = 955 this.store.getStoreEngine().getStoreFileManager().getCompactedfiles(); 956 HStoreFile sf = null; 957 Iterator<HStoreFile> it = files.iterator(); 958 for (int i = 0; i <= index; i++) { 959 sf = it.next(); 960 } 961 sf.closeStoreFile(true); 962 store.getStoreEngine().getStoreFileManager().removeCompactedFiles(Lists.newArrayList(sf)); 963 } 964 965 @Test 966 public void testRefreshStoreFiles() throws Exception { 967 init(name.getMethodName()); 968 969 assertEquals(0, this.store.getStorefilesCount()); 970 971 // Test refreshing store files when no store files are there 972 store.refreshStoreFiles(); 973 assertEquals(0, this.store.getStorefilesCount()); 974 975 // add some data, flush 976 this.store.add(new KeyValue(row, family, qf1, 1, (byte[])null), null); 977 flush(1); 978 assertEquals(1, this.store.getStorefilesCount()); 979 980 // add one more file 981 addStoreFile(); 982 983 assertEquals(1, this.store.getStorefilesCount()); 984 store.refreshStoreFiles(); 985 assertEquals(2, this.store.getStorefilesCount()); 986 987 // add three more files 988 addStoreFile(); 989 addStoreFile(); 990 addStoreFile(); 991 992 assertEquals(2, this.store.getStorefilesCount()); 993 store.refreshStoreFiles(); 994 assertEquals(5, this.store.getStorefilesCount()); 995 996 closeCompactedFile(0); 997 archiveStoreFile(0); 998 999 assertEquals(5, this.store.getStorefilesCount()); 1000 store.refreshStoreFiles(); 1001 assertEquals(4, this.store.getStorefilesCount()); 1002 1003 archiveStoreFile(0); 1004 archiveStoreFile(1); 1005 archiveStoreFile(2); 1006 1007 assertEquals(4, this.store.getStorefilesCount()); 1008 store.refreshStoreFiles(); 1009 assertEquals(1, this.store.getStorefilesCount()); 1010 1011 archiveStoreFile(0); 1012 store.refreshStoreFiles(); 1013 assertEquals(0, this.store.getStorefilesCount()); 1014 } 1015 1016 @Test 1017 public void testRefreshStoreFilesNotChanged() throws IOException { 1018 init(name.getMethodName()); 1019 1020 assertEquals(0, this.store.getStorefilesCount()); 1021 1022 // add some data, flush 1023 this.store.add(new KeyValue(row, family, qf1, 1, (byte[])null), null); 1024 flush(1); 1025 // add one more file 1026 addStoreFile(); 1027 1028 HStore spiedStore = spy(store); 1029 1030 // call first time after files changed 1031 spiedStore.refreshStoreFiles(); 1032 assertEquals(2, this.store.getStorefilesCount()); 1033 verify(spiedStore, times(1)).replaceStoreFiles(any(), any()); 1034 1035 // call second time 1036 spiedStore.refreshStoreFiles(); 1037 1038 //ensure that replaceStoreFiles is not called if files are not refreshed 1039 verify(spiedStore, times(0)).replaceStoreFiles(null, null); 1040 } 1041 1042 private long countMemStoreScanner(StoreScanner scanner) { 1043 if (scanner.currentScanners == null) { 1044 return 0; 1045 } 1046 return scanner.currentScanners.stream() 1047 .filter(s -> !s.isFileScanner()) 1048 .count(); 1049 } 1050 1051 @Test 1052 public void testNumberOfMemStoreScannersAfterFlush() throws IOException { 1053 long seqId = 100; 1054 long timestamp = System.currentTimeMillis(); 1055 Cell cell0 = CellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row).setFamily(family) 1056 .setQualifier(qf1).setTimestamp(timestamp).setType(Cell.Type.Put) 1057 .setValue(qf1).build(); 1058 PrivateCellUtil.setSequenceId(cell0, seqId); 1059 testNumberOfMemStoreScannersAfterFlush(Arrays.asList(cell0), Collections.emptyList()); 1060 1061 Cell cell1 = CellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row).setFamily(family) 1062 .setQualifier(qf2).setTimestamp(timestamp).setType(Cell.Type.Put) 1063 .setValue(qf1).build(); 1064 PrivateCellUtil.setSequenceId(cell1, seqId); 1065 testNumberOfMemStoreScannersAfterFlush(Arrays.asList(cell0), Arrays.asList(cell1)); 1066 1067 seqId = 101; 1068 timestamp = System.currentTimeMillis(); 1069 Cell cell2 = CellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row2).setFamily(family) 1070 .setQualifier(qf2).setTimestamp(timestamp).setType(Cell.Type.Put) 1071 .setValue(qf1).build(); 1072 PrivateCellUtil.setSequenceId(cell2, seqId); 1073 testNumberOfMemStoreScannersAfterFlush(Arrays.asList(cell0), Arrays.asList(cell1, cell2)); 1074 } 1075 1076 private void testNumberOfMemStoreScannersAfterFlush(List<Cell> inputCellsBeforeSnapshot, 1077 List<Cell> inputCellsAfterSnapshot) throws IOException { 1078 init(this.name.getMethodName() + "-" + inputCellsBeforeSnapshot.size()); 1079 TreeSet<byte[]> quals = new TreeSet<>(Bytes.BYTES_COMPARATOR); 1080 long seqId = Long.MIN_VALUE; 1081 for (Cell c : inputCellsBeforeSnapshot) { 1082 quals.add(CellUtil.cloneQualifier(c)); 1083 seqId = Math.max(seqId, c.getSequenceId()); 1084 } 1085 for (Cell c : inputCellsAfterSnapshot) { 1086 quals.add(CellUtil.cloneQualifier(c)); 1087 seqId = Math.max(seqId, c.getSequenceId()); 1088 } 1089 inputCellsBeforeSnapshot.forEach(c -> store.add(c, null)); 1090 StoreFlushContext storeFlushCtx = store.createFlushContext(id++, FlushLifeCycleTracker.DUMMY); 1091 storeFlushCtx.prepare(); 1092 inputCellsAfterSnapshot.forEach(c -> store.add(c, null)); 1093 int numberOfMemScannersBeforeFlush = inputCellsAfterSnapshot.isEmpty() ? 1 : 2; 1094 try (StoreScanner s = (StoreScanner) store.getScanner(new Scan(), quals, seqId)) { 1095 // snapshot + active (if inputCellsAfterSnapshot isn't empty) 1096 assertEquals(numberOfMemScannersBeforeFlush, countMemStoreScanner(s)); 1097 storeFlushCtx.flushCache(Mockito.mock(MonitoredTask.class)); 1098 storeFlushCtx.commit(Mockito.mock(MonitoredTask.class)); 1099 // snapshot has no data after flush 1100 int numberOfMemScannersAfterFlush = inputCellsAfterSnapshot.isEmpty() ? 0 : 1; 1101 boolean more; 1102 int cellCount = 0; 1103 do { 1104 List<Cell> cells = new ArrayList<>(); 1105 more = s.next(cells); 1106 cellCount += cells.size(); 1107 assertEquals(more ? numberOfMemScannersAfterFlush : 0, countMemStoreScanner(s)); 1108 } while (more); 1109 assertEquals("The number of cells added before snapshot is " + inputCellsBeforeSnapshot.size() 1110 + ", The number of cells added after snapshot is " + inputCellsAfterSnapshot.size(), 1111 inputCellsBeforeSnapshot.size() + inputCellsAfterSnapshot.size(), cellCount); 1112 // the current scanners is cleared 1113 assertEquals(0, countMemStoreScanner(s)); 1114 } 1115 } 1116 1117 private Cell createCell(byte[] qualifier, long ts, long sequenceId, byte[] value) 1118 throws IOException { 1119 return createCell(row, qualifier, ts, sequenceId, value); 1120 } 1121 1122 private Cell createCell(byte[] row, byte[] qualifier, long ts, long sequenceId, byte[] value) 1123 throws IOException { 1124 Cell c = CellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row).setFamily(family) 1125 .setQualifier(qualifier).setTimestamp(ts).setType(Cell.Type.Put) 1126 .setValue(value).build(); 1127 PrivateCellUtil.setSequenceId(c, sequenceId); 1128 return c; 1129 } 1130 1131 @Test 1132 public void testFlushBeforeCompletingScanWoFilter() throws IOException, InterruptedException { 1133 final AtomicBoolean timeToGoNextRow = new AtomicBoolean(false); 1134 final int expectedSize = 3; 1135 testFlushBeforeCompletingScan(new MyListHook() { 1136 @Override 1137 public void hook(int currentSize) { 1138 if (currentSize == expectedSize - 1) { 1139 try { 1140 flushStore(store, id++); 1141 timeToGoNextRow.set(true); 1142 } catch (IOException e) { 1143 throw new RuntimeException(e); 1144 } 1145 } 1146 } 1147 }, new FilterBase() { 1148 @Override 1149 public Filter.ReturnCode filterCell(final Cell c) throws IOException { 1150 return ReturnCode.INCLUDE; 1151 } 1152 }, expectedSize); 1153 } 1154 1155 @Test 1156 public void testFlushBeforeCompletingScanWithFilter() throws IOException, InterruptedException { 1157 final AtomicBoolean timeToGoNextRow = new AtomicBoolean(false); 1158 final int expectedSize = 2; 1159 testFlushBeforeCompletingScan(new MyListHook() { 1160 @Override 1161 public void hook(int currentSize) { 1162 if (currentSize == expectedSize - 1) { 1163 try { 1164 flushStore(store, id++); 1165 timeToGoNextRow.set(true); 1166 } catch (IOException e) { 1167 throw new RuntimeException(e); 1168 } 1169 } 1170 } 1171 }, new FilterBase() { 1172 @Override 1173 public Filter.ReturnCode filterCell(final Cell c) throws IOException { 1174 if (timeToGoNextRow.get()) { 1175 timeToGoNextRow.set(false); 1176 return ReturnCode.NEXT_ROW; 1177 } else { 1178 return ReturnCode.INCLUDE; 1179 } 1180 } 1181 }, expectedSize); 1182 } 1183 1184 @Test 1185 public void testFlushBeforeCompletingScanWithFilterHint() throws IOException, 1186 InterruptedException { 1187 final AtomicBoolean timeToGetHint = new AtomicBoolean(false); 1188 final int expectedSize = 2; 1189 testFlushBeforeCompletingScan(new MyListHook() { 1190 @Override 1191 public void hook(int currentSize) { 1192 if (currentSize == expectedSize - 1) { 1193 try { 1194 flushStore(store, id++); 1195 timeToGetHint.set(true); 1196 } catch (IOException e) { 1197 throw new RuntimeException(e); 1198 } 1199 } 1200 } 1201 }, new FilterBase() { 1202 @Override 1203 public Filter.ReturnCode filterCell(final Cell c) throws IOException { 1204 if (timeToGetHint.get()) { 1205 timeToGetHint.set(false); 1206 return Filter.ReturnCode.SEEK_NEXT_USING_HINT; 1207 } else { 1208 return Filter.ReturnCode.INCLUDE; 1209 } 1210 } 1211 @Override 1212 public Cell getNextCellHint(Cell currentCell) throws IOException { 1213 return currentCell; 1214 } 1215 }, expectedSize); 1216 } 1217 1218 private void testFlushBeforeCompletingScan(MyListHook hook, Filter filter, int expectedSize) 1219 throws IOException, InterruptedException { 1220 Configuration conf = HBaseConfiguration.create(); 1221 byte[] r0 = Bytes.toBytes("row0"); 1222 byte[] r1 = Bytes.toBytes("row1"); 1223 byte[] r2 = Bytes.toBytes("row2"); 1224 byte[] value0 = Bytes.toBytes("value0"); 1225 byte[] value1 = Bytes.toBytes("value1"); 1226 byte[] value2 = Bytes.toBytes("value2"); 1227 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 1228 long ts = EnvironmentEdgeManager.currentTime(); 1229 long seqId = 100; 1230 init(name.getMethodName(), conf, TableDescriptorBuilder.newBuilder(TableName.valueOf(table)), 1231 ColumnFamilyDescriptorBuilder.newBuilder(family).setMaxVersions(1).build(), 1232 new MyStoreHook() { 1233 @Override 1234 public long getSmallestReadPoint(HStore store) { 1235 return seqId + 3; 1236 } 1237 }); 1238 // The cells having the value0 won't be flushed to disk because the value of max version is 1 1239 store.add(createCell(r0, qf1, ts, seqId, value0), memStoreSizing); 1240 store.add(createCell(r0, qf2, ts, seqId, value0), memStoreSizing); 1241 store.add(createCell(r0, qf3, ts, seqId, value0), memStoreSizing); 1242 store.add(createCell(r1, qf1, ts + 1, seqId + 1, value1), memStoreSizing); 1243 store.add(createCell(r1, qf2, ts + 1, seqId + 1, value1), memStoreSizing); 1244 store.add(createCell(r1, qf3, ts + 1, seqId + 1, value1), memStoreSizing); 1245 store.add(createCell(r2, qf1, ts + 2, seqId + 2, value2), memStoreSizing); 1246 store.add(createCell(r2, qf2, ts + 2, seqId + 2, value2), memStoreSizing); 1247 store.add(createCell(r2, qf3, ts + 2, seqId + 2, value2), memStoreSizing); 1248 store.add(createCell(r1, qf1, ts + 3, seqId + 3, value1), memStoreSizing); 1249 store.add(createCell(r1, qf2, ts + 3, seqId + 3, value1), memStoreSizing); 1250 store.add(createCell(r1, qf3, ts + 3, seqId + 3, value1), memStoreSizing); 1251 List<Cell> myList = new MyList<>(hook); 1252 Scan scan = new Scan() 1253 .withStartRow(r1) 1254 .setFilter(filter); 1255 try (InternalScanner scanner = (InternalScanner) store.getScanner( 1256 scan, null, seqId + 3)){ 1257 // r1 1258 scanner.next(myList); 1259 assertEquals(expectedSize, myList.size()); 1260 for (Cell c : myList) { 1261 byte[] actualValue = CellUtil.cloneValue(c); 1262 assertTrue("expected:" + Bytes.toStringBinary(value1) 1263 + ", actual:" + Bytes.toStringBinary(actualValue) 1264 , Bytes.equals(actualValue, value1)); 1265 } 1266 List<Cell> normalList = new ArrayList<>(3); 1267 // r2 1268 scanner.next(normalList); 1269 assertEquals(3, normalList.size()); 1270 for (Cell c : normalList) { 1271 byte[] actualValue = CellUtil.cloneValue(c); 1272 assertTrue("expected:" + Bytes.toStringBinary(value2) 1273 + ", actual:" + Bytes.toStringBinary(actualValue) 1274 , Bytes.equals(actualValue, value2)); 1275 } 1276 } 1277 } 1278 1279 @Test 1280 public void testCreateScannerAndSnapshotConcurrently() throws IOException, InterruptedException { 1281 Configuration conf = HBaseConfiguration.create(); 1282 conf.set(HStore.MEMSTORE_CLASS_NAME, MyCompactingMemStore.class.getName()); 1283 init(name.getMethodName(), conf, ColumnFamilyDescriptorBuilder.newBuilder(family) 1284 .setInMemoryCompaction(MemoryCompactionPolicy.BASIC).build()); 1285 byte[] value = Bytes.toBytes("value"); 1286 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 1287 long ts = EnvironmentEdgeManager.currentTime(); 1288 long seqId = 100; 1289 // older data whihc shouldn't be "seen" by client 1290 store.add(createCell(qf1, ts, seqId, value), memStoreSizing); 1291 store.add(createCell(qf2, ts, seqId, value), memStoreSizing); 1292 store.add(createCell(qf3, ts, seqId, value), memStoreSizing); 1293 TreeSet<byte[]> quals = new TreeSet<>(Bytes.BYTES_COMPARATOR); 1294 quals.add(qf1); 1295 quals.add(qf2); 1296 quals.add(qf3); 1297 StoreFlushContext storeFlushCtx = store.createFlushContext(id++, FlushLifeCycleTracker.DUMMY); 1298 MyCompactingMemStore.START_TEST.set(true); 1299 Runnable flush = () -> { 1300 // this is blocked until we create first scanner from pipeline and snapshot -- phase (1/5) 1301 // recreate the active memstore -- phase (4/5) 1302 storeFlushCtx.prepare(); 1303 }; 1304 ExecutorService service = Executors.newSingleThreadExecutor(); 1305 service.submit(flush); 1306 // we get scanner from pipeline and snapshot but they are empty. -- phase (2/5) 1307 // this is blocked until we recreate the active memstore -- phase (3/5) 1308 // we get scanner from active memstore but it is empty -- phase (5/5) 1309 InternalScanner scanner = (InternalScanner) store.getScanner( 1310 new Scan(new Get(row)), quals, seqId + 1); 1311 service.shutdown(); 1312 service.awaitTermination(20, TimeUnit.SECONDS); 1313 try { 1314 try { 1315 List<Cell> results = new ArrayList<>(); 1316 scanner.next(results); 1317 assertEquals(3, results.size()); 1318 for (Cell c : results) { 1319 byte[] actualValue = CellUtil.cloneValue(c); 1320 assertTrue("expected:" + Bytes.toStringBinary(value) 1321 + ", actual:" + Bytes.toStringBinary(actualValue) 1322 , Bytes.equals(actualValue, value)); 1323 } 1324 } finally { 1325 scanner.close(); 1326 } 1327 } finally { 1328 MyCompactingMemStore.START_TEST.set(false); 1329 storeFlushCtx.flushCache(Mockito.mock(MonitoredTask.class)); 1330 storeFlushCtx.commit(Mockito.mock(MonitoredTask.class)); 1331 } 1332 } 1333 1334 @Test 1335 public void testScanWithDoubleFlush() throws IOException { 1336 Configuration conf = HBaseConfiguration.create(); 1337 // Initialize region 1338 MyStore myStore = initMyStore(name.getMethodName(), conf, new MyStoreHook(){ 1339 @Override 1340 public void getScanners(MyStore store) throws IOException { 1341 final long tmpId = id++; 1342 ExecutorService s = Executors.newSingleThreadExecutor(); 1343 s.submit(() -> { 1344 try { 1345 // flush the store before storescanner updates the scanners from store. 1346 // The current data will be flushed into files, and the memstore will 1347 // be clear. 1348 // -- phase (4/4) 1349 flushStore(store, tmpId); 1350 }catch (IOException ex) { 1351 throw new RuntimeException(ex); 1352 } 1353 }); 1354 s.shutdown(); 1355 try { 1356 // wait for the flush, the thread will be blocked in HStore#notifyChangedReadersObservers. 1357 s.awaitTermination(3, TimeUnit.SECONDS); 1358 } catch (InterruptedException ex) { 1359 } 1360 } 1361 }); 1362 byte[] oldValue = Bytes.toBytes("oldValue"); 1363 byte[] currentValue = Bytes.toBytes("currentValue"); 1364 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 1365 long ts = EnvironmentEdgeManager.currentTime(); 1366 long seqId = 100; 1367 // older data whihc shouldn't be "seen" by client 1368 myStore.add(createCell(qf1, ts, seqId, oldValue), memStoreSizing); 1369 myStore.add(createCell(qf2, ts, seqId, oldValue), memStoreSizing); 1370 myStore.add(createCell(qf3, ts, seqId, oldValue), memStoreSizing); 1371 long snapshotId = id++; 1372 // push older data into snapshot -- phase (1/4) 1373 StoreFlushContext storeFlushCtx = store.createFlushContext(snapshotId, FlushLifeCycleTracker 1374 .DUMMY); 1375 storeFlushCtx.prepare(); 1376 1377 // insert current data into active -- phase (2/4) 1378 myStore.add(createCell(qf1, ts + 1, seqId + 1, currentValue), memStoreSizing); 1379 myStore.add(createCell(qf2, ts + 1, seqId + 1, currentValue), memStoreSizing); 1380 myStore.add(createCell(qf3, ts + 1, seqId + 1, currentValue), memStoreSizing); 1381 TreeSet<byte[]> quals = new TreeSet<>(Bytes.BYTES_COMPARATOR); 1382 quals.add(qf1); 1383 quals.add(qf2); 1384 quals.add(qf3); 1385 try (InternalScanner scanner = (InternalScanner) myStore.getScanner( 1386 new Scan(new Get(row)), quals, seqId + 1)) { 1387 // complete the flush -- phase (3/4) 1388 storeFlushCtx.flushCache(Mockito.mock(MonitoredTask.class)); 1389 storeFlushCtx.commit(Mockito.mock(MonitoredTask.class)); 1390 1391 List<Cell> results = new ArrayList<>(); 1392 scanner.next(results); 1393 assertEquals(3, results.size()); 1394 for (Cell c : results) { 1395 byte[] actualValue = CellUtil.cloneValue(c); 1396 assertTrue("expected:" + Bytes.toStringBinary(currentValue) 1397 + ", actual:" + Bytes.toStringBinary(actualValue) 1398 , Bytes.equals(actualValue, currentValue)); 1399 } 1400 } 1401 } 1402 1403 @Test 1404 public void testReclaimChunkWhenScaning() throws IOException { 1405 init("testReclaimChunkWhenScaning"); 1406 long ts = EnvironmentEdgeManager.currentTime(); 1407 long seqId = 100; 1408 byte[] value = Bytes.toBytes("value"); 1409 // older data whihc shouldn't be "seen" by client 1410 store.add(createCell(qf1, ts, seqId, value), null); 1411 store.add(createCell(qf2, ts, seqId, value), null); 1412 store.add(createCell(qf3, ts, seqId, value), null); 1413 TreeSet<byte[]> quals = new TreeSet<>(Bytes.BYTES_COMPARATOR); 1414 quals.add(qf1); 1415 quals.add(qf2); 1416 quals.add(qf3); 1417 try (InternalScanner scanner = (InternalScanner) store.getScanner( 1418 new Scan(new Get(row)), quals, seqId)) { 1419 List<Cell> results = new MyList<>(size -> { 1420 switch (size) { 1421 // 1) we get the first cell (qf1) 1422 // 2) flush the data to have StoreScanner update inner scanners 1423 // 3) the chunk will be reclaimed after updaing 1424 case 1: 1425 try { 1426 flushStore(store, id++); 1427 } catch (IOException e) { 1428 throw new RuntimeException(e); 1429 } 1430 break; 1431 // 1) we get the second cell (qf2) 1432 // 2) add some cell to fill some byte into the chunk (we have only one chunk) 1433 case 2: 1434 try { 1435 byte[] newValue = Bytes.toBytes("newValue"); 1436 // older data whihc shouldn't be "seen" by client 1437 store.add(createCell(qf1, ts + 1, seqId + 1, newValue), null); 1438 store.add(createCell(qf2, ts + 1, seqId + 1, newValue), null); 1439 store.add(createCell(qf3, ts + 1, seqId + 1, newValue), null); 1440 } catch (IOException e) { 1441 throw new RuntimeException(e); 1442 } 1443 break; 1444 default: 1445 break; 1446 } 1447 }); 1448 scanner.next(results); 1449 assertEquals(3, results.size()); 1450 for (Cell c : results) { 1451 byte[] actualValue = CellUtil.cloneValue(c); 1452 assertTrue("expected:" + Bytes.toStringBinary(value) 1453 + ", actual:" + Bytes.toStringBinary(actualValue) 1454 , Bytes.equals(actualValue, value)); 1455 } 1456 } 1457 } 1458 1459 /** 1460 * If there are two running InMemoryFlushRunnable, the later InMemoryFlushRunnable 1461 * may change the versionedList. And the first InMemoryFlushRunnable will use the chagned 1462 * versionedList to remove the corresponding segments. 1463 * In short, there will be some segements which isn't in merge are removed. 1464 * @throws IOException 1465 * @throws InterruptedException 1466 */ 1467 @Test 1468 public void testRunDoubleMemStoreCompactors() throws IOException, InterruptedException { 1469 int flushSize = 500; 1470 Configuration conf = HBaseConfiguration.create(); 1471 conf.set(HStore.MEMSTORE_CLASS_NAME, MyCompactingMemStoreWithCustomCompactor.class.getName()); 1472 conf.setDouble(CompactingMemStore.IN_MEMORY_FLUSH_THRESHOLD_FACTOR_KEY, 0.25); 1473 MyCompactingMemStoreWithCustomCompactor.RUNNER_COUNT.set(0); 1474 conf.set(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, String.valueOf(flushSize)); 1475 // Set the lower threshold to invoke the "MERGE" policy 1476 conf.set(MemStoreCompactionStrategy.COMPACTING_MEMSTORE_THRESHOLD_KEY, String.valueOf(0)); 1477 init(name.getMethodName(), conf, ColumnFamilyDescriptorBuilder.newBuilder(family) 1478 .setInMemoryCompaction(MemoryCompactionPolicy.BASIC).build()); 1479 byte[] value = Bytes.toBytes("thisisavarylargevalue"); 1480 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 1481 long ts = EnvironmentEdgeManager.currentTime(); 1482 long seqId = 100; 1483 // older data whihc shouldn't be "seen" by client 1484 store.add(createCell(qf1, ts, seqId, value), memStoreSizing); 1485 store.add(createCell(qf2, ts, seqId, value), memStoreSizing); 1486 store.add(createCell(qf3, ts, seqId, value), memStoreSizing); 1487 assertEquals(1, MyCompactingMemStoreWithCustomCompactor.RUNNER_COUNT.get()); 1488 StoreFlushContext storeFlushCtx = store.createFlushContext(id++, FlushLifeCycleTracker.DUMMY); 1489 storeFlushCtx.prepare(); 1490 // This shouldn't invoke another in-memory flush because the first compactor thread 1491 // hasn't accomplished the in-memory compaction. 1492 store.add(createCell(qf1, ts + 1, seqId + 1, value), memStoreSizing); 1493 store.add(createCell(qf1, ts + 1, seqId + 1, value), memStoreSizing); 1494 store.add(createCell(qf1, ts + 1, seqId + 1, value), memStoreSizing); 1495 assertEquals(1, MyCompactingMemStoreWithCustomCompactor.RUNNER_COUNT.get()); 1496 //okay. Let the compaction be completed 1497 MyMemStoreCompactor.START_COMPACTOR_LATCH.countDown(); 1498 CompactingMemStore mem = (CompactingMemStore) ((HStore)store).memstore; 1499 while (mem.isMemStoreFlushingInMemory()) { 1500 TimeUnit.SECONDS.sleep(1); 1501 } 1502 // This should invoke another in-memory flush. 1503 store.add(createCell(qf1, ts + 2, seqId + 2, value), memStoreSizing); 1504 store.add(createCell(qf1, ts + 2, seqId + 2, value), memStoreSizing); 1505 store.add(createCell(qf1, ts + 2, seqId + 2, value), memStoreSizing); 1506 assertEquals(2, MyCompactingMemStoreWithCustomCompactor.RUNNER_COUNT.get()); 1507 conf.set(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, 1508 String.valueOf(TableDescriptorBuilder.DEFAULT_MEMSTORE_FLUSH_SIZE)); 1509 storeFlushCtx.flushCache(Mockito.mock(MonitoredTask.class)); 1510 storeFlushCtx.commit(Mockito.mock(MonitoredTask.class)); 1511 } 1512 1513 @Test 1514 public void testAge() throws IOException { 1515 long currentTime = System.currentTimeMillis(); 1516 ManualEnvironmentEdge edge = new ManualEnvironmentEdge(); 1517 edge.setValue(currentTime); 1518 EnvironmentEdgeManager.injectEdge(edge); 1519 Configuration conf = TEST_UTIL.getConfiguration(); 1520 ColumnFamilyDescriptor hcd = ColumnFamilyDescriptorBuilder.of(family); 1521 initHRegion(name.getMethodName(), conf, 1522 TableDescriptorBuilder.newBuilder(TableName.valueOf(table)), hcd, null, false); 1523 HStore store = new HStore(region, hcd, conf) { 1524 1525 @Override 1526 protected StoreEngine<?, ?, ?, ?> createStoreEngine(HStore store, Configuration conf, 1527 CellComparator kvComparator) throws IOException { 1528 List<HStoreFile> storefiles = 1529 Arrays.asList(mockStoreFile(currentTime - 10), mockStoreFile(currentTime - 100), 1530 mockStoreFile(currentTime - 1000), mockStoreFile(currentTime - 10000)); 1531 StoreFileManager sfm = mock(StoreFileManager.class); 1532 when(sfm.getStorefiles()).thenReturn(storefiles); 1533 StoreEngine<?, ?, ?, ?> storeEngine = mock(StoreEngine.class); 1534 when(storeEngine.getStoreFileManager()).thenReturn(sfm); 1535 return storeEngine; 1536 } 1537 }; 1538 assertEquals(10L, store.getMinStoreFileAge().getAsLong()); 1539 assertEquals(10000L, store.getMaxStoreFileAge().getAsLong()); 1540 assertEquals((10 + 100 + 1000 + 10000) / 4.0, store.getAvgStoreFileAge().getAsDouble(), 1E-4); 1541 } 1542 1543 private HStoreFile mockStoreFile(long createdTime) { 1544 StoreFileInfo info = mock(StoreFileInfo.class); 1545 when(info.getCreatedTimestamp()).thenReturn(createdTime); 1546 HStoreFile sf = mock(HStoreFile.class); 1547 when(sf.getReader()).thenReturn(mock(StoreFileReader.class)); 1548 when(sf.isHFile()).thenReturn(true); 1549 when(sf.getFileInfo()).thenReturn(info); 1550 return sf; 1551 } 1552 1553 private MyStore initMyStore(String methodName, Configuration conf, MyStoreHook hook) 1554 throws IOException { 1555 return (MyStore) init(methodName, conf, 1556 TableDescriptorBuilder.newBuilder(TableName.valueOf(table)), 1557 ColumnFamilyDescriptorBuilder.newBuilder(family).setMaxVersions(5).build(), hook); 1558 } 1559 1560 private static class MyStore extends HStore { 1561 private final MyStoreHook hook; 1562 1563 MyStore(final HRegion region, final ColumnFamilyDescriptor family, final Configuration 1564 confParam, MyStoreHook hook, boolean switchToPread) throws IOException { 1565 super(region, family, confParam); 1566 this.hook = hook; 1567 } 1568 1569 @Override 1570 public List<KeyValueScanner> getScanners(List<HStoreFile> files, boolean cacheBlocks, 1571 boolean usePread, boolean isCompaction, ScanQueryMatcher matcher, byte[] startRow, 1572 boolean includeStartRow, byte[] stopRow, boolean includeStopRow, long readPt, 1573 boolean includeMemstoreScanner) throws IOException { 1574 hook.getScanners(this); 1575 return super.getScanners(files, cacheBlocks, usePread, isCompaction, matcher, startRow, true, 1576 stopRow, false, readPt, includeMemstoreScanner); 1577 } 1578 1579 @Override 1580 public long getSmallestReadPoint() { 1581 return hook.getSmallestReadPoint(this); 1582 } 1583 } 1584 1585 private abstract static class MyStoreHook { 1586 1587 void getScanners(MyStore store) throws IOException { 1588 } 1589 1590 long getSmallestReadPoint(HStore store) { 1591 return store.getHRegion().getSmallestReadPoint(); 1592 } 1593 } 1594 1595 @Test 1596 public void testSwitchingPreadtoStreamParallelyWithCompactionDischarger() throws Exception { 1597 Configuration conf = HBaseConfiguration.create(); 1598 conf.set("hbase.hstore.engine.class", DummyStoreEngine.class.getName()); 1599 conf.setLong(StoreScanner.STORESCANNER_PREAD_MAX_BYTES, 0); 1600 // Set the lower threshold to invoke the "MERGE" policy 1601 MyStore store = initMyStore(name.getMethodName(), conf, new MyStoreHook() {}); 1602 MemStoreSizing memStoreSizing = new NonThreadSafeMemStoreSizing(); 1603 long ts = System.currentTimeMillis(); 1604 long seqID = 1L; 1605 // Add some data to the region and do some flushes 1606 for (int i = 1; i < 10; i++) { 1607 store.add(createCell(Bytes.toBytes("row" + i), qf1, ts, seqID++, Bytes.toBytes("")), 1608 memStoreSizing); 1609 } 1610 // flush them 1611 flushStore(store, seqID); 1612 for (int i = 11; i < 20; i++) { 1613 store.add(createCell(Bytes.toBytes("row" + i), qf1, ts, seqID++, Bytes.toBytes("")), 1614 memStoreSizing); 1615 } 1616 // flush them 1617 flushStore(store, seqID); 1618 for (int i = 21; i < 30; i++) { 1619 store.add(createCell(Bytes.toBytes("row" + i), qf1, ts, seqID++, Bytes.toBytes("")), 1620 memStoreSizing); 1621 } 1622 // flush them 1623 flushStore(store, seqID); 1624 1625 assertEquals(3, store.getStorefilesCount()); 1626 Scan scan = new Scan(); 1627 scan.addFamily(family); 1628 Collection<HStoreFile> storefiles2 = store.getStorefiles(); 1629 ArrayList<HStoreFile> actualStorefiles = Lists.newArrayList(storefiles2); 1630 StoreScanner storeScanner = 1631 (StoreScanner) store.getScanner(scan, scan.getFamilyMap().get(family), Long.MAX_VALUE); 1632 // get the current heap 1633 KeyValueHeap heap = storeScanner.heap; 1634 // create more store files 1635 for (int i = 31; i < 40; i++) { 1636 store.add(createCell(Bytes.toBytes("row" + i), qf1, ts, seqID++, Bytes.toBytes("")), 1637 memStoreSizing); 1638 } 1639 // flush them 1640 flushStore(store, seqID); 1641 1642 for (int i = 41; i < 50; i++) { 1643 store.add(createCell(Bytes.toBytes("row" + i), qf1, ts, seqID++, Bytes.toBytes("")), 1644 memStoreSizing); 1645 } 1646 // flush them 1647 flushStore(store, seqID); 1648 storefiles2 = store.getStorefiles(); 1649 ArrayList<HStoreFile> actualStorefiles1 = Lists.newArrayList(storefiles2); 1650 actualStorefiles1.removeAll(actualStorefiles); 1651 // Do compaction 1652 MyThread thread = new MyThread(storeScanner); 1653 thread.start(); 1654 store.replaceStoreFiles(actualStorefiles, actualStorefiles1); 1655 thread.join(); 1656 KeyValueHeap heap2 = thread.getHeap(); 1657 assertFalse(heap.equals(heap2)); 1658 } 1659 1660 private static class MyThread extends Thread { 1661 private StoreScanner scanner; 1662 private KeyValueHeap heap; 1663 1664 public MyThread(StoreScanner scanner) { 1665 this.scanner = scanner; 1666 } 1667 1668 public KeyValueHeap getHeap() { 1669 return this.heap; 1670 } 1671 1672 @Override 1673 public void run() { 1674 scanner.trySwitchToStreamRead(); 1675 heap = scanner.heap; 1676 } 1677 } 1678 1679 private static class MyMemStoreCompactor extends MemStoreCompactor { 1680 private static final AtomicInteger RUNNER_COUNT = new AtomicInteger(0); 1681 private static final CountDownLatch START_COMPACTOR_LATCH = new CountDownLatch(1); 1682 public MyMemStoreCompactor(CompactingMemStore compactingMemStore, MemoryCompactionPolicy 1683 compactionPolicy) throws IllegalArgumentIOException { 1684 super(compactingMemStore, compactionPolicy); 1685 } 1686 1687 @Override 1688 public boolean start() throws IOException { 1689 boolean isFirst = RUNNER_COUNT.getAndIncrement() == 0; 1690 boolean rval = super.start(); 1691 if (isFirst) { 1692 try { 1693 START_COMPACTOR_LATCH.await(); 1694 } catch (InterruptedException ex) { 1695 throw new RuntimeException(ex); 1696 } 1697 } 1698 return rval; 1699 } 1700 } 1701 1702 public static class MyCompactingMemStoreWithCustomCompactor extends CompactingMemStore { 1703 private static final AtomicInteger RUNNER_COUNT = new AtomicInteger(0); 1704 public MyCompactingMemStoreWithCustomCompactor(Configuration conf, CellComparatorImpl c, 1705 HStore store, RegionServicesForStores regionServices, 1706 MemoryCompactionPolicy compactionPolicy) throws IOException { 1707 super(conf, c, store, regionServices, compactionPolicy); 1708 } 1709 1710 @Override 1711 protected MemStoreCompactor createMemStoreCompactor(MemoryCompactionPolicy compactionPolicy) 1712 throws IllegalArgumentIOException { 1713 return new MyMemStoreCompactor(this, compactionPolicy); 1714 } 1715 1716 @Override 1717 protected boolean shouldFlushInMemory() { 1718 boolean rval = super.shouldFlushInMemory(); 1719 if (rval) { 1720 RUNNER_COUNT.incrementAndGet(); 1721 if (LOG.isDebugEnabled()) { 1722 LOG.debug("runner count: " + RUNNER_COUNT.get()); 1723 } 1724 } 1725 return rval; 1726 } 1727 } 1728 1729 public static class MyCompactingMemStore extends CompactingMemStore { 1730 private static final AtomicBoolean START_TEST = new AtomicBoolean(false); 1731 private final CountDownLatch getScannerLatch = new CountDownLatch(1); 1732 private final CountDownLatch snapshotLatch = new CountDownLatch(1); 1733 public MyCompactingMemStore(Configuration conf, CellComparatorImpl c, 1734 HStore store, RegionServicesForStores regionServices, 1735 MemoryCompactionPolicy compactionPolicy) throws IOException { 1736 super(conf, c, store, regionServices, compactionPolicy); 1737 } 1738 1739 @Override 1740 protected List<KeyValueScanner> createList(int capacity) { 1741 if (START_TEST.get()) { 1742 try { 1743 getScannerLatch.countDown(); 1744 snapshotLatch.await(); 1745 } catch (InterruptedException e) { 1746 throw new RuntimeException(e); 1747 } 1748 } 1749 return new ArrayList<>(capacity); 1750 } 1751 @Override 1752 protected void pushActiveToPipeline(MutableSegment active) { 1753 if (START_TEST.get()) { 1754 try { 1755 getScannerLatch.await(); 1756 } catch (InterruptedException e) { 1757 throw new RuntimeException(e); 1758 } 1759 } 1760 1761 super.pushActiveToPipeline(active); 1762 if (START_TEST.get()) { 1763 snapshotLatch.countDown(); 1764 } 1765 } 1766 } 1767 1768 interface MyListHook { 1769 void hook(int currentSize); 1770 } 1771 1772 private static class MyList<T> implements List<T> { 1773 private final List<T> delegatee = new ArrayList<>(); 1774 private final MyListHook hookAtAdd; 1775 MyList(final MyListHook hookAtAdd) { 1776 this.hookAtAdd = hookAtAdd; 1777 } 1778 @Override 1779 public int size() {return delegatee.size();} 1780 1781 @Override 1782 public boolean isEmpty() {return delegatee.isEmpty();} 1783 1784 @Override 1785 public boolean contains(Object o) {return delegatee.contains(o);} 1786 1787 @Override 1788 public Iterator<T> iterator() {return delegatee.iterator();} 1789 1790 @Override 1791 public Object[] toArray() {return delegatee.toArray();} 1792 1793 @Override 1794 public <R> R[] toArray(R[] a) {return delegatee.toArray(a);} 1795 1796 @Override 1797 public boolean add(T e) { 1798 hookAtAdd.hook(size()); 1799 return delegatee.add(e); 1800 } 1801 1802 @Override 1803 public boolean remove(Object o) {return delegatee.remove(o);} 1804 1805 @Override 1806 public boolean containsAll(Collection<?> c) {return delegatee.containsAll(c);} 1807 1808 @Override 1809 public boolean addAll(Collection<? extends T> c) {return delegatee.addAll(c);} 1810 1811 @Override 1812 public boolean addAll(int index, Collection<? extends T> c) {return delegatee.addAll(index, c);} 1813 1814 @Override 1815 public boolean removeAll(Collection<?> c) {return delegatee.removeAll(c);} 1816 1817 @Override 1818 public boolean retainAll(Collection<?> c) {return delegatee.retainAll(c);} 1819 1820 @Override 1821 public void clear() {delegatee.clear();} 1822 1823 @Override 1824 public T get(int index) {return delegatee.get(index);} 1825 1826 @Override 1827 public T set(int index, T element) {return delegatee.set(index, element);} 1828 1829 @Override 1830 public void add(int index, T element) {delegatee.add(index, element);} 1831 1832 @Override 1833 public T remove(int index) {return delegatee.remove(index);} 1834 1835 @Override 1836 public int indexOf(Object o) {return delegatee.indexOf(o);} 1837 1838 @Override 1839 public int lastIndexOf(Object o) {return delegatee.lastIndexOf(o);} 1840 1841 @Override 1842 public ListIterator<T> listIterator() {return delegatee.listIterator();} 1843 1844 @Override 1845 public ListIterator<T> listIterator(int index) {return delegatee.listIterator(index);} 1846 1847 @Override 1848 public List<T> subList(int fromIndex, int toIndex) {return delegatee.subList(fromIndex, toIndex);} 1849 } 1850}