@@ -538,13 +538,63 @@ public void testExpireWithTime() throws Exception {
538538 expire .config (builder .snapshotTimeRetain (Duration .ofMillis (1000 )).build ()).expire ();
539539
540540 int latestSnapshotId = requireNonNull (snapshotManager .latestSnapshotId ()).intValue ();
541- for (int i = 1 ; i <= latestSnapshotId ; i ++) {
542- if (snapshotManager .snapshotExists (i )) {
543- assertThat (snapshotManager .snapshot (i ).timeMillis ())
544- .isBetween (expireMillis - 1000 , expireMillis );
545- assertSnapshot (i , allData , snapshotPositions );
546- }
541+ // snapshots 1-4 should be expired, snapshot 5 is retained because its next
542+ // snapshot (6) is within the time window
543+ for (int i = 1 ; i <= 4 ; i ++) {
544+ assertThat (snapshotManager .snapshotExists (i )).isFalse ();
545+ }
546+ for (int i = 5 ; i <= latestSnapshotId ; i ++) {
547+ assertThat (snapshotManager .snapshotExists (i )).isTrue ();
548+ assertSnapshot (i , allData , snapshotPositions );
549+ }
550+
551+ store .assertCleaned ();
552+ }
553+
554+ @ Test
555+ public void testExpireWithTimeProtectsEachSnapshot () throws Exception {
556+ // Even with a small retainMin, each snapshot should be protected by
557+ // snapshotTimeRetain: a snapshot can only be expired when its next
558+ // snapshot has been alive longer than snapshotTimeRetain.
559+ ExpireConfig .Builder builder = ExpireConfig .builder ();
560+ builder .snapshotRetainMin (1 )
561+ .snapshotRetainMax (Integer .MAX_VALUE )
562+ .snapshotTimeRetain (Duration .ofMillis (5000 ));
563+ ExpireSnapshots expire = store .newExpire (builder .build ());
564+
565+ List <KeyValue > allData = new ArrayList <>();
566+ List <Integer > snapshotPositions = new ArrayList <>();
567+
568+ // create 5 snapshots quickly
569+ commit (5 , allData , snapshotPositions );
570+
571+ // expire immediately - no snapshot should be expired because each
572+ // snapshot's next snapshot is still within the time window
573+ expire .config (builder .build ()).expire ();
574+
575+ for (int i = 1 ; i <= 5 ; i ++) {
576+ assertThat (snapshotManager .snapshotExists (i )).isTrue ();
577+ assertSnapshot (i , allData , snapshotPositions );
578+ }
579+
580+ // wait for snapshotTimeRetain to pass
581+ Thread .sleep (5500 );
582+
583+ // create one more snapshot so snapshot 5 has a "next"
584+ commit (1 , allData , snapshotPositions );
585+
586+ // expire again - now snapshots 1-4 can be expired (their next snapshots
587+ // are older than 5000ms), but snapshot 5 is still protected because its
588+ // next snapshot (6) was just created
589+ expire .config (builder .build ()).expire ();
590+
591+ for (int i = 1 ; i <= 4 ; i ++) {
592+ assertThat (snapshotManager .snapshotExists (i )).isFalse ();
547593 }
594+ assertThat (snapshotManager .snapshotExists (5 )).isTrue ();
595+ assertThat (snapshotManager .snapshotExists (6 )).isTrue ();
596+ assertSnapshot (5 , allData , snapshotPositions );
597+ assertSnapshot (6 , allData , snapshotPositions );
548598
549599 store .assertCleaned ();
550600 }
0 commit comments