@@ -30,8 +30,9 @@ actor Snapshot<AccessMode: _AccessMode> {
3030 /// A cached instance of the manifest as last loaded from disk.
3131 var cachedManifest : SnapshotManifest ?
3232
33- /// A cached instance of the current iteration as last loaded from disk.
34- var cachedIteration : SnapshotIteration ?
33+ /// Cache for the loaded iterations as last loaded from disk. ``isExtendedIterationCacheEnabled`` controls if multiple iterations are cached or not.
34+ var cachedIterations : [ SnapshotIterationIdentifier : SnapshotIteration ] = [ : ]
35+ var isExtendedIterationCacheEnabled : Bool
3536
3637 /// A pointer to the last manifest updater, so updates can be serialized after the last request.
3738 var lastUpdateManifestTask : Task < Sendable , Error > ?
@@ -42,11 +43,13 @@ actor Snapshot<AccessMode: _AccessMode> {
4243 init (
4344 id: SnapshotIdentifier ,
4445 persistence: DiskPersistence < AccessMode > ,
45- isBackup: Bool = false
46+ isBackup: Bool = false ,
47+ isExtendedIterationCacheEnabled: Bool = false
4648 ) {
4749 self . id = id
4850 self . persistence = persistence
4951 self . isBackup = isBackup
52+ self . isExtendedIterationCacheEnabled = isExtendedIterationCacheEnabled
5053 }
5154}
5255
@@ -124,14 +127,25 @@ extension Snapshot {
124127 }
125128 }
126129
130+ func setExtendedIterationCacheEnabled( _ isEnabled: Bool ) {
131+ isExtendedIterationCacheEnabled = isEnabled
132+ }
133+
127134 /// Load an iteration from disk, or create a suitable starting value if such a file does not exist.
128- private func loadIteration( for iterationID: SnapshotIterationIdentifier ) throws -> SnapshotIteration {
135+ func loadIteration( for iterationID: SnapshotIterationIdentifier ? ) async throws -> SnapshotIteration ? {
136+ guard let iterationID else { return nil }
137+ if let iteration = cachedIterations [ iterationID] {
138+ return iteration
139+ }
129140 do {
130141 let data = try Data ( contentsOf: iterationURL ( for: iterationID) )
131142
132143 let iteration = try JSONDecoder . shared. decode ( SnapshotIteration . self, from: data)
133144
134- cachedIteration = iteration
145+ if !isExtendedIterationCacheEnabled {
146+ cachedIterations. removeAll ( )
147+ }
148+ cachedIterations [ iteration. id] = iteration
135149 return iteration
136150 } catch {
137151 throw error
@@ -155,7 +169,7 @@ extension Snapshot {
155169 cachedManifest = manifest
156170 }
157171
158- /// Write the specified iteration to the store, and cache the results in ``Snapshot/cachedIteration ``.
172+ /// Write the specified iteration to the store, and cache the results in ``Snapshot/cachedIterations ``.
159173 private func write( iteration: SnapshotIteration ) throws where AccessMode == ReadWrite {
160174 let iterationURL = iterationURL ( for: iteration. id)
161175 /// Make sure the directories exists first.
@@ -166,7 +180,10 @@ extension Snapshot {
166180 try data. write ( to: iterationURL, options: . atomic)
167181
168182 /// Update the cache since we know what it should be.
169- cachedIteration = iteration
183+ if !isExtendedIterationCacheEnabled {
184+ cachedIterations. removeAll ( )
185+ }
186+ cachedIterations [ iteration. id] = iteration
170187 }
171188
172189 /// Load and update the manifest in an updater, returning the task for the updater.
@@ -200,26 +217,19 @@ extension Snapshot {
200217
201218 /// Load the manifest so we have a fresh copy, unless we have a cached copy already.
202219 var manifest = try cachedManifest ?? self . loadManifest ( )
203- var iteration : SnapshotIteration
204- if let cachedIteration, cachedIteration. id == manifest. currentIteration {
205- iteration = cachedIteration
206- } else if let iterationID = manifest. currentIteration {
207- iteration = try self . loadIteration ( for: iterationID)
208- } else {
209- let date = Date ( )
210- iteration = SnapshotIteration ( id: SnapshotIterationIdentifier ( date: date) , creationDate: date)
211- }
220+ let precedingIteration = try await self . loadIteration ( for: manifest. currentIteration)
221+ var iteration = precedingIteration ?? SnapshotIteration ( )
212222
213223 /// Let the updater do something with the manifest, storing the variable on the Task Local stack.
214224 let returnValue = try await SnapshotTaskLocals . $manifest. withValue ( ( manifest, iteration) ) {
215225 try await updater ( & manifest, & iteration)
216226 }
217227
218228 /// Only write to the store if we changed the manifest for any reason
219- if iteration. isMeaningfullyChanged ( from: cachedIteration ) {
229+ if iteration. isMeaningfullyChanged ( from: precedingIteration ) {
220230 iteration. creationDate = Date ( )
221231 iteration. id = SnapshotIterationIdentifier ( date: iteration. creationDate)
222- iteration. precedingIteration = cachedIteration ? . id
232+ iteration. precedingIteration = precedingIteration ? . id
223233
224234 try write ( iteration: iteration)
225235 }
@@ -260,15 +270,7 @@ extension Snapshot {
260270
261271 /// Load the manifest so we have a fresh copy, unless we have a cached copy already.
262272 let manifest = try cachedManifest ?? self . loadManifest ( )
263- var iteration : SnapshotIteration
264- if let cachedIteration, cachedIteration. id == manifest. currentIteration {
265- iteration = cachedIteration
266- } else if let iterationID = manifest. currentIteration {
267- iteration = try self . loadIteration ( for: iterationID)
268- } else {
269- let date = Date ( )
270- iteration = SnapshotIteration ( id: SnapshotIterationIdentifier ( date: date) , creationDate: date)
271- }
273+ let iteration = try await self . loadIteration ( for: manifest. currentIteration) ?? SnapshotIteration ( )
272274
273275 /// Let the accessor do something with the manifest, storing the variable on the Task Local stack.
274276 return try await SnapshotTaskLocals . $manifest. withValue ( ( manifest, iteration) ) {
0 commit comments