|
5 | 5 | */
|
6 | 6 | package org.elasticsearch.index.store;
|
7 | 7 |
|
| 8 | +import org.apache.lucene.codecs.CodecUtil; |
8 | 9 | import org.apache.lucene.document.Document;
|
9 | 10 | import org.apache.lucene.document.Field;
|
10 | 11 | import org.apache.lucene.document.NumericDocValuesField;
|
|
14 | 15 | import org.apache.lucene.index.FieldInfo;
|
15 | 16 | import org.apache.lucene.index.FieldInfos;
|
16 | 17 | import org.apache.lucene.index.IndexCommit;
|
| 18 | +import org.apache.lucene.index.IndexFileNames; |
17 | 19 | import org.apache.lucene.index.IndexOptions;
|
18 | 20 | import org.apache.lucene.index.IndexWriter;
|
19 | 21 | import org.apache.lucene.index.IndexWriterConfig;
|
|
47 | 49 | import org.elasticsearch.common.lease.Releasables;
|
48 | 50 | import org.elasticsearch.common.lucene.BytesRefs;
|
49 | 51 | import org.elasticsearch.common.lucene.Lucene;
|
| 52 | +import org.elasticsearch.common.lucene.store.ByteArrayIndexInput; |
50 | 53 | import org.elasticsearch.common.settings.Settings;
|
51 | 54 | import org.elasticsearch.common.unit.ByteSizeUnit;
|
52 | 55 | import org.elasticsearch.common.unit.ByteSizeValue;
|
|
59 | 62 | import org.elasticsearch.index.shard.ShardId;
|
60 | 63 | import org.elasticsearch.index.snapshots.IndexShardSnapshotStatus;
|
61 | 64 | import org.elasticsearch.index.snapshots.blobstore.BlobStoreIndexShardSnapshot;
|
| 65 | +import org.elasticsearch.index.store.checksum.ChecksumBlobContainerIndexInput; |
62 | 66 | import org.elasticsearch.index.translog.Translog;
|
63 | 67 | import org.elasticsearch.repositories.IndexId;
|
64 | 68 | import org.elasticsearch.repositories.blobstore.BlobStoreRepository;
|
|
98 | 102 | import static org.hamcrest.Matchers.equalTo;
|
99 | 103 | import static org.hamcrest.Matchers.greaterThan;
|
100 | 104 | import static org.hamcrest.Matchers.hasSize;
|
| 105 | +import static org.hamcrest.Matchers.instanceOf; |
101 | 106 | import static org.hamcrest.Matchers.is;
|
102 | 107 | import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
| 108 | +import static org.hamcrest.Matchers.nullValue; |
103 | 109 | import static org.hamcrest.Matchers.sameInstance;
|
104 | 110 |
|
105 | 111 | public class SearchableSnapshotDirectoryTests extends ESTestCase {
|
@@ -308,21 +314,125 @@ public void testReadBytes() throws Exception {
|
308 | 314 | });
|
309 | 315 | }
|
310 | 316 |
|
| 317 | + public void testChecksumBlobContainerIndexInput() throws Exception { |
| 318 | + testDirectories( |
| 319 | + randomBoolean(), |
| 320 | + false, // no prewarming in this test because we want to ensure that files are accessed on purpose |
| 321 | + (directory, snapshotDirectory) -> { |
| 322 | + for (String fileName : randomSubsetOf(Arrays.asList(snapshotDirectory.listAll()))) { |
| 323 | + final long checksum; |
| 324 | + try (IndexInput input = directory.openInput(fileName, Store.READONCE_CHECKSUM)) { |
| 325 | + checksum = CodecUtil.checksumEntireFile(input); |
| 326 | + } |
| 327 | + |
| 328 | + final long snapshotChecksum; |
| 329 | + try (IndexInput input = snapshotDirectory.openInput(fileName, Store.READONCE_CHECKSUM)) { |
| 330 | + snapshotChecksum = CodecUtil.retrieveChecksum(input); |
| 331 | + assertThat( |
| 332 | + input, |
| 333 | + "si".equals(IndexFileNames.getExtension(fileName)) || fileName.startsWith(IndexFileNames.SEGMENTS) |
| 334 | + ? instanceOf(ByteArrayIndexInput.class) |
| 335 | + : instanceOf(ChecksumBlobContainerIndexInput.class) |
| 336 | + ); |
| 337 | + } |
| 338 | + |
| 339 | + assertThat( |
| 340 | + "Expected checksum [" + checksum + "] but got [" + snapshotChecksum + ']', |
| 341 | + snapshotChecksum, |
| 342 | + equalTo(checksum) |
| 343 | + ); |
| 344 | + assertThat( |
| 345 | + "File [" + fileName + "] should have been read from heap", |
| 346 | + snapshotDirectory.getStats(fileName), |
| 347 | + nullValue() |
| 348 | + ); |
| 349 | + } |
| 350 | + } |
| 351 | + ); |
| 352 | + } |
| 353 | + |
| 354 | + public void testMetadataSnapshotsDoesNotAccessFilesOnDisk() throws Exception { |
| 355 | + final ShardId shardId = new ShardId("_name", "_id", 0); |
| 356 | + final IndexSettings indexSettings = newIndexSettings(); |
| 357 | + |
| 358 | + // sometimes load store's MetadataSnapshot using an IndexCommit |
| 359 | + final boolean useIndexCommit = randomBoolean(); |
| 360 | + logger.info("--> loading Store.MetadataSnapshot using index commit is [{}]", useIndexCommit); |
| 361 | + final CheckedFunction<Store, Store.MetadataSnapshot, IOException> loader = store -> { |
| 362 | + if (useIndexCommit) { |
| 363 | + return store.getMetadata(Lucene.getIndexCommit(Lucene.readSegmentInfos(store.directory()), store.directory())); |
| 364 | + } else { |
| 365 | + return store.getMetadata(null, true); |
| 366 | + } |
| 367 | + }; |
| 368 | + |
| 369 | + testDirectories( |
| 370 | + randomBoolean(), |
| 371 | + false, // no prewarming in this test because we want to ensure that files are accessed on purpose |
| 372 | + ((directory, snapshotDirectory) -> { |
| 373 | + final Store.MetadataSnapshot metadata; |
| 374 | + try (Store store = new Store(shardId, indexSettings, directory, new DummyShardLock(shardId))) { |
| 375 | + metadata = loader.apply(store); |
| 376 | + assertNotNull(metadata); |
| 377 | + } |
| 378 | + |
| 379 | + final Store.MetadataSnapshot snapshotMetadata; |
| 380 | + try (Store store = new Store(shardId, indexSettings, snapshotDirectory, new DummyShardLock(shardId))) { |
| 381 | + assertTrue("No files should have been read yet", snapshotDirectory.getStats().isEmpty()); |
| 382 | + snapshotMetadata = store.getMetadata(null); |
| 383 | + assertTrue("No files should have been read to compute MetadataSnapshot", snapshotDirectory.getStats().isEmpty()); |
| 384 | + assertNotNull(snapshotMetadata); |
| 385 | + } |
| 386 | + |
| 387 | + final Store.RecoveryDiff diff = randomBoolean() |
| 388 | + ? metadata.recoveryDiff(snapshotMetadata) |
| 389 | + : snapshotMetadata.recoveryDiff(metadata); |
| 390 | + |
| 391 | + assertThat( |
| 392 | + "List of different files should be empty but got [" + metadata.asMap() + "] and [" + snapshotMetadata.asMap() + ']', |
| 393 | + diff.different.isEmpty(), |
| 394 | + is(true) |
| 395 | + ); |
| 396 | + assertThat( |
| 397 | + "List of missing files should be empty but got [" + metadata.asMap() + "] and [" + snapshotMetadata.asMap() + ']', |
| 398 | + diff.missing.isEmpty(), |
| 399 | + is(true) |
| 400 | + ); |
| 401 | + assertThat( |
| 402 | + "List of files should be identical [" + metadata.asMap() + "] and [" + snapshotMetadata.asMap() + ']', |
| 403 | + diff.identical.size(), |
| 404 | + equalTo(metadata.size()) |
| 405 | + ); |
| 406 | + assertThat("Number of files should be identical", snapshotMetadata.size(), equalTo(metadata.size())); |
| 407 | + |
| 408 | + for (StoreFileMetadata storeFileMetadata : metadata) { |
| 409 | + final StoreFileMetadata snapshotFileMetadata = snapshotMetadata.get(storeFileMetadata.name()); |
| 410 | + assertTrue( |
| 411 | + storeFileMetadata + " should be identical but got [" + snapshotFileMetadata + ']', |
| 412 | + storeFileMetadata.isSame(snapshotFileMetadata) |
| 413 | + ); |
| 414 | + } |
| 415 | + }) |
| 416 | + ); |
| 417 | + } |
| 418 | + |
311 | 419 | /**
|
312 | 420 | * This method :
|
313 | 421 | * - sets up a default {@link Directory} and index random documents
|
314 | 422 | * - snapshots the directory using a FS repository
|
315 | 423 | * - creates a {@link SearchableSnapshotDirectory} instance based on the snapshotted files
|
316 | 424 | * - consumes the default and the searchable snapshot directories using the {@link CheckedBiConsumer}.
|
317 | 425 | */
|
318 |
| - private void testDirectories(final CheckedBiConsumer<Directory, Directory, Exception> consumer) throws Exception { |
319 |
| - final IndexSettings indexSettings = IndexSettingsModule.newIndexSettings( |
320 |
| - "_index", |
321 |
| - Settings.builder() |
322 |
| - .put(IndexMetadata.SETTING_INDEX_UUID, UUIDs.randomBase64UUID(random())) |
323 |
| - .put(IndexMetadata.SETTING_VERSION_CREATED, org.elasticsearch.Version.CURRENT) |
324 |
| - .build() |
325 |
| - ); |
| 426 | + private void testDirectories(final CheckedBiConsumer<Directory, SearchableSnapshotDirectory, Exception> consumer) throws Exception { |
| 427 | + testDirectories(randomBoolean(), randomBoolean(), consumer); |
| 428 | + } |
| 429 | + |
| 430 | + private void testDirectories( |
| 431 | + final boolean enableCache, |
| 432 | + final boolean prewarmCache, |
| 433 | + final CheckedBiConsumer<Directory, SearchableSnapshotDirectory, Exception> consumer |
| 434 | + ) throws Exception { |
| 435 | + final IndexSettings indexSettings = newIndexSettings(); |
326 | 436 | final ShardId shardId = new ShardId(indexSettings.getIndex(), randomIntBetween(0, 10));
|
327 | 437 | final List<Releasable> releasables = new ArrayList<>();
|
328 | 438 |
|
@@ -442,8 +552,8 @@ protected void assertSnapshotOrGenericThread() {
|
442 | 552 | indexId,
|
443 | 553 | shardId,
|
444 | 554 | Settings.builder()
|
445 |
| - .put(SNAPSHOT_CACHE_ENABLED_SETTING.getKey(), randomBoolean()) |
446 |
| - .put(SNAPSHOT_CACHE_PREWARM_ENABLED_SETTING.getKey(), randomBoolean()) |
| 555 | + .put(SNAPSHOT_CACHE_ENABLED_SETTING.getKey(), enableCache) |
| 556 | + .put(SNAPSHOT_CACHE_PREWARM_ENABLED_SETTING.getKey(), prewarmCache) |
447 | 557 | .build(),
|
448 | 558 | () -> 0L,
|
449 | 559 | cacheService,
|
@@ -606,4 +716,14 @@ private void assertListOfFiles(Path cacheDir, Matcher<Integer> matchNumberOfFile
|
606 | 716 | assertThat("Number of files (" + files.size() + ") mismatch, got : " + files.keySet(), files.size(), matchNumberOfFiles);
|
607 | 717 | assertThat("Sum of file sizes mismatch, got: " + files, files.values().stream().mapToLong(Long::longValue).sum(), matchSizeOfFiles);
|
608 | 718 | }
|
| 719 | + |
| 720 | + private static IndexSettings newIndexSettings() { |
| 721 | + return IndexSettingsModule.newIndexSettings( |
| 722 | + "_index", |
| 723 | + Settings.builder() |
| 724 | + .put(IndexMetadata.SETTING_INDEX_UUID, UUIDs.randomBase64UUID(random())) |
| 725 | + .put(IndexMetadata.SETTING_VERSION_CREATED, org.elasticsearch.Version.CURRENT) |
| 726 | + .build() |
| 727 | + ); |
| 728 | + } |
609 | 729 | }
|
0 commit comments