@@ -682,6 +682,119 @@ describe('VmBackupsCache collection', () => {
682682 assert . equal ( events [ 1 ] . payload [ 0 ] . size , 42 )
683683 } )
684684
685+ it ( 'reads the journal again when refresh() is called while it is being replayed' , async t => {
686+ const { tick } = mockTime ( t , Date . parse ( '2026-08-11T10:00:00Z' ) )
687+ const repository = new Repository ( [ metadataOf ( VM , '20260811T090000' ) ] )
688+ const source = repository . source
689+ const cache = new VmBackupsCache ( source , { minRefreshDelay : 60e3 } )
690+
691+ await cache . get ( REPOSITORY )
692+ const events = recordEvents ( cache )
693+ tick ( 60e3 )
694+
695+ const held = holdNextRead ( source , 'readJournal' )
696+ const pending = cache . get ( REPOSITORY )
697+ await held . read
698+
699+ // written after the journal has been read, e.g. by the next VM of a backup job
700+ const added = metadataOf ( OTHER_VM , '20260811T100000' )
701+ repository . add ( added , Date . now ( ) )
702+ cache . refresh ( REPOSITORY . id )
703+ held . release ( )
704+
705+ assert . deepEqual ( filenames ( await pending ) . sort ( ) , [ filenameOf ( VM , '20260811T090000' ) , added . _filename ] . sort ( ) )
706+ assert . equal ( repository . nJournalReads , 2 )
707+ assert . deepEqual ( summarize ( events ) , [ { event : 'add' , ids : [ archiveIdOf ( OTHER_VM , '20260811T100000' ) ] } ] )
708+ } )
709+
710+ it ( 'reads the journal again for every refresh() called while it is being read again' , async t => {
711+ const { tick } = mockTime ( t , Date . parse ( '2026-08-11T10:00:00Z' ) )
712+ const repository = new Repository ( [ metadataOf ( VM , '20260811T090000' ) ] )
713+ const source = repository . source
714+ const cache = new VmBackupsCache ( source , { minRefreshDelay : 60e3 } )
715+
716+ await cache . get ( REPOSITORY )
717+ tick ( 60e3 )
718+
719+ const firstRead = holdNextRead ( source , 'readJournal' )
720+ const pending = cache . get ( REPOSITORY )
721+ await firstRead . read
722+
723+ const added = metadataOf ( OTHER_VM , '20260811T100000' )
724+ repository . add ( added , Date . now ( ) )
725+ cache . refresh ( REPOSITORY . id )
726+ const secondRead = holdNextRead ( source , 'readJournal' )
727+ firstRead . release ( )
728+ await secondRead . read
729+
730+ // e.g. the VM after it in the same backup job
731+ const addedLater = metadataOf ( VM , '20260811T100000' )
732+ repository . add ( addedLater , Date . now ( ) )
733+ cache . refresh ( REPOSITORY . id )
734+ secondRead . release ( )
735+
736+ assert . deepEqual (
737+ filenames ( await pending ) . sort ( ) ,
738+ [ filenameOf ( VM , '20260811T090000' ) , added . _filename , addedLater . _filename ] . sort ( )
739+ )
740+ assert . equal ( repository . nJournalReads , 3 )
741+ } )
742+
743+ it ( 'reads the journal when refresh() is called while the repository is being listed' , async t => {
744+ mockTime ( t , Date . parse ( '2026-08-11T10:00:00Z' ) )
745+ const repository = new Repository ( [ metadataOf ( VM , '20260811T090000' ) ] )
746+ const source = repository . source
747+ const cache = new VmBackupsCache ( source , { minRefreshDelay : 60e3 } )
748+ const events = recordEvents ( cache )
749+
750+ const held = holdNextRead ( source , 'listAll' )
751+ const pending = cache . get ( REPOSITORY )
752+ await held . read
753+
754+ const added = metadataOf ( OTHER_VM , '20260811T100000' )
755+ repository . add ( added , Date . now ( ) )
756+ cache . refresh ( REPOSITORY . id )
757+ held . release ( )
758+
759+ assert . deepEqual ( filenames ( await pending ) . sort ( ) , [ filenameOf ( VM , '20260811T090000' ) , added . _filename ] . sort ( ) )
760+ assert . equal ( repository . nListings , 1 )
761+ assert . equal ( repository . nJournalReads , 1 )
762+ assert . deepEqual ( summarize ( events ) , [
763+ { event : 'add' , ids : [ archiveIdOf ( VM , '20260811T090000' ) ] } ,
764+ { event : 'add' , ids : [ archiveIdOf ( OTHER_VM , '20260811T100000' ) ] } ,
765+ ] )
766+ } )
767+
768+ it ( 'leaves a refresh() called while a repository is listed in full to the next read' , async t => {
769+ const { tick } = mockTime ( t , Date . parse ( '2026-08-11T10:00:00Z' ) )
770+ const repository = new Repository ( [ metadataOf ( VM , '20260811T090000' ) ] )
771+ repository . cannotReplay = true
772+ const source = repository . source
773+ const cache = new VmBackupsCache ( source , { minRefreshDelay : 60e3 } )
774+
775+ // the cache only finds out on the first replay
776+ await cache . get ( REPOSITORY )
777+ tick ( 60e3 )
778+ await cache . get ( REPOSITORY )
779+ tick ( 60e3 )
780+
781+ const held = holdNextRead ( source , 'listAll' )
782+ const pending = cache . get ( REPOSITORY )
783+ await held . read
784+
785+ const added = metadataOf ( OTHER_VM , '20260811T100000' )
786+ repository . add ( added , Date . now ( ) )
787+ cache . refresh ( REPOSITORY . id )
788+ held . release ( )
789+
790+ assert . deepEqual ( filenames ( await pending ) , [ filenameOf ( VM , '20260811T090000' ) ] )
791+ assert . equal ( repository . nListings , 3 )
792+
793+ // still stale: listed again without waiting for the end of the refresh window
794+ assert . ok ( filenames ( await cache . get ( REPOSITORY ) ) . includes ( added . _filename ) )
795+ assert . equal ( repository . nListings , 4 )
796+ } )
797+
685798 it ( 'announces nothing when a journal event changed nothing' , async t => {
686799 const { tick } = mockTime ( t , Date . parse ( '2026-08-11T10:00:00Z' ) )
687800 const metadata = metadataOf ( VM , '20260811T090000' )
0 commit comments