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