001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.regionserver; 019 020import static org.apache.hadoop.hbase.regionserver.Store.PRIORITY_USER; 021import static org.junit.jupiter.api.Assertions.assertEquals; 022import static org.junit.jupiter.api.Assertions.assertFalse; 023import static org.junit.jupiter.api.Assertions.assertTrue; 024 025import java.io.IOException; 026import java.security.Key; 027import java.util.ArrayList; 028import java.util.Collection; 029import java.util.Collections; 030import java.util.Date; 031import java.util.HashSet; 032import java.util.Iterator; 033import java.util.List; 034import java.util.NavigableSet; 035import java.util.Optional; 036import java.util.Set; 037import java.util.concurrent.ConcurrentSkipListSet; 038import javax.crypto.spec.SecretKeySpec; 039import org.apache.hadoop.conf.Configuration; 040import org.apache.hadoop.fs.FileStatus; 041import org.apache.hadoop.fs.FileSystem; 042import org.apache.hadoop.fs.Path; 043import org.apache.hadoop.hbase.ArrayBackedTag; 044import org.apache.hadoop.hbase.Cell; 045import org.apache.hadoop.hbase.CellComparatorImpl; 046import org.apache.hadoop.hbase.CellUtil; 047import org.apache.hadoop.hbase.ExtendedCell; 048import org.apache.hadoop.hbase.HBaseConfiguration; 049import org.apache.hadoop.hbase.HBaseTestingUtil; 050import org.apache.hadoop.hbase.HConstants; 051import org.apache.hadoop.hbase.KeyValue; 052import org.apache.hadoop.hbase.TableName; 053import org.apache.hadoop.hbase.Tag; 054import org.apache.hadoop.hbase.TagType; 055import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 056import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 057import org.apache.hadoop.hbase.client.Get; 058import org.apache.hadoop.hbase.client.RegionInfo; 059import org.apache.hadoop.hbase.client.RegionInfoBuilder; 060import org.apache.hadoop.hbase.client.Scan; 061import org.apache.hadoop.hbase.client.TableDescriptor; 062import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 063import org.apache.hadoop.hbase.io.crypto.MockAesKeyProvider; 064import org.apache.hadoop.hbase.io.crypto.aes.AES; 065import org.apache.hadoop.hbase.io.hfile.HFile; 066import org.apache.hadoop.hbase.mob.MobCell; 067import org.apache.hadoop.hbase.mob.MobConstants; 068import org.apache.hadoop.hbase.mob.MobFileCache; 069import org.apache.hadoop.hbase.mob.MobUtils; 070import org.apache.hadoop.hbase.monitoring.MonitoredTask; 071import org.apache.hadoop.hbase.regionserver.compactions.CompactionContext; 072import org.apache.hadoop.hbase.regionserver.compactions.CompactionLifeCycleTracker; 073import org.apache.hadoop.hbase.regionserver.throttle.NoLimitThroughputController; 074import org.apache.hadoop.hbase.security.EncryptionUtil; 075import org.apache.hadoop.hbase.security.User; 076import org.apache.hadoop.hbase.testclassification.MediumTests; 077import org.apache.hadoop.hbase.util.Bytes; 078import org.apache.hadoop.hbase.util.CommonFSUtils; 079import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 080import org.apache.hadoop.hbase.wal.WALFactory; 081import org.junit.jupiter.api.BeforeEach; 082import org.junit.jupiter.api.Test; 083import org.junit.jupiter.api.TestInfo; 084import org.mockito.Mockito; 085import org.slf4j.Logger; 086import org.slf4j.LoggerFactory; 087 088@org.junit.jupiter.api.Tag(MediumTests.TAG) 089public class TestHMobStore { 090 091 public static final Logger LOG = LoggerFactory.getLogger(TestHMobStore.class); 092 private String name; 093 094 private HMobStore store; 095 private HRegion region; 096 private FileSystem fs; 097 private byte[] table = Bytes.toBytes("table"); 098 private byte[] family = Bytes.toBytes("family"); 099 private byte[] row = Bytes.toBytes("row"); 100 private byte[] row2 = Bytes.toBytes("row2"); 101 private byte[] qf1 = Bytes.toBytes("qf1"); 102 private byte[] qf2 = Bytes.toBytes("qf2"); 103 private byte[] qf3 = Bytes.toBytes("qf3"); 104 private byte[] qf4 = Bytes.toBytes("qf4"); 105 private byte[] qf5 = Bytes.toBytes("qf5"); 106 private byte[] qf6 = Bytes.toBytes("qf6"); 107 private byte[] value = Bytes.toBytes("value"); 108 private byte[] value2 = Bytes.toBytes("value2"); 109 private Path mobFilePath; 110 private Date currentDate = new Date(); 111 private ExtendedCell seekKey1; 112 private ExtendedCell seekKey2; 113 private ExtendedCell seekKey3; 114 private NavigableSet<byte[]> qualifiers = new ConcurrentSkipListSet<>(Bytes.BYTES_COMPARATOR); 115 private List<ExtendedCell> expected = new ArrayList<>(); 116 private long id = EnvironmentEdgeManager.currentTime(); 117 private Get get = new Get(row); 118 private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 119 private final String DIR = TEST_UTIL.getDataTestDir("TestHMobStore").toString(); 120 121 /** 122 * Setup 123 */ 124 @BeforeEach 125 public void setUp(TestInfo testInfo) throws Exception { 126 this.name = testInfo.getTestMethod().get().getName(); 127 qualifiers.add(qf1); 128 qualifiers.add(qf3); 129 qualifiers.add(qf5); 130 131 Iterator<byte[]> iter = qualifiers.iterator(); 132 while (iter.hasNext()) { 133 byte[] next = iter.next(); 134 expected.add(new KeyValue(row, family, next, 1, value)); 135 get.addColumn(family, next); 136 get.readAllVersions(); 137 } 138 } 139 140 private void init(String methodName, Configuration conf, boolean testStore) throws IOException { 141 ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder.newBuilder(family) 142 .setMobEnabled(true).setMobThreshold(3L).setMaxVersions(4).build(); 143 init(methodName, conf, cfd, testStore); 144 } 145 146 private void init(String methodName, Configuration conf, ColumnFamilyDescriptor cfd, 147 boolean testStore) throws IOException { 148 TableDescriptor td = 149 TableDescriptorBuilder.newBuilder(TableName.valueOf(table)).setColumnFamily(cfd).build(); 150 151 // Setting up tje Region and Store 152 Path basedir = new Path(DIR + methodName); 153 Path tableDir = CommonFSUtils.getTableDir(basedir, td.getTableName()); 154 String logName = "logs"; 155 Path logdir = new Path(basedir, logName); 156 FileSystem fs = FileSystem.get(conf); 157 fs.delete(logdir, true); 158 159 RegionInfo info = RegionInfoBuilder.newBuilder(td.getTableName()).build(); 160 ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null, 161 MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT); 162 final Configuration walConf = new Configuration(conf); 163 CommonFSUtils.setRootDir(walConf, basedir); 164 final WALFactory wals = new WALFactory(walConf, methodName); 165 region = new HRegion(tableDir, wals.getWAL(info), fs, conf, info, td, null); 166 region.setMobFileCache(new MobFileCache(conf)); 167 store = new HMobStore(region, cfd, conf, false); 168 if (testStore) { 169 init(conf, cfd); 170 } 171 } 172 173 private void init(Configuration conf, ColumnFamilyDescriptor cfd) throws IOException { 174 Path basedir = CommonFSUtils.getRootDir(conf); 175 fs = FileSystem.get(conf); 176 Path homePath = 177 new Path(basedir, Bytes.toString(family) + Path.SEPARATOR + Bytes.toString(family)); 178 fs.mkdirs(homePath); 179 180 KeyValue key1 = new KeyValue(row, family, qf1, 1, value); 181 KeyValue key2 = new KeyValue(row, family, qf2, 1, value); 182 KeyValue key3 = new KeyValue(row2, family, qf3, 1, value2); 183 KeyValue[] keys = new KeyValue[] { key1, key2, key3 }; 184 int maxKeyCount = keys.length; 185 StoreFileWriter mobWriter = store.createWriterInTmp(currentDate, maxKeyCount, 186 cfd.getCompactionCompressionType(), region.getRegionInfo().getStartKey(), false); 187 mobFilePath = mobWriter.getPath(); 188 189 mobWriter.append(key1); 190 mobWriter.append(key2); 191 mobWriter.append(key3); 192 mobWriter.close(); 193 194 String targetPathName = MobUtils.formatDate(currentDate); 195 byte[] referenceValue = Bytes.toBytes(targetPathName + Path.SEPARATOR + mobFilePath.getName()); 196 Tag tableNameTag = 197 new ArrayBackedTag(TagType.MOB_TABLE_NAME_TAG_TYPE, store.getTableName().getName()); 198 KeyValue kv1 = new KeyValue(row, family, qf1, Long.MAX_VALUE, referenceValue); 199 KeyValue kv2 = new KeyValue(row, family, qf2, Long.MAX_VALUE, referenceValue); 200 KeyValue kv3 = new KeyValue(row2, family, qf3, Long.MAX_VALUE, referenceValue); 201 seekKey1 = MobUtils.createMobRefCell(kv1, referenceValue, tableNameTag); 202 seekKey2 = MobUtils.createMobRefCell(kv2, referenceValue, tableNameTag); 203 seekKey3 = MobUtils.createMobRefCell(kv3, referenceValue, tableNameTag); 204 } 205 206 /** 207 * Getting data from memstore 208 */ 209 210 @Test 211 public void testGetFromMemStore() throws IOException { 212 final Configuration conf = HBaseConfiguration.create(); 213 init(name, conf, false); 214 215 // Put data in memstore 216 this.store.add(new KeyValue(row, family, qf1, 1, value), null); 217 this.store.add(new KeyValue(row, family, qf2, 1, value), null); 218 this.store.add(new KeyValue(row, family, qf3, 1, value), null); 219 this.store.add(new KeyValue(row, family, qf4, 1, value), null); 220 this.store.add(new KeyValue(row, family, qf5, 1, value), null); 221 this.store.add(new KeyValue(row, family, qf6, 1, value), null); 222 223 Scan scan = new Scan(get); 224 InternalScanner scanner = (InternalScanner) store.getScanner(scan, 225 scan.getFamilyMap().get(store.getColumnFamilyDescriptor().getName()), 0); 226 227 List<Cell> results = new ArrayList<>(); 228 scanner.next(results); 229 Collections.sort(results, CellComparatorImpl.COMPARATOR); 230 scanner.close(); 231 232 // Compare 233 assertEquals(expected.size(), results.size()); 234 for (int i = 0; i < results.size(); i++) { 235 // Verify the values 236 assertEquals(expected.get(i), results.get(i)); 237 } 238 } 239 240 /** 241 * Getting MOB data from files 242 */ 243 @Test 244 public void testGetFromFiles() throws IOException { 245 final Configuration conf = TEST_UTIL.getConfiguration(); 246 init(name, conf, false); 247 248 // Put data in memstore 249 this.store.add(new KeyValue(row, family, qf1, 1, value), null); 250 this.store.add(new KeyValue(row, family, qf2, 1, value), null); 251 // flush 252 flush(1); 253 254 // Add more data 255 this.store.add(new KeyValue(row, family, qf3, 1, value), null); 256 this.store.add(new KeyValue(row, family, qf4, 1, value), null); 257 // flush 258 flush(2); 259 260 // Add more data 261 this.store.add(new KeyValue(row, family, qf5, 1, value), null); 262 this.store.add(new KeyValue(row, family, qf6, 1, value), null); 263 // flush 264 flush(3); 265 266 Scan scan = new Scan(get); 267 InternalScanner scanner = (InternalScanner) store.getScanner(scan, 268 scan.getFamilyMap().get(store.getColumnFamilyDescriptor().getName()), 0); 269 270 List<Cell> results = new ArrayList<>(); 271 scanner.next(results); 272 Collections.sort(results, CellComparatorImpl.COMPARATOR); 273 scanner.close(); 274 275 // Compare 276 assertEquals(expected.size(), results.size()); 277 for (int i = 0; i < results.size(); i++) { 278 assertEquals(expected.get(i), results.get(i)); 279 } 280 } 281 282 /** 283 * Getting the reference data from files 284 */ 285 @Test 286 public void testGetReferencesFromFiles() throws IOException { 287 final Configuration conf = HBaseConfiguration.create(); 288 init(name, conf, false); 289 290 // Put data in memstore 291 this.store.add(new KeyValue(row, family, qf1, 1, value), null); 292 this.store.add(new KeyValue(row, family, qf2, 1, value), null); 293 // flush 294 flush(1); 295 296 // Add more data 297 this.store.add(new KeyValue(row, family, qf3, 1, value), null); 298 this.store.add(new KeyValue(row, family, qf4, 1, value), null); 299 // flush 300 flush(2); 301 302 // Add more data 303 this.store.add(new KeyValue(row, family, qf5, 1, value), null); 304 this.store.add(new KeyValue(row, family, qf6, 1, value), null); 305 // flush 306 flush(3); 307 308 Scan scan = new Scan(get); 309 scan.setAttribute(MobConstants.MOB_SCAN_RAW, Bytes.toBytes(Boolean.TRUE)); 310 InternalScanner scanner = (InternalScanner) store.getScanner(scan, 311 scan.getFamilyMap().get(store.getColumnFamilyDescriptor().getName()), 0); 312 313 List<ExtendedCell> results = new ArrayList<>(); 314 scanner.next(results); 315 Collections.sort(results, CellComparatorImpl.COMPARATOR); 316 scanner.close(); 317 318 // Compare 319 assertEquals(expected.size(), results.size()); 320 for (int i = 0; i < results.size(); i++) { 321 ExtendedCell cell = results.get(i); 322 assertTrue(MobUtils.isMobReferenceCell(cell)); 323 } 324 } 325 326 /** 327 * Getting data from memstore and files 328 */ 329 @Test 330 public void testGetFromMemStoreAndFiles() throws IOException { 331 332 final Configuration conf = HBaseConfiguration.create(); 333 334 init(name, conf, false); 335 336 // Put data in memstore 337 this.store.add(new KeyValue(row, family, qf1, 1, value), null); 338 this.store.add(new KeyValue(row, family, qf2, 1, value), null); 339 // flush 340 flush(1); 341 342 // Add more data 343 this.store.add(new KeyValue(row, family, qf3, 1, value), null); 344 this.store.add(new KeyValue(row, family, qf4, 1, value), null); 345 // flush 346 flush(2); 347 348 // Add more data 349 this.store.add(new KeyValue(row, family, qf5, 1, value), null); 350 this.store.add(new KeyValue(row, family, qf6, 1, value), null); 351 352 Scan scan = new Scan(get); 353 InternalScanner scanner = (InternalScanner) store.getScanner(scan, 354 scan.getFamilyMap().get(store.getColumnFamilyDescriptor().getName()), 0); 355 356 List<Cell> results = new ArrayList<>(); 357 scanner.next(results); 358 Collections.sort(results, CellComparatorImpl.COMPARATOR); 359 scanner.close(); 360 361 // Compare 362 assertEquals(expected.size(), results.size()); 363 for (int i = 0; i < results.size(); i++) { 364 assertEquals(expected.get(i), results.get(i)); 365 } 366 } 367 368 /** 369 * Getting data from memstore and files 370 */ 371 @Test 372 public void testMobCellSizeThreshold() throws IOException { 373 final Configuration conf = HBaseConfiguration.create(); 374 ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder.newBuilder(family) 375 .setMobEnabled(true).setMobThreshold(100).setMaxVersions(4).build(); 376 init(name, conf, cfd, false); 377 378 // Put data in memstore 379 this.store.add(new KeyValue(row, family, qf1, 1, value), null); 380 this.store.add(new KeyValue(row, family, qf2, 1, value), null); 381 // flush 382 flush(1); 383 384 // Add more data 385 this.store.add(new KeyValue(row, family, qf3, 1, value), null); 386 this.store.add(new KeyValue(row, family, qf4, 1, value), null); 387 // flush 388 flush(2); 389 390 // Add more data 391 this.store.add(new KeyValue(row, family, qf5, 1, value), null); 392 this.store.add(new KeyValue(row, family, qf6, 1, value), null); 393 // flush 394 flush(3); 395 396 Scan scan = new Scan(get); 397 scan.setAttribute(MobConstants.MOB_SCAN_RAW, Bytes.toBytes(Boolean.TRUE)); 398 InternalScanner scanner = (InternalScanner) store.getScanner(scan, 399 scan.getFamilyMap().get(store.getColumnFamilyDescriptor().getName()), 0); 400 401 List<ExtendedCell> results = new ArrayList<>(); 402 scanner.next(results); 403 Collections.sort(results, CellComparatorImpl.COMPARATOR); 404 scanner.close(); 405 406 // Compare 407 assertEquals(expected.size(), results.size()); 408 for (int i = 0; i < results.size(); i++) { 409 ExtendedCell cell = results.get(i); 410 // this is not mob reference cell. 411 assertFalse(MobUtils.isMobReferenceCell(cell)); 412 assertEquals(expected.get(i), results.get(i)); 413 assertEquals(100, store.getColumnFamilyDescriptor().getMobThreshold()); 414 } 415 } 416 417 @Test 418 public void testCommitFile() throws Exception { 419 final Configuration conf = HBaseConfiguration.create(); 420 init(name, conf, true); 421 String targetPathName = MobUtils.formatDate(new Date()); 422 Path targetPath = 423 new Path(store.getPath(), (targetPathName + Path.SEPARATOR + mobFilePath.getName())); 424 fs.delete(targetPath, true); 425 assertFalse(fs.exists(targetPath)); 426 // commit file 427 store.commitFile(mobFilePath, targetPath); 428 assertTrue(fs.exists(targetPath)); 429 } 430 431 @Test 432 public void testResolve() throws Exception { 433 final Configuration conf = HBaseConfiguration.create(); 434 init(name, conf, true); 435 String targetPathName = MobUtils.formatDate(currentDate); 436 Path targetPath = new Path(store.getPath(), targetPathName); 437 store.commitFile(mobFilePath, targetPath); 438 // resolve 439 try (MobCell resultCell1 = store.resolve(seekKey1, false); 440 MobCell resultCell2 = store.resolve(seekKey2, false); 441 MobCell resultCell3 = store.resolve(seekKey3, false)) { 442 // compare 443 assertEquals(Bytes.toString(value), 444 Bytes.toString(CellUtil.cloneValue(resultCell1.getCell()))); 445 assertEquals(Bytes.toString(value), 446 Bytes.toString(CellUtil.cloneValue(resultCell2.getCell()))); 447 assertEquals(Bytes.toString(value2), 448 Bytes.toString(CellUtil.cloneValue(resultCell3.getCell()))); 449 } 450 } 451 452 @Test 453 public void testMobStoreScannerGetFilesRead() throws IOException { 454 doTestMobStoreScannerGetFilesRead(false); 455 } 456 457 @Test 458 public void testReversedMobStoreScannerGetFilesRead() throws IOException { 459 doTestMobStoreScannerGetFilesRead(true); 460 } 461 462 /** 463 * Utility method for getFilesRead tests on MOB store scanners. Uses values above mob threshold so 464 * DefaultMobStoreFlusher creates the mob file and refs. 465 */ 466 private void doTestMobStoreScannerGetFilesRead(boolean reversed) throws IOException { 467 // Setup: conf, root dir, and MOB store init (mob threshold causes large values to go to MOB). 468 final Configuration conf = HBaseConfiguration.create(); 469 Path basedir = new Path(DIR + name); 470 CommonFSUtils.setRootDir(conf, basedir); 471 init(name, conf, false); 472 473 // Add values above MOB threshold and flush so DefaultMobStoreFlusher creates mob file and refs. 474 byte[] valueAboveThreshold = Bytes.toBytes("value"); // threshold in setup is 3 bytes 475 this.store.add(new KeyValue(row, family, qf1, 1, valueAboveThreshold), null); 476 this.store.add(new KeyValue(row, family, qf2, 1, valueAboveThreshold), null); 477 this.store.add(new KeyValue(row2, family, qf3, 1, valueAboveThreshold), null); 478 flush(1); 479 480 // Collect expected paths: store files (refs) plus actual MOB files under mob family path. 481 FileSystem storeFs = store.getFileSystem(); 482 Set<Path> expectedFilePaths = new HashSet<>(); 483 for (HStoreFile storeFile : this.store.getStorefiles()) { 484 expectedFilePaths.add(storeFs.makeQualified(storeFile.getPath())); 485 } 486 Path mobFamilyPath = 487 MobUtils.getMobFamilyPath(conf, TableName.valueOf(table), Bytes.toString(family)); 488 if (storeFs.exists(mobFamilyPath)) { 489 FileStatus[] mobFiles = storeFs.listStatus(mobFamilyPath); 490 for (FileStatus f : mobFiles) { 491 if (!f.isDirectory()) { 492 expectedFilePaths.add(storeFs.makeQualified(f.getPath())); 493 } 494 } 495 } 496 assertTrue(expectedFilePaths.size() >= 2, 497 "Should have at least one store file and one mob file"); 498 499 // Build scan (optionally reversed) and target columns; get store scanner and verify type. 500 Scan scan = new Scan(); 501 if (reversed) { 502 scan.setReversed(true); 503 } 504 scan.addColumn(family, qf1); 505 scan.addColumn(family, qf2); 506 scan.addColumn(family, qf3); 507 NavigableSet<byte[]> targetCols = new ConcurrentSkipListSet<>(Bytes.BYTES_COMPARATOR); 508 targetCols.add(qf1); 509 targetCols.add(qf2); 510 targetCols.add(qf3); 511 512 KeyValueScanner kvScanner = store.getScanner(scan, targetCols, 0); 513 if (reversed) { 514 assertTrue(kvScanner instanceof ReversedMobStoreScanner, 515 "Store scanner should be ReversedMobStoreScanner"); 516 } else { 517 assertTrue(kvScanner instanceof MobStoreScanner, "Store scanner should be MobStoreScanner"); 518 } 519 520 // Before close: getFilesRead must be empty; then drain scanner to resolve MOB refs. 521 try { 522 Set<Path> filesReadBeforeClose = kvScanner.getFilesRead(); 523 assertTrue(filesReadBeforeClose.isEmpty(), "Should return empty set before closing"); 524 assertEquals(0, filesReadBeforeClose.size(), "Should have 0 files before closing"); 525 526 List<Cell> results = new ArrayList<>(); 527 InternalScanner storeScanner = (InternalScanner) kvScanner; 528 while (storeScanner.next(results)) { 529 results.clear(); 530 } 531 532 // Still before close: set must remain empty until scanner is closed. 533 filesReadBeforeClose = kvScanner.getFilesRead(); 534 assertTrue(filesReadBeforeClose.isEmpty(), 535 "Should return empty set before closing even after reading"); 536 } finally { 537 kvScanner.close(); 538 } 539 540 // After close: set must contain exactly the expected store + MOB file paths. 541 Set<Path> filesReadAfterClose = kvScanner.getFilesRead(); 542 assertEquals(expectedFilePaths.size(), filesReadAfterClose.size(), 543 "Should have exact file count after closing"); 544 assertEquals(expectedFilePaths, filesReadAfterClose, "Should contain all expected file paths"); 545 } 546 547 /** 548 * Flush the memstore 549 */ 550 private void flush(int storeFilesSize) throws IOException { 551 flushStore(store, id++); 552 assertEquals(storeFilesSize, this.store.getStorefiles().size()); 553 assertEquals(0, ((AbstractMemStore) this.store.memstore).getActive().getCellsCount()); 554 } 555 556 /** 557 * Flush the memstore 558 */ 559 private static void flushStore(HMobStore store, long id) throws IOException { 560 StoreFlushContext storeFlushCtx = store.createFlushContext(id, FlushLifeCycleTracker.DUMMY); 561 storeFlushCtx.prepare(); 562 storeFlushCtx.flushCache(Mockito.mock(MonitoredTask.class)); 563 storeFlushCtx.commit(Mockito.mock(MonitoredTask.class)); 564 } 565 566 @Test 567 public void testMOBStoreEncryption() throws Exception { 568 final Configuration conf = TEST_UTIL.getConfiguration(); 569 570 conf.set(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY, MockAesKeyProvider.class.getName()); 571 conf.set(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY, "hbase"); 572 byte[] keyBytes = new byte[AES.KEY_LENGTH]; 573 Bytes.secureRandom(keyBytes); 574 String algorithm = conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES); 575 Key cfKey = new SecretKeySpec(keyBytes, algorithm); 576 577 ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder.newBuilder(family) 578 .setMobEnabled(true).setMobThreshold(100).setMaxVersions(4).setEncryptionType(algorithm) 579 .setEncryptionKey(EncryptionUtil.wrapKey(conf, 580 conf.get(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY, User.getCurrent().getShortName()), 581 cfKey)) 582 .build(); 583 init(name, conf, cfd, false); 584 585 this.store.add(new KeyValue(row, family, qf1, 1, value), null); 586 this.store.add(new KeyValue(row, family, qf2, 1, value), null); 587 this.store.add(new KeyValue(row, family, qf3, 1, value), null); 588 flush(1); 589 590 this.store.add(new KeyValue(row, family, qf4, 1, value), null); 591 this.store.add(new KeyValue(row, family, qf5, 1, value), null); 592 this.store.add(new KeyValue(row, family, qf6, 1, value), null); 593 flush(2); 594 595 Collection<HStoreFile> storefiles = this.store.getStorefiles(); 596 checkMobHFileEncrytption(storefiles); 597 598 // Scan the values 599 Scan scan = new Scan(get); 600 StoreScanner scanner = (StoreScanner) store.getScanner(scan, 601 scan.getFamilyMap().get(store.getColumnFamilyDescriptor().getName()), 0); 602 603 List<Cell> results = new ArrayList<>(); 604 scanner.next(results); 605 Collections.sort(results, CellComparatorImpl.COMPARATOR); 606 scanner.close(); 607 assertEquals(expected.size(), results.size()); 608 for (int i = 0; i < results.size(); i++) { 609 assertEquals(expected.get(i), results.get(i)); 610 } 611 612 // Trigger major compaction 613 this.store.triggerMajorCompaction(); 614 Optional<CompactionContext> requestCompaction = 615 this.store.requestCompaction(PRIORITY_USER, CompactionLifeCycleTracker.DUMMY, null); 616 this.store.compact(requestCompaction.get(), NoLimitThroughputController.INSTANCE, null); 617 assertEquals(1, this.store.getStorefiles().size()); 618 619 // Check encryption after compaction 620 checkMobHFileEncrytption(this.store.getStorefiles()); 621 } 622 623 private void checkMobHFileEncrytption(Collection<HStoreFile> storefiles) { 624 HStoreFile storeFile = storefiles.iterator().next(); 625 HFile.Reader reader = storeFile.getReader().getHFileReader(); 626 byte[] encryptionKey = reader.getTrailer().getEncryptionKey(); 627 assertTrue(null != encryptionKey); 628 assertTrue(reader.getFileContext().getEncryptionContext().getCipher().getName() 629 .equals(HConstants.CIPHER_AES)); 630 } 631 632}