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