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.assertNull; 022import static org.junit.Assert.assertTrue; 023 024import java.io.IOException; 025import java.lang.management.ManagementFactory; 026import java.util.ArrayList; 027import java.util.List; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.hbase.Cell; 030import org.apache.hadoop.hbase.CellComparator; 031import org.apache.hadoop.hbase.CellUtil; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.HBaseConfiguration; 034import org.apache.hadoop.hbase.HBaseTestingUtility; 035import org.apache.hadoop.hbase.HColumnDescriptor; 036import org.apache.hadoop.hbase.HConstants; 037import org.apache.hadoop.hbase.HRegionInfo; 038import org.apache.hadoop.hbase.HTableDescriptor; 039import org.apache.hadoop.hbase.KeepDeletedCells; 040import org.apache.hadoop.hbase.KeyValue; 041import org.apache.hadoop.hbase.KeyValueTestUtil; 042import org.apache.hadoop.hbase.MemoryCompactionPolicy; 043import org.apache.hadoop.hbase.TableName; 044import org.apache.hadoop.hbase.client.Scan; 045import org.apache.hadoop.hbase.exceptions.IllegalArgumentIOException; 046import org.apache.hadoop.hbase.io.util.MemorySizeUtil; 047import org.apache.hadoop.hbase.testclassification.MediumTests; 048import org.apache.hadoop.hbase.testclassification.RegionServerTests; 049import org.apache.hadoop.hbase.util.Bytes; 050import org.apache.hadoop.hbase.util.EnvironmentEdge; 051import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 052import org.apache.hadoop.hbase.util.Threads; 053import org.apache.hadoop.hbase.wal.WAL; 054import org.junit.After; 055import org.junit.Before; 056import org.junit.ClassRule; 057import org.junit.Test; 058import org.junit.experimental.categories.Category; 059import org.slf4j.Logger; 060import org.slf4j.LoggerFactory; 061 062/** 063 * compacted memstore test case 064 */ 065@Category({RegionServerTests.class, MediumTests.class}) 066public class TestCompactingMemStore extends TestDefaultMemStore { 067 068 @ClassRule 069 public static final HBaseClassTestRule CLASS_RULE = 070 HBaseClassTestRule.forClass(TestCompactingMemStore.class); 071 072 private static final Logger LOG = LoggerFactory.getLogger(TestCompactingMemStore.class); 073 protected static ChunkCreator chunkCreator; 074 protected HRegion region; 075 protected RegionServicesForStores regionServicesForStores; 076 protected HStore store; 077 078 ////////////////////////////////////////////////////////////////////////////// 079 // Helpers 080 ////////////////////////////////////////////////////////////////////////////// 081 protected static byte[] makeQualifier(final int i1, final int i2) { 082 return Bytes.toBytes(Integer.toString(i1) + ";" + 083 Integer.toString(i2)); 084 } 085 086 @After 087 public void tearDown() throws Exception { 088 chunkCreator.clearChunksInPool(); 089 } 090 091 @Override 092 @Before 093 public void setUp() throws Exception { 094 compactingSetUp(); 095 this.memstore = new MyCompactingMemStore(HBaseConfiguration.create(), CellComparator.getInstance(), 096 store, regionServicesForStores, MemoryCompactionPolicy.EAGER); 097 ((CompactingMemStore)memstore).setIndexType(CompactingMemStore.IndexType.ARRAY_MAP); 098 } 099 100 protected void compactingSetUp() throws Exception { 101 super.internalSetUp(); 102 Configuration conf = new Configuration(); 103 conf.setBoolean(MemStoreLAB.USEMSLAB_KEY, true); 104 conf.setFloat(MemStoreLAB.CHUNK_POOL_MAXSIZE_KEY, 0.2f); 105 conf.setInt(HRegion.MEMSTORE_PERIODIC_FLUSH_INTERVAL, 1000); 106 HBaseTestingUtility hbaseUtility = HBaseTestingUtility.createLocalHTU(conf); 107 HColumnDescriptor hcd = new HColumnDescriptor(FAMILY); 108 HTableDescriptor htd = new HTableDescriptor(TableName.valueOf("foobar")); 109 htd.addFamily(hcd); 110 HRegionInfo info = 111 new HRegionInfo(TableName.valueOf("foobar"), null, null, false); 112 WAL wal = hbaseUtility.createWal(conf, hbaseUtility.getDataTestDir(), info); 113 this.region = HRegion.createHRegion(info, hbaseUtility.getDataTestDir(), conf, htd, wal, true); 114 //this.region = hbaseUtility.createTestRegion("foobar", hcd); 115 this.regionServicesForStores = region.getRegionServicesForStores(); 116 this.store = new HStore(region, hcd, conf); 117 118 long globalMemStoreLimit = (long) (ManagementFactory.getMemoryMXBean().getHeapMemoryUsage() 119 .getMax() * MemorySizeUtil.getGlobalMemStoreHeapPercent(conf, false)); 120 chunkCreator = ChunkCreator.initialize(MemStoreLABImpl.CHUNK_SIZE_DEFAULT, false, 121 globalMemStoreLimit, 0.4f, MemStoreLAB.POOL_INITIAL_SIZE_DEFAULT, 122 null); 123 assertTrue(chunkCreator != null); 124 } 125 126 /** 127 * A simple test which verifies the 3 possible states when scanning across snapshot. 128 * 129 * @throws IOException 130 * @throws CloneNotSupportedException 131 */ 132 @Override 133 @Test 134 public void testScanAcrossSnapshot2() throws IOException, CloneNotSupportedException { 135 // we are going to the scanning across snapshot with two kvs 136 // kv1 should always be returned before kv2 137 final byte[] one = Bytes.toBytes(1); 138 final byte[] two = Bytes.toBytes(2); 139 final byte[] f = Bytes.toBytes("f"); 140 final byte[] q = Bytes.toBytes("q"); 141 final byte[] v = Bytes.toBytes(3); 142 143 final KeyValue kv1 = new KeyValue(one, f, q, 10, v); 144 final KeyValue kv2 = new KeyValue(two, f, q, 10, v); 145 146 // use case 1: both kvs in kvset 147 this.memstore.add(kv1.clone(), null); 148 this.memstore.add(kv2.clone(), null); 149 verifyScanAcrossSnapshot2(kv1, kv2); 150 151 // use case 2: both kvs in snapshot 152 this.memstore.snapshot(); 153 verifyScanAcrossSnapshot2(kv1, kv2); 154 155 // use case 3: first in snapshot second in kvset 156 this.memstore = new CompactingMemStore(HBaseConfiguration.create(), 157 CellComparator.getInstance(), store, regionServicesForStores, 158 MemoryCompactionPolicy.EAGER); 159 this.memstore.add(kv1.clone(), null); 160 // As compaction is starting in the background the repetition 161 // of the k1 might be removed BUT the scanners created earlier 162 // should look on the OLD MutableCellSetSegment, so this should be OK... 163 this.memstore.snapshot(); 164 this.memstore.add(kv2.clone(), null); 165 verifyScanAcrossSnapshot2(kv1,kv2); 166 } 167 168 /** 169 * Test memstore snapshots 170 * @throws IOException 171 */ 172 @Override 173 @Test 174 public void testSnapshotting() throws IOException { 175 final int snapshotCount = 5; 176 // Add some rows, run a snapshot. Do it a few times. 177 for (int i = 0; i < snapshotCount; i++) { 178 addRows(this.memstore); 179 runSnapshot(this.memstore, true); 180 assertEquals("History not being cleared", 0, this.memstore.getSnapshot().getCellsCount()); 181 } 182 } 183 184 185 ////////////////////////////////////////////////////////////////////////////// 186 // Get tests 187 ////////////////////////////////////////////////////////////////////////////// 188 189 /** Test getNextRow from memstore 190 * @throws InterruptedException 191 */ 192 @Override 193 @Test 194 public void testGetNextRow() throws Exception { 195 addRows(this.memstore); 196 // Add more versions to make it a little more interesting. 197 Thread.sleep(1); 198 addRows(this.memstore); 199 Cell closestToEmpty = ((CompactingMemStore)this.memstore).getNextRow(KeyValue.LOWESTKEY); 200 assertTrue(CellComparator.getInstance().compareRows(closestToEmpty, 201 new KeyValue(Bytes.toBytes(0), System.currentTimeMillis())) == 0); 202 for (int i = 0; i < ROW_COUNT; i++) { 203 Cell nr = ((CompactingMemStore)this.memstore).getNextRow(new KeyValue(Bytes.toBytes(i), 204 System.currentTimeMillis())); 205 if (i + 1 == ROW_COUNT) { 206 assertNull(nr); 207 } else { 208 assertTrue(CellComparator.getInstance().compareRows(nr, 209 new KeyValue(Bytes.toBytes(i + 1), System.currentTimeMillis())) == 0); 210 } 211 } 212 //starting from each row, validate results should contain the starting row 213 Configuration conf = HBaseConfiguration.create(); 214 for (int startRowId = 0; startRowId < ROW_COUNT; startRowId++) { 215 ScanInfo scanInfo = new ScanInfo(conf, FAMILY, 0, 1, Integer.MAX_VALUE, 216 KeepDeletedCells.FALSE, HConstants.DEFAULT_BLOCKSIZE, 0, this.memstore.getComparator(), false); 217 try (InternalScanner scanner = 218 new StoreScanner(new Scan().withStartRow(Bytes.toBytes(startRowId)), scanInfo, null, 219 memstore.getScanners(0))) { 220 List<Cell> results = new ArrayList<>(); 221 for (int i = 0; scanner.next(results); i++) { 222 int rowId = startRowId + i; 223 Cell left = results.get(0); 224 byte[] row1 = Bytes.toBytes(rowId); 225 assertTrue("Row name", 226 CellComparator.getInstance().compareRows(left, row1, 0, row1.length) == 0); 227 assertEquals("Count of columns", QUALIFIER_COUNT, results.size()); 228 List<Cell> row = new ArrayList<>(); 229 for (Cell kv : results) { 230 row.add(kv); 231 } 232 isExpectedRowWithoutTimestamps(rowId, row); 233 // Clear out set. Otherwise row results accumulate. 234 results.clear(); 235 } 236 } 237 } 238 } 239 240 @Override 241 @Test 242 public void testGet_memstoreAndSnapShot() throws IOException { 243 byte[] row = Bytes.toBytes("testrow"); 244 byte[] fam = Bytes.toBytes("testfamily"); 245 byte[] qf1 = Bytes.toBytes("testqualifier1"); 246 byte[] qf2 = Bytes.toBytes("testqualifier2"); 247 byte[] qf3 = Bytes.toBytes("testqualifier3"); 248 byte[] qf4 = Bytes.toBytes("testqualifier4"); 249 byte[] qf5 = Bytes.toBytes("testqualifier5"); 250 byte[] val = Bytes.toBytes("testval"); 251 252 //Setting up memstore 253 memstore.add(new KeyValue(row, fam, qf1, val), null); 254 memstore.add(new KeyValue(row, fam, qf2, val), null); 255 memstore.add(new KeyValue(row, fam, qf3, val), null); 256 //Pushing to pipeline 257 ((CompactingMemStore)memstore).flushInMemory(); 258 assertEquals(0, memstore.getSnapshot().getCellsCount()); 259 //Creating a snapshot 260 memstore.snapshot(); 261 assertEquals(3, memstore.getSnapshot().getCellsCount()); 262 //Adding value to "new" memstore 263 assertEquals(0, memstore.getActive().getCellsCount()); 264 memstore.add(new KeyValue(row, fam, qf4, val), null); 265 memstore.add(new KeyValue(row, fam, qf5, val), null); 266 assertEquals(2, memstore.getActive().getCellsCount()); 267 } 268 269 //////////////////////////////////// 270 // Test for periodic memstore flushes 271 // based on time of oldest edit 272 //////////////////////////////////// 273 274 /** 275 * Add keyvalues with a fixed memstoreTs, and checks that memstore size is decreased 276 * as older keyvalues are deleted from the memstore. 277 * 278 * @throws Exception 279 */ 280 @Override 281 @Test 282 public void testUpsertMemstoreSize() throws Exception { 283 MemStoreSize oldSize = memstore.size(); 284 285 List<Cell> l = new ArrayList<>(); 286 KeyValue kv1 = KeyValueTestUtil.create("r", "f", "q", 100, "v"); 287 KeyValue kv2 = KeyValueTestUtil.create("r", "f", "q", 101, "v"); 288 KeyValue kv3 = KeyValueTestUtil.create("r", "f", "q", 102, "v"); 289 290 kv1.setSequenceId(1); 291 kv2.setSequenceId(1); 292 kv3.setSequenceId(1); 293 l.add(kv1); 294 l.add(kv2); 295 l.add(kv3); 296 297 this.memstore.upsert(l, 2, null);// readpoint is 2 298 MemStoreSize newSize = this.memstore.size(); 299 assert (newSize.getDataSize() > oldSize.getDataSize()); 300 //The kv1 should be removed. 301 assert (memstore.getActive().getCellsCount() == 2); 302 303 KeyValue kv4 = KeyValueTestUtil.create("r", "f", "q", 104, "v"); 304 kv4.setSequenceId(1); 305 l.clear(); 306 l.add(kv4); 307 this.memstore.upsert(l, 3, null); 308 assertEquals(newSize, this.memstore.size()); 309 //The kv2 should be removed. 310 assert (memstore.getActive().getCellsCount() == 2); 311 //this.memstore = null; 312 } 313 314 /** 315 * Tests that the timeOfOldestEdit is updated correctly for the 316 * various edit operations in memstore. 317 * @throws Exception 318 */ 319 @Override 320 @Test 321 public void testUpdateToTimeOfOldestEdit() throws Exception { 322 try { 323 EnvironmentEdgeForMemstoreTest edge = new EnvironmentEdgeForMemstoreTest(); 324 EnvironmentEdgeManager.injectEdge(edge); 325 long t = memstore.timeOfOldestEdit(); 326 assertEquals(Long.MAX_VALUE, t); 327 328 // test the case that the timeOfOldestEdit is updated after a KV add 329 memstore.add(KeyValueTestUtil.create("r", "f", "q", 100, "v"), null); 330 t = memstore.timeOfOldestEdit(); 331 assertTrue(t == 1234); 332 // The method will also assert 333 // the value is reset to Long.MAX_VALUE 334 t = runSnapshot(memstore, true); 335 336 // test the case that the timeOfOldestEdit is updated after a KV delete 337 memstore.add(KeyValueTestUtil.create("r", "f", "q", 100, KeyValue.Type.Delete, "v"), null); 338 t = memstore.timeOfOldestEdit(); 339 assertTrue(t == 1234); 340 t = runSnapshot(memstore, true); 341 342 // test the case that the timeOfOldestEdit is updated after a KV upsert 343 List<Cell> l = new ArrayList<>(); 344 KeyValue kv1 = KeyValueTestUtil.create("r", "f", "q", 100, "v"); 345 kv1.setSequenceId(100); 346 l.add(kv1); 347 memstore.upsert(l, 1000, null); 348 t = memstore.timeOfOldestEdit(); 349 assertTrue(t == 1234); 350 } finally { 351 EnvironmentEdgeManager.reset(); 352 } 353 } 354 355 private long runSnapshot(final AbstractMemStore hmc, boolean useForce) 356 throws IOException { 357 // Save off old state. 358 long oldHistorySize = hmc.getSnapshot().getDataSize(); 359 long prevTimeStamp = hmc.timeOfOldestEdit(); 360 361 hmc.snapshot(); 362 MemStoreSnapshot snapshot = hmc.snapshot(); 363 if (useForce) { 364 // Make some assertions about what just happened. 365 assertTrue("History size has not increased", oldHistorySize < snapshot.getDataSize()); 366 long t = hmc.timeOfOldestEdit(); 367 assertTrue("Time of oldest edit is not Long.MAX_VALUE", t == Long.MAX_VALUE); 368 hmc.clearSnapshot(snapshot.getId()); 369 } else { 370 long t = hmc.timeOfOldestEdit(); 371 assertTrue("Time of oldest edit didn't remain the same", t == prevTimeStamp); 372 } 373 return prevTimeStamp; 374 } 375 376 private void isExpectedRowWithoutTimestamps(final int rowIndex, 377 List<Cell> kvs) { 378 int i = 0; 379 for (Cell kv : kvs) { 380 byte[] expectedColname = makeQualifier(rowIndex, i++); 381 assertTrue("Column name", CellUtil.matchingQualifier(kv, expectedColname)); 382 // Value is column name as bytes. Usually result is 383 // 100 bytes in size at least. This is the default size 384 // for BytesWriteable. For comparison, convert bytes to 385 // String and trim to remove trailing null bytes. 386 assertTrue("Content", CellUtil.matchingValue(kv, expectedColname)); 387 } 388 } 389 390 @Test 391 public void testPuttingBackChunksAfterFlushing() throws IOException { 392 byte[] row = Bytes.toBytes("testrow"); 393 byte[] fam = Bytes.toBytes("testfamily"); 394 byte[] qf1 = Bytes.toBytes("testqualifier1"); 395 byte[] qf2 = Bytes.toBytes("testqualifier2"); 396 byte[] qf3 = Bytes.toBytes("testqualifier3"); 397 byte[] qf4 = Bytes.toBytes("testqualifier4"); 398 byte[] qf5 = Bytes.toBytes("testqualifier5"); 399 byte[] val = Bytes.toBytes("testval"); 400 401 // Setting up memstore 402 memstore.add(new KeyValue(row, fam, qf1, val), null); 403 memstore.add(new KeyValue(row, fam, qf2, val), null); 404 memstore.add(new KeyValue(row, fam, qf3, val), null); 405 406 // Creating a snapshot 407 MemStoreSnapshot snapshot = memstore.snapshot(); 408 assertEquals(3, memstore.getSnapshot().getCellsCount()); 409 410 // Adding value to "new" memstore 411 assertEquals(0, memstore.getActive().getCellsCount()); 412 memstore.add(new KeyValue(row, fam, qf4, val), null); 413 memstore.add(new KeyValue(row, fam, qf5, val), null); 414 assertEquals(2, memstore.getActive().getCellsCount()); 415 // close the scanners 416 for(KeyValueScanner scanner : snapshot.getScanners()) { 417 scanner.close(); 418 } 419 memstore.clearSnapshot(snapshot.getId()); 420 421 int chunkCount = chunkCreator.getPoolSize(); 422 assertTrue(chunkCount > 0); 423 424 } 425 426 @Test 427 public void testPuttingBackChunksWithOpeningScanner() 428 throws IOException { 429 byte[] row = Bytes.toBytes("testrow"); 430 byte[] fam = Bytes.toBytes("testfamily"); 431 byte[] qf1 = Bytes.toBytes("testqualifier1"); 432 byte[] qf2 = Bytes.toBytes("testqualifier2"); 433 byte[] qf3 = Bytes.toBytes("testqualifier3"); 434 byte[] qf4 = Bytes.toBytes("testqualifier4"); 435 byte[] qf5 = Bytes.toBytes("testqualifier5"); 436 byte[] qf6 = Bytes.toBytes("testqualifier6"); 437 byte[] qf7 = Bytes.toBytes("testqualifier7"); 438 byte[] val = Bytes.toBytes("testval"); 439 440 // Setting up memstore 441 memstore.add(new KeyValue(row, fam, qf1, val), null); 442 memstore.add(new KeyValue(row, fam, qf2, val), null); 443 memstore.add(new KeyValue(row, fam, qf3, val), null); 444 445 // Creating a snapshot 446 MemStoreSnapshot snapshot = memstore.snapshot(); 447 assertEquals(3, memstore.getSnapshot().getCellsCount()); 448 449 // Adding value to "new" memstore 450 assertEquals(0, memstore.getActive().getCellsCount()); 451 memstore.add(new KeyValue(row, fam, qf4, val), null); 452 memstore.add(new KeyValue(row, fam, qf5, val), null); 453 assertEquals(2, memstore.getActive().getCellsCount()); 454 455 // opening scanner before clear the snapshot 456 List<KeyValueScanner> scanners = memstore.getScanners(0); 457 // Shouldn't putting back the chunks to pool,since some scanners are opening 458 // based on their data 459 // close the scanners 460 for(KeyValueScanner scanner : snapshot.getScanners()) { 461 scanner.close(); 462 } 463 memstore.clearSnapshot(snapshot.getId()); 464 465 assertTrue(chunkCreator.getPoolSize() == 0); 466 467 // Chunks will be put back to pool after close scanners; 468 for (KeyValueScanner scanner : scanners) { 469 scanner.close(); 470 } 471 assertTrue(chunkCreator.getPoolSize() > 0); 472 473 // clear chunks 474 chunkCreator.clearChunksInPool(); 475 476 // Creating another snapshot 477 478 snapshot = memstore.snapshot(); 479 // Adding more value 480 memstore.add(new KeyValue(row, fam, qf6, val), null); 481 memstore.add(new KeyValue(row, fam, qf7, val), null); 482 // opening scanners 483 scanners = memstore.getScanners(0); 484 // close scanners before clear the snapshot 485 for (KeyValueScanner scanner : scanners) { 486 scanner.close(); 487 } 488 // Since no opening scanner, the chunks of snapshot should be put back to 489 // pool 490 // close the scanners 491 for(KeyValueScanner scanner : snapshot.getScanners()) { 492 scanner.close(); 493 } 494 memstore.clearSnapshot(snapshot.getId()); 495 assertTrue(chunkCreator.getPoolSize() > 0); 496 } 497 498 @Test 499 public void testPuttingBackChunksWithOpeningPipelineScanner() 500 throws IOException { 501 502 // set memstore to do data compaction and not to use the speculative scan 503 MemoryCompactionPolicy compactionType = MemoryCompactionPolicy.EAGER; 504 memstore.getConfiguration().set(CompactingMemStore.COMPACTING_MEMSTORE_TYPE_KEY, 505 String.valueOf(compactionType)); 506 ((MyCompactingMemStore)memstore).initiateType(compactionType, memstore.getConfiguration()); 507 508 byte[] row = Bytes.toBytes("testrow"); 509 byte[] fam = Bytes.toBytes("testfamily"); 510 byte[] qf1 = Bytes.toBytes("testqualifier1"); 511 byte[] qf2 = Bytes.toBytes("testqualifier2"); 512 byte[] qf3 = Bytes.toBytes("testqualifier3"); 513 byte[] val = Bytes.toBytes("testval"); 514 515 // Setting up memstore 516 memstore.add(new KeyValue(row, fam, qf1, 1, val), null); 517 memstore.add(new KeyValue(row, fam, qf2, 1, val), null); 518 memstore.add(new KeyValue(row, fam, qf3, 1, val), null); 519 520 // Creating a pipeline 521 ((MyCompactingMemStore)memstore).disableCompaction(); 522 ((CompactingMemStore)memstore).flushInMemory(); 523 524 // Adding value to "new" memstore 525 assertEquals(0, memstore.getActive().getCellsCount()); 526 memstore.add(new KeyValue(row, fam, qf1, 2, val), null); 527 memstore.add(new KeyValue(row, fam, qf2, 2, val), null); 528 assertEquals(2, memstore.getActive().getCellsCount()); 529 530 // pipeline bucket 2 531 ((CompactingMemStore)memstore).flushInMemory(); 532 // opening scanner before force flushing 533 List<KeyValueScanner> scanners = memstore.getScanners(0); 534 // Shouldn't putting back the chunks to pool,since some scanners are opening 535 // based on their data 536 ((MyCompactingMemStore)memstore).enableCompaction(); 537 // trigger compaction 538 ((CompactingMemStore)memstore).flushInMemory(); 539 540 // Adding value to "new" memstore 541 assertEquals(0, memstore.getActive().getCellsCount()); 542 memstore.add(new KeyValue(row, fam, qf3, 3, val), null); 543 memstore.add(new KeyValue(row, fam, qf2, 3, val), null); 544 memstore.add(new KeyValue(row, fam, qf1, 3, val), null); 545 assertEquals(3, memstore.getActive().getCellsCount()); 546 547 assertTrue(chunkCreator.getPoolSize() == 0); 548 549 // Chunks will be put back to pool after close scanners; 550 for (KeyValueScanner scanner : scanners) { 551 scanner.close(); 552 } 553 assertTrue(chunkCreator.getPoolSize() > 0); 554 555 // clear chunks 556 chunkCreator.clearChunksInPool(); 557 558 // Creating another snapshot 559 560 MemStoreSnapshot snapshot = memstore.snapshot(); 561 // close the scanners 562 for(KeyValueScanner scanner : snapshot.getScanners()) { 563 scanner.close(); 564 } 565 memstore.clearSnapshot(snapshot.getId()); 566 567 snapshot = memstore.snapshot(); 568 // Adding more value 569 memstore.add(new KeyValue(row, fam, qf2, 4, val), null); 570 memstore.add(new KeyValue(row, fam, qf3, 4, val), null); 571 // opening scanners 572 scanners = memstore.getScanners(0); 573 // close scanners before clear the snapshot 574 for (KeyValueScanner scanner : scanners) { 575 scanner.close(); 576 } 577 // Since no opening scanner, the chunks of snapshot should be put back to 578 // pool 579 // close the scanners 580 for(KeyValueScanner scanner : snapshot.getScanners()) { 581 scanner.close(); 582 } 583 memstore.clearSnapshot(snapshot.getId()); 584 assertTrue(chunkCreator.getPoolSize() > 0); 585 } 586 587 ////////////////////////////////////////////////////////////////////////////// 588 // Compaction tests 589 ////////////////////////////////////////////////////////////////////////////// 590 @Test 591 public void testCompaction1Bucket() throws IOException { 592 593 // set memstore to do basic structure flattening, the "eager" option is tested in 594 // TestCompactingToCellFlatMapMemStore 595 MemoryCompactionPolicy compactionType = MemoryCompactionPolicy.BASIC; 596 memstore.getConfiguration() 597 .set(CompactingMemStore.COMPACTING_MEMSTORE_TYPE_KEY, String.valueOf(compactionType)); 598 ((MyCompactingMemStore)memstore).initiateType(compactionType, memstore.getConfiguration()); 599 600 String[] keys1 = { "A", "A", "B", "C" }; //A1, A2, B3, C4 601 602 // test 1 bucket 603 int totalCellsLen = addRowsByKeys(memstore, keys1); 604 int oneCellOnCSLMHeapSize = 120; 605 int oneCellOnCAHeapSize = 88; 606 long totalHeapSize = MutableSegment.DEEP_OVERHEAD + 4 * oneCellOnCSLMHeapSize; 607 assertEquals(totalCellsLen, regionServicesForStores.getMemStoreSize()); 608 assertEquals(totalHeapSize, ((CompactingMemStore)memstore).heapSize()); 609 610 ((CompactingMemStore)memstore).flushInMemory(); // push keys to pipeline and compact 611 assertEquals(0, memstore.getSnapshot().getCellsCount()); 612 // There is no compaction, as the compacting memstore type is basic. 613 // totalCellsLen remains the same 614 totalHeapSize = MutableSegment.DEEP_OVERHEAD + CellArrayImmutableSegment.DEEP_OVERHEAD_CAM 615 + 4 * oneCellOnCAHeapSize; 616 assertEquals(totalCellsLen, regionServicesForStores.getMemStoreSize()); 617 assertEquals(totalHeapSize, ((CompactingMemStore)memstore).heapSize()); 618 619 MemStoreSize mss = memstore.getFlushableSize(); 620 MemStoreSnapshot snapshot = memstore.snapshot(); // push keys to snapshot 621 // simulate flusher 622 region.decrMemStoreSize(mss); 623 ImmutableSegment s = memstore.getSnapshot(); 624 assertEquals(4, s.getCellsCount()); 625 assertEquals(0, regionServicesForStores.getMemStoreSize()); 626 627 memstore.clearSnapshot(snapshot.getId()); 628 } 629 630 @Test 631 public void testCompaction2Buckets() throws IOException { 632 633 // set memstore to do basic structure flattening, the "eager" option is tested in 634 // TestCompactingToCellFlatMapMemStore 635 MemoryCompactionPolicy compactionType = MemoryCompactionPolicy.BASIC; 636 memstore.getConfiguration().set(CompactingMemStore.COMPACTING_MEMSTORE_TYPE_KEY, 637 String.valueOf(compactionType)); 638 memstore.getConfiguration().set(MemStoreCompactionStrategy.COMPACTING_MEMSTORE_THRESHOLD_KEY, 639 String.valueOf(1)); 640 ((MyCompactingMemStore)memstore).initiateType(compactionType, memstore.getConfiguration()); 641 String[] keys1 = { "A", "A", "B", "C" }; 642 String[] keys2 = { "A", "B", "D" }; 643 644 int totalCellsLen1 = addRowsByKeys(memstore, keys1); 645 int oneCellOnCSLMHeapSize = 120; 646 int oneCellOnCAHeapSize = 88; 647 long totalHeapSize = MutableSegment.DEEP_OVERHEAD + 4 * oneCellOnCSLMHeapSize; 648 649 assertEquals(totalCellsLen1, regionServicesForStores.getMemStoreSize()); 650 assertEquals(totalHeapSize, ((CompactingMemStore)memstore).heapSize()); 651 652 ((CompactingMemStore)memstore).flushInMemory(); // push keys to pipeline and compact 653 int counter = 0; 654 for ( Segment s : memstore.getSegments()) { 655 counter += s.getCellsCount(); 656 } 657 assertEquals(4, counter); 658 assertEquals(0, memstore.getSnapshot().getCellsCount()); 659 // There is no compaction, as the compacting memstore type is basic. 660 // totalCellsLen remains the same 661 assertEquals(totalCellsLen1, regionServicesForStores.getMemStoreSize()); 662 totalHeapSize = MutableSegment.DEEP_OVERHEAD + CellArrayImmutableSegment.DEEP_OVERHEAD_CAM 663 + 4 * oneCellOnCAHeapSize; 664 assertEquals(totalHeapSize, ((CompactingMemStore)memstore).heapSize()); 665 666 int totalCellsLen2 = addRowsByKeys(memstore, keys2); 667 totalHeapSize += 3 * oneCellOnCSLMHeapSize; 668 assertEquals(totalCellsLen1 + totalCellsLen2, regionServicesForStores.getMemStoreSize()); 669 assertEquals(totalHeapSize, ((CompactingMemStore) memstore).heapSize()); 670 671 MemStoreSize mss = memstore.getFlushableSize(); 672 ((CompactingMemStore)memstore).flushInMemory(); // push keys to pipeline and compact 673 assertEquals(0, memstore.getSnapshot().getCellsCount()); 674 assertEquals(totalCellsLen1 + totalCellsLen2, regionServicesForStores.getMemStoreSize()); 675 totalHeapSize = MutableSegment.DEEP_OVERHEAD + CellArrayImmutableSegment.DEEP_OVERHEAD_CAM 676 + 7 * oneCellOnCAHeapSize; 677 assertEquals(totalHeapSize, ((CompactingMemStore)memstore).heapSize()); 678 679 mss = memstore.getFlushableSize(); 680 MemStoreSnapshot snapshot = memstore.snapshot(); // push keys to snapshot 681 // simulate flusher 682 region.decrMemStoreSize(mss.getDataSize(), mss.getHeapSize(), mss.getOffHeapSize(), 683 mss.getCellsCount()); 684 ImmutableSegment s = memstore.getSnapshot(); 685 assertEquals(7, s.getCellsCount()); 686 assertEquals(0, regionServicesForStores.getMemStoreSize()); 687 688 memstore.clearSnapshot(snapshot.getId()); 689 } 690 691 @Test 692 public void testCompaction3Buckets() throws IOException { 693 694 // set memstore to do data compaction and not to use the speculative scan 695 MemoryCompactionPolicy compactionType = MemoryCompactionPolicy.EAGER; 696 memstore.getConfiguration().set(CompactingMemStore.COMPACTING_MEMSTORE_TYPE_KEY, 697 String.valueOf(compactionType)); 698 ((MyCompactingMemStore)memstore).initiateType(compactionType, memstore.getConfiguration()); 699 String[] keys1 = { "A", "A", "B", "C" }; 700 String[] keys2 = { "A", "B", "D" }; 701 String[] keys3 = { "D", "B", "B" }; 702 703 int totalCellsLen1 = addRowsByKeys(memstore, keys1);// Adding 4 cells. 704 int oneCellOnCSLMHeapSize = 120; 705 int oneCellOnCAHeapSize = 88; 706 assertEquals(totalCellsLen1, region.getMemStoreDataSize()); 707 long totalHeapSize = MutableSegment.DEEP_OVERHEAD + 4 * oneCellOnCSLMHeapSize; 708 assertEquals(totalHeapSize, ((CompactingMemStore)memstore).heapSize()); 709 ((CompactingMemStore)memstore).flushInMemory(); // push keys to pipeline and compact 710 711 assertEquals(0, memstore.getSnapshot().getCellsCount()); 712 // One cell is duplicated and the compaction will remove it. All cells of same time so adjusting 713 // totalCellsLen 714 totalCellsLen1 = (totalCellsLen1 * 3) / 4; 715 assertEquals(totalCellsLen1, regionServicesForStores.getMemStoreSize()); 716 // In memory flush to make a CellArrayMap instead of CSLM. See the overhead diff. 717 totalHeapSize = MutableSegment.DEEP_OVERHEAD + CellArrayImmutableSegment.DEEP_OVERHEAD_CAM 718 + 3 * oneCellOnCAHeapSize; 719 assertEquals(totalHeapSize, ((CompactingMemStore)memstore).heapSize()); 720 721 int totalCellsLen2 = addRowsByKeys(memstore, keys2);// Adding 3 more cells. 722 long totalHeapSize2 = totalHeapSize + 3 * oneCellOnCSLMHeapSize; 723 724 assertEquals(totalCellsLen1 + totalCellsLen2, regionServicesForStores.getMemStoreSize()); 725 assertEquals(totalHeapSize2, ((CompactingMemStore) memstore).heapSize()); 726 727 ((MyCompactingMemStore) memstore).disableCompaction(); 728 MemStoreSize mss = memstore.getFlushableSize(); 729 ((CompactingMemStore)memstore).flushInMemory(); // push keys to pipeline without compaction 730 assertEquals(0, memstore.getSnapshot().getCellsCount()); 731 // No change in the cells data size. ie. memstore size. as there is no compaction. 732 assertEquals(totalCellsLen1 + totalCellsLen2, regionServicesForStores.getMemStoreSize()); 733 assertEquals(totalHeapSize2 + CellArrayImmutableSegment.DEEP_OVERHEAD_CAM, 734 ((CompactingMemStore) memstore).heapSize()); 735 736 int totalCellsLen3 = addRowsByKeys(memstore, keys3);// 3 more cells added 737 assertEquals(totalCellsLen1 + totalCellsLen2 + totalCellsLen3, 738 regionServicesForStores.getMemStoreSize()); 739 long totalHeapSize3 = totalHeapSize2 + CellArrayImmutableSegment.DEEP_OVERHEAD_CAM 740 + 3 * oneCellOnCSLMHeapSize; 741 assertEquals(totalHeapSize3, ((CompactingMemStore) memstore).heapSize()); 742 743 ((MyCompactingMemStore)memstore).enableCompaction(); 744 mss = memstore.getFlushableSize(); 745 ((CompactingMemStore)memstore).flushInMemory(); // push keys to pipeline and compact 746 assertEquals(0, memstore.getSnapshot().getCellsCount()); 747 // active flushed to pipeline and all 3 segments compacted. Will get rid of duplicated cells. 748 // Out of total 10, only 4 cells are unique 749 totalCellsLen2 = totalCellsLen2 / 3;// 2 out of 3 cells are duplicated 750 totalCellsLen3 = 0;// All duplicated cells. 751 assertEquals(totalCellsLen1 + totalCellsLen2 + totalCellsLen3, 752 regionServicesForStores.getMemStoreSize()); 753 // Only 4 unique cells left 754 assertEquals(4 * oneCellOnCAHeapSize + MutableSegment.DEEP_OVERHEAD 755 + CellArrayImmutableSegment.DEEP_OVERHEAD_CAM, ((CompactingMemStore) memstore).heapSize()); 756 757 mss = memstore.getFlushableSize(); 758 MemStoreSnapshot snapshot = memstore.snapshot(); // push keys to snapshot 759 // simulate flusher 760 region.decrMemStoreSize(mss.getDataSize(), mss.getHeapSize(), mss.getOffHeapSize(), 761 mss.getCellsCount()); 762 ImmutableSegment s = memstore.getSnapshot(); 763 assertEquals(4, s.getCellsCount()); 764 assertEquals(0, regionServicesForStores.getMemStoreSize()); 765 766 memstore.clearSnapshot(snapshot.getId()); 767 } 768 769 @Test 770 public void testMagicCompaction3Buckets() throws IOException { 771 772 MemoryCompactionPolicy compactionType = MemoryCompactionPolicy.ADAPTIVE; 773 memstore.getConfiguration().set(CompactingMemStore.COMPACTING_MEMSTORE_TYPE_KEY, 774 String.valueOf(compactionType)); 775 memstore.getConfiguration().setDouble( 776 AdaptiveMemStoreCompactionStrategy.ADAPTIVE_COMPACTION_THRESHOLD_KEY, 0.45); 777 memstore.getConfiguration().setInt( 778 AdaptiveMemStoreCompactionStrategy.COMPACTING_MEMSTORE_THRESHOLD_KEY, 2); 779 memstore.getConfiguration().setInt(CompactingMemStore.IN_MEMORY_FLUSH_THRESHOLD_FACTOR_KEY, 1); 780 ((MyCompactingMemStore) memstore).initiateType(compactionType, memstore.getConfiguration()); 781 782 String[] keys1 = { "A", "B", "D" }; 783 String[] keys2 = { "A" }; 784 String[] keys3 = { "A", "A", "B", "C" }; 785 String[] keys4 = { "D", "B", "B" }; 786 787 int totalCellsLen1 = addRowsByKeys(memstore, keys1);// Adding 3 cells. 788 int oneCellOnCSLMHeapSize = 120; 789 assertEquals(totalCellsLen1, region.getMemStoreDataSize()); 790 long totalHeapSize = MutableSegment.DEEP_OVERHEAD + 3 * oneCellOnCSLMHeapSize; 791 assertEquals(totalHeapSize, memstore.heapSize()); 792 793 ((CompactingMemStore)memstore).flushInMemory(); // push keys to pipeline - flatten 794 assertEquals(3, ((CompactingMemStore) memstore).getImmutableSegments().getNumOfCells()); 795 assertEquals(1.0, 796 ((CompactingMemStore) memstore).getImmutableSegments().getEstimatedUniquesFrac(), 0); 797 assertEquals(0, memstore.getSnapshot().getCellsCount()); 798 799 addRowsByKeys(memstore, keys2);// Adding 1 more cell - flatten. 800 ((CompactingMemStore)memstore).flushInMemory(); // push keys to pipeline without compaction 801 assertEquals(4, ((CompactingMemStore) memstore).getImmutableSegments().getNumOfCells()); 802 assertEquals(1.0, 803 ((CompactingMemStore) memstore).getImmutableSegments().getEstimatedUniquesFrac(), 0); 804 assertEquals(0, memstore.getSnapshot().getCellsCount()); 805 806 addRowsByKeys(memstore, keys3);// Adding 4 more cells - merge. 807 ((CompactingMemStore)memstore).flushInMemory(); // push keys to pipeline without compaction 808 assertEquals(8, ((CompactingMemStore) memstore).getImmutableSegments().getNumOfCells()); 809 assertEquals((4.0 / 8.0), 810 ((CompactingMemStore) memstore).getImmutableSegments().getEstimatedUniquesFrac(), 0); 811 assertEquals(0, memstore.getSnapshot().getCellsCount()); 812 813 addRowsByKeys(memstore, keys4);// 3 more cells added - compact (or not) 814 ((CompactingMemStore)memstore).flushInMemory(); // push keys to pipeline and compact 815 int numCells = ((CompactingMemStore) memstore).getImmutableSegments().getNumOfCells(); 816 assertTrue(4 == numCells || 11 == numCells); 817 assertEquals(0, memstore.getSnapshot().getCellsCount()); 818 819 MemStoreSize mss = memstore.getFlushableSize(); 820 MemStoreSnapshot snapshot = memstore.snapshot(); // push keys to snapshot 821 // simulate flusher 822 region.decrMemStoreSize(mss); 823 ImmutableSegment s = memstore.getSnapshot(); 824 numCells = s.getCellsCount(); 825 assertTrue(4 == numCells || 11 == numCells); 826 assertEquals(0, regionServicesForStores.getMemStoreSize()); 827 828 memstore.clearSnapshot(snapshot.getId()); 829 } 830 831 protected int addRowsByKeys(final AbstractMemStore hmc, String[] keys) { 832 byte[] fam = Bytes.toBytes("testfamily"); 833 byte[] qf = Bytes.toBytes("testqualifier"); 834 long size = hmc.getActive().getDataSize(); 835 long heapOverhead = hmc.getActive().getHeapSize(); 836 int cellsCount = hmc.getActive().getCellsCount(); 837 int totalLen = 0; 838 for (int i = 0; i < keys.length; i++) { 839 long timestamp = System.currentTimeMillis(); 840 Threads.sleep(1); // to make sure each kv gets a different ts 841 byte[] row = Bytes.toBytes(keys[i]); 842 byte[] val = Bytes.toBytes(keys[i] + i); 843 KeyValue kv = new KeyValue(row, fam, qf, timestamp, val); 844 totalLen += kv.getLength(); 845 hmc.add(kv, null); 846 LOG.debug("added kv: " + kv.getKeyString() + ", timestamp:" + kv.getTimestamp()); 847 } 848 regionServicesForStores.addMemStoreSize(hmc.getActive().getDataSize() - size, 849 hmc.getActive().getHeapSize() - heapOverhead, 0, 850 hmc.getActive().getCellsCount() - cellsCount); 851 return totalLen; 852 } 853 854 // for controlling the val size when adding a new cell 855 protected int addRowsByKeys(final AbstractMemStore hmc, String[] keys, byte[] val) { 856 byte[] fam = Bytes.toBytes("testfamily"); 857 byte[] qf = Bytes.toBytes("testqualifier"); 858 long size = hmc.getActive().getDataSize(); 859 long heapOverhead = hmc.getActive().getHeapSize(); 860 int cellsCount = hmc.getActive().getCellsCount(); 861 int totalLen = 0; 862 for (int i = 0; i < keys.length; i++) { 863 long timestamp = System.currentTimeMillis(); 864 Threads.sleep(1); // to make sure each kv gets a different ts 865 byte[] row = Bytes.toBytes(keys[i]); 866 KeyValue kv = new KeyValue(row, fam, qf, timestamp, val); 867 totalLen += kv.getLength(); 868 hmc.add(kv, null); 869 LOG.debug("added kv: " + kv.getKeyString() + ", timestamp:" + kv.getTimestamp()); 870 } 871 regionServicesForStores.addMemStoreSize(hmc.getActive().getDataSize() - size, 872 hmc.getActive().getHeapSize() - heapOverhead, 0, cellsCount); 873 return totalLen; 874 } 875 876 private class EnvironmentEdgeForMemstoreTest implements EnvironmentEdge { 877 long t = 1234; 878 879 @Override 880 public long currentTime() { 881 return t; 882 } 883 public void setCurrentTimeMillis(long t) { 884 this.t = t; 885 } 886 } 887 888 static protected class MyCompactingMemStore extends CompactingMemStore { 889 890 public MyCompactingMemStore(Configuration conf, CellComparator c, HStore store, 891 RegionServicesForStores regionServices, MemoryCompactionPolicy compactionPolicy) 892 throws IOException { 893 super(conf, c, store, regionServices, compactionPolicy); 894 } 895 896 void disableCompaction() { 897 allowCompaction.set(false); 898 } 899 900 void enableCompaction() { 901 allowCompaction.set(true); 902 } 903 void initiateType(MemoryCompactionPolicy compactionType, Configuration conf) 904 throws IllegalArgumentIOException { 905 compactor.initiateCompactionStrategy(compactionType, conf, "CF_TEST"); 906 } 907 908 } 909}