|
44 | 44 |
|
45 | 45 | import java.io.IOException; |
46 | 46 | import java.util.ArrayList; |
| 47 | +import java.util.HashMap; |
47 | 48 | import java.util.HashSet; |
48 | 49 | import java.util.List; |
49 | 50 | import java.util.Map; |
|
52 | 53 | import java.util.TreeSet; |
53 | 54 | import java.util.concurrent.CountDownLatch; |
54 | 55 | import java.util.function.Supplier; |
| 56 | +import java.util.stream.Collectors; |
| 57 | +import java.util.stream.LongStream; |
55 | 58 |
|
56 | 59 | import org.mockito.Mockito; |
57 | 60 |
|
58 | 61 | import static org.opensearch.index.translog.TranslogDeletionPolicies.createTranslogDeletionPolicy; |
| 62 | +import static org.opensearch.index.translog.transfer.TranslogTransferMetadata.METADATA_SEPARATOR; |
59 | 63 | import static org.opensearch.indices.RemoteStoreSettings.CLUSTER_REMOTE_STORE_PINNED_TIMESTAMP_ENABLED; |
60 | 64 | import static org.junit.Assert.fail; |
61 | 65 | import static org.mockito.ArgumentMatchers.any; |
@@ -304,6 +308,91 @@ public void testMetadataFileDeletion() throws Exception { |
304 | 308 | assertBusy(() -> { assertEquals(1, blobStoreTransferService.listAll(getTranslogDirectory().add(METADATA_DIR)).size()); }); |
305 | 309 | } |
306 | 310 |
|
| 311 | + public void testMetadataFileDeletionWithPinnedTimestamps() throws Exception { |
| 312 | + ArrayList<Translog.Operation> ops = new ArrayList<>(); |
| 313 | + // Test deletion of metadata files |
| 314 | + int numDocs = randomIntBetween(16, 20); |
| 315 | + for (int i = 0; i < numDocs; i++) { |
| 316 | + addToTranslogAndListAndUpload(translog, ops, new Translog.Index(String.valueOf(i), i, primaryTerm.get(), new byte[] { 1 })); |
| 317 | + translog.setMinSeqNoToKeep(i); |
| 318 | + assertBusy(() -> assertTrue(translog.isRemoteGenerationDeletionPermitsAvailable())); |
| 319 | + translog.trimUnreferencedReaders(); |
| 320 | + // This is just to make sure that each metadata is at least 1ms apart |
| 321 | + Thread.sleep(1); |
| 322 | + } |
| 323 | + |
| 324 | + CountDownLatch latch = new CountDownLatch(1); |
| 325 | + blobStoreTransferService.listAllInSortedOrder( |
| 326 | + getTranslogDirectory().add(METADATA_DIR), |
| 327 | + "metadata", |
| 328 | + Integer.MAX_VALUE, |
| 329 | + new LatchedActionListener<>(new ActionListener<>() { |
| 330 | + @Override |
| 331 | + public void onResponse(List<BlobMetadata> blobMetadataList) { |
| 332 | + List<String> pinnedTimestampMatchingMetadataFiles = new ArrayList<>(); |
| 333 | + List<Long> pinnedTimestamps = new ArrayList<>(); |
| 334 | + for (BlobMetadata blobMetadata : blobMetadataList) { |
| 335 | + String metadataFilename = blobMetadata.name(); |
| 336 | + if (randomBoolean()) { |
| 337 | + long timestamp = RemoteStoreUtils.invertLong(metadataFilename.split(METADATA_SEPARATOR)[3]); |
| 338 | + pinnedTimestamps.add(timestamp); |
| 339 | + pinnedTimestampMatchingMetadataFiles.add(metadataFilename); |
| 340 | + } |
| 341 | + } |
| 342 | + |
| 343 | + doAnswer(invocationOnMock -> { |
| 344 | + ActionListener<List<BlobMetadata>> actionListener = invocationOnMock.getArgument(3); |
| 345 | + actionListener.onResponse(List.of(new PlainBlobMetadata("pinned_timestamp_123", 1000))); |
| 346 | + return null; |
| 347 | + }).when(pinnedTimestampBlobStoreTransferService).listAllInSortedOrder(any(), any(), eq(1), any()); |
| 348 | + |
| 349 | + Map<Long, List<String>> pinnedTimestampsMap = new HashMap<>(); |
| 350 | + pinnedTimestamps.forEach(ts -> pinnedTimestampsMap.put(ts, new ArrayList<>())); |
| 351 | + |
| 352 | + try { |
| 353 | + when(remoteStorePinnedTimestampsBlobStore.read(any())).thenReturn( |
| 354 | + new RemotePinnedTimestamps.PinnedTimestamps(pinnedTimestampsMap) |
| 355 | + ); |
| 356 | + when(remoteStorePinnedTimestampsBlobStore.getBlobPathForUpload(any())).thenReturn(new BlobPath()); |
| 357 | + |
| 358 | + updatePinnedTimstampTask.run(); |
| 359 | + RemoteStoreSettings.setPinnedTimestampsLookbackInterval(TimeValue.ZERO); |
| 360 | + translog.trimUnreferencedReaders(); |
| 361 | + assertBusy(() -> assertTrue(translog.isRemoteGenerationDeletionPermitsAvailable())); |
| 362 | + |
| 363 | + Set<String> metadataFilesAfterTrim = blobStoreTransferService.listAll(getTranslogDirectory().add(METADATA_DIR)); |
| 364 | + Set<String> dataFilesAfterTrim = blobStoreTransferService.listAll( |
| 365 | + getTranslogDirectory().add(DATA_DIR).add(String.valueOf(primaryTerm.get())) |
| 366 | + ); |
| 367 | + |
| 368 | + // We check for number of pinned timestamp or +1 due to latest metadata. |
| 369 | + assertTrue( |
| 370 | + metadataFilesAfterTrim.size() == pinnedTimestamps.size() |
| 371 | + || metadataFilesAfterTrim.size() == pinnedTimestamps.size() + 1 |
| 372 | + ); |
| 373 | + |
| 374 | + for (String md : pinnedTimestampMatchingMetadataFiles) { |
| 375 | + assertTrue(metadataFilesAfterTrim.contains(md)); |
| 376 | + Tuple<Long, Long> minMaXGen = TranslogTransferMetadata.getMinMaxTranslogGenerationFromFilename(md); |
| 377 | + for (long i = minMaXGen.v1(); i <= minMaXGen.v2(); i++) { |
| 378 | + assertTrue(dataFilesAfterTrim.contains(Translog.getFilename(i))); |
| 379 | + } |
| 380 | + } |
| 381 | + } catch (Exception e) { |
| 382 | + fail(); |
| 383 | + } |
| 384 | + } |
| 385 | + |
| 386 | + @Override |
| 387 | + public void onFailure(Exception e) { |
| 388 | + fail(); |
| 389 | + } |
| 390 | + }, latch) |
| 391 | + ); |
| 392 | + |
| 393 | + latch.await(); |
| 394 | + } |
| 395 | + |
307 | 396 | @Override |
308 | 397 | public void testDrainSync() throws Exception { |
309 | 398 | RemoteStoreSettings.setPinnedTimestampsLookbackInterval(TimeValue.ZERO); |
@@ -452,10 +541,59 @@ ChannelFactory getChannelFactory() { |
452 | 541 | } |
453 | 542 | } |
454 | 543 |
|
455 | | - // getGenerationsToBeDeleted |
456 | | - public void testGetGenerationsToBeDeleted() { |
457 | | - // translog.readAndCacheGenerationForPinnedTimestamp |
458 | | - // translog.getGenerationsToBeDeleted |
| 544 | + public void testGetGenerationsToBeDeletedEmptyMetadataFilesNotToBeDeleted() throws IOException { |
| 545 | + List<String> metadataFilesNotToBeDeleted = new ArrayList<>(); |
| 546 | + List<String> metadataFilesToBeDeleted = List.of( |
| 547 | + // 4 to 7 |
| 548 | + "metadata__9223372036438563903__9223372036854775800__9223370311919910398__31__9223372036854775803__1", |
| 549 | + // 17 to 37 |
| 550 | + "metadata__9223372036438563903__9223372036854775770__9223370311919910398__31__9223372036854775790__1", |
| 551 | + // 27 to 42 |
| 552 | + "metadata__9223372036438563903__9223372036854775765__9223370311919910403__31__9223372036854775780__1" |
| 553 | + ); |
| 554 | + Set<Long> generations = translog.getGenerationsToBeDeleted(metadataFilesNotToBeDeleted, metadataFilesToBeDeleted, true); |
| 555 | + Set<Long> md1Generations = LongStream.rangeClosed(4, 7).boxed().collect(Collectors.toSet()); |
| 556 | + Set<Long> md2Generations = LongStream.rangeClosed(17, 37).boxed().collect(Collectors.toSet()); |
| 557 | + Set<Long> md3Generations = LongStream.rangeClosed(27, 42).boxed().collect(Collectors.toSet()); |
| 558 | + assertTrue(generations.containsAll(md1Generations)); |
| 559 | + assertTrue(generations.containsAll(md2Generations)); |
| 560 | + assertTrue(generations.containsAll(md3Generations)); |
| 561 | + |
| 562 | + generations.removeAll(md1Generations); |
| 563 | + generations.removeAll(md2Generations); |
| 564 | + generations.removeAll(md3Generations); |
| 565 | + assertTrue(generations.isEmpty()); |
| 566 | + } |
| 567 | + |
| 568 | + public void testGetGenerationsToBeDeleted() throws IOException { |
| 569 | + List<String> metadataFilesNotToBeDeleted = List.of( |
| 570 | + // 1 to 4 |
| 571 | + "metadata__9223372036438563903__9223372036854775803__9223370311919910398__31__9223372036854775806__1", |
| 572 | + // 26 to 30 |
| 573 | + "metadata__9223372036438563903__9223372036854775777__9223370311919910398__31__9223372036854775781__1", |
| 574 | + // 42 to 100 |
| 575 | + "metadata__9223372036438563903__9223372036854775707__9223370311919910403__31__9223372036854775765__1" |
| 576 | + ); |
| 577 | + List<String> metadataFilesToBeDeleted = List.of( |
| 578 | + // 4 to 7 |
| 579 | + "metadata__9223372036438563903__9223372036854775800__9223370311919910398__31__9223372036854775803__1", |
| 580 | + // 17 to 37 |
| 581 | + "metadata__9223372036438563903__9223372036854775770__9223370311919910398__31__9223372036854775790__1", |
| 582 | + // 27 to 42 |
| 583 | + "metadata__9223372036438563903__9223372036854775765__9223370311919910403__31__9223372036854775780__1" |
| 584 | + ); |
| 585 | + Set<Long> generations = translog.getGenerationsToBeDeleted(metadataFilesNotToBeDeleted, metadataFilesToBeDeleted, true); |
| 586 | + Set<Long> md1Generations = LongStream.rangeClosed(5, 7).boxed().collect(Collectors.toSet()); |
| 587 | + Set<Long> md2Generations = LongStream.rangeClosed(17, 25).boxed().collect(Collectors.toSet()); |
| 588 | + Set<Long> md3Generations = LongStream.rangeClosed(31, 41).boxed().collect(Collectors.toSet()); |
| 589 | + assertTrue(generations.containsAll(md1Generations)); |
| 590 | + assertTrue(generations.containsAll(md2Generations)); |
| 591 | + assertTrue(generations.containsAll(md3Generations)); |
| 592 | + |
| 593 | + generations.removeAll(md1Generations); |
| 594 | + generations.removeAll(md2Generations); |
| 595 | + generations.removeAll(md3Generations); |
| 596 | + assertTrue(generations.isEmpty()); |
459 | 597 | } |
460 | 598 |
|
461 | 599 | public void testGetMetadataFilesToBeDeletedNoExclusion() { |
|
0 commit comments