Skip to content

Commit

Permalink
Improved datastore root and snapshot iteration caching
Browse files Browse the repository at this point in the history
  • Loading branch information
dimitribouniol committed Oct 10, 2024
1 parent 50830f9 commit 231a9fb
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ extension DiskPersistence.Datastore.Index {
case .secondary(let index, let manifest): self = .secondary(index: index, manifest: manifest)
}
}

init(_ id: DatastoreRootManifest.IndexManifestID) {
switch id {
case .primary(let manifest):
self = .primary(manifest: manifest)
case .direct(let index, let manifest):
self = .direct(index: index, manifest: manifest)
case .secondary(let index, let manifest):
self = .secondary(index: index, manifest: manifest)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,16 @@ extension DiskPersistence.Datastore {

extension DiskPersistence.Datastore {
/// Load the root object from disk for the given identifier.
func loadRootObject(for rootIdentifier: DatastoreRootIdentifier) throws -> DatastoreRootManifest {
func loadRootObject(for rootIdentifier: DatastoreRootIdentifier, shouldCache: Bool = true) throws -> DatastoreRootManifest {
let rootObjectURL = rootURL(for: rootIdentifier)

let data = try Data(contentsOf: rootObjectURL)

let root = try JSONDecoder.shared.decode(DatastoreRootManifest.self, from: data)

cachedRootObject = root
if shouldCache {
cachedRootObject = root
}
return root
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ extension DiskPersistence {
return snapshot
}

let snapshot = Snapshot(id: snapshotID, persistence: self)
let snapshot = Snapshot(id: snapshotID, persistence: self, isExtendedIterationCacheEnabled: !_transactionRetentionPolicy.isIndefinite)
snapshots[snapshotID] = snapshot

return snapshot
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ actor Snapshot<AccessMode: _AccessMode> {
/// A cached instance of the manifest as last loaded from disk.
var cachedManifest: SnapshotManifest?

/// A cached instance of the current iteration as last loaded from disk.
var cachedIteration: SnapshotIteration?
/// Cache for the loaded iterations as last loaded from disk. ``isExtendedIterationCacheEnabled`` controls if multiple iterations are cached or not.
var cachedIterations: [SnapshotIterationIdentifier : SnapshotIteration] = [:]
var isExtendedIterationCacheEnabled: Bool

/// A pointer to the last manifest updater, so updates can be serialized after the last request.
var lastUpdateManifestTask: Task<Sendable, Error>?
Expand All @@ -42,11 +43,13 @@ actor Snapshot<AccessMode: _AccessMode> {
init(
id: SnapshotIdentifier,
persistence: DiskPersistence<AccessMode>,
isBackup: Bool = false
isBackup: Bool = false,
isExtendedIterationCacheEnabled: Bool = false
) {
self.id = id
self.persistence = persistence
self.isBackup = isBackup
self.isExtendedIterationCacheEnabled = isExtendedIterationCacheEnabled
}
}

Expand Down Expand Up @@ -124,14 +127,25 @@ extension Snapshot {
}
}

func setExtendedIterationCacheEnabled(_ isEnabled: Bool) {
isExtendedIterationCacheEnabled = isEnabled
}

/// Load an iteration from disk, or create a suitable starting value if such a file does not exist.
private func loadIteration(for iterationID: SnapshotIterationIdentifier) throws -> SnapshotIteration {
func loadIteration(for iterationID: SnapshotIterationIdentifier?) async throws -> SnapshotIteration? {
guard let iterationID else { return nil }
if let iteration = cachedIterations[iterationID] {
return iteration
}
do {
let data = try Data(contentsOf: iterationURL(for: iterationID))

let iteration = try JSONDecoder.shared.decode(SnapshotIteration.self, from: data)

cachedIteration = iteration
if !isExtendedIterationCacheEnabled {
cachedIterations.removeAll()
}
cachedIterations[iteration.id] = iteration
return iteration
} catch {
throw error
Expand All @@ -155,7 +169,7 @@ extension Snapshot {
cachedManifest = manifest
}

/// Write the specified iteration to the store, and cache the results in ``Snapshot/cachedIteration``.
/// Write the specified iteration to the store, and cache the results in ``Snapshot/cachedIterations``.
private func write(iteration: SnapshotIteration) throws where AccessMode == ReadWrite {
let iterationURL = iterationURL(for: iteration.id)
/// Make sure the directories exists first.
Expand All @@ -166,7 +180,10 @@ extension Snapshot {
try data.write(to: iterationURL, options: .atomic)

/// Update the cache since we know what it should be.
cachedIteration = iteration
if !isExtendedIterationCacheEnabled {
cachedIterations.removeAll()
}
cachedIterations[iteration.id] = iteration
}

/// Load and update the manifest in an updater, returning the task for the updater.
Expand Down Expand Up @@ -200,26 +217,19 @@ extension Snapshot {

/// Load the manifest so we have a fresh copy, unless we have a cached copy already.
var manifest = try cachedManifest ?? self.loadManifest()
var iteration: SnapshotIteration
if let cachedIteration, cachedIteration.id == manifest.currentIteration {
iteration = cachedIteration
} else if let iterationID = manifest.currentIteration {
iteration = try self.loadIteration(for: iterationID)
} else {
let date = Date()
iteration = SnapshotIteration(id: SnapshotIterationIdentifier(date: date), creationDate: date)
}
let precedingIteration = try await self.loadIteration(for: manifest.currentIteration)
var iteration = precedingIteration ?? SnapshotIteration()

/// Let the updater do something with the manifest, storing the variable on the Task Local stack.
let returnValue = try await SnapshotTaskLocals.$manifest.withValue((manifest, iteration)) {
try await updater(&manifest, &iteration)
}

/// Only write to the store if we changed the manifest for any reason
if iteration.isMeaningfullyChanged(from: cachedIteration) {
if iteration.isMeaningfullyChanged(from: precedingIteration) {
iteration.creationDate = Date()
iteration.id = SnapshotIterationIdentifier(date: iteration.creationDate)
iteration.precedingIteration = cachedIteration?.id
iteration.precedingIteration = precedingIteration?.id

try write(iteration: iteration)
}
Expand Down Expand Up @@ -260,15 +270,7 @@ extension Snapshot {

/// Load the manifest so we have a fresh copy, unless we have a cached copy already.
let manifest = try cachedManifest ?? self.loadManifest()
var iteration: SnapshotIteration
if let cachedIteration, cachedIteration.id == manifest.currentIteration {
iteration = cachedIteration
} else if let iterationID = manifest.currentIteration {
iteration = try self.loadIteration(for: iterationID)
} else {
let date = Date()
iteration = SnapshotIteration(id: SnapshotIterationIdentifier(date: date), creationDate: date)
}
let iteration = try await self.loadIteration(for: manifest.currentIteration) ?? SnapshotIteration()

/// Let the accessor do something with the manifest, storing the variable on the Task Local stack.
return try await SnapshotTaskLocals.$manifest.withValue((manifest, iteration)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ extension SnapshotIteration {
}

extension SnapshotIteration {
/// Initialize a snapshot iteration with a date
/// - Parameter date: The date to base the identifier and creation date off of.
init(date: Date = Date()) {
self.init(id: SnapshotIterationIdentifier(date: date), creationDate: date)
}

/// Internal method to check if an instance should be persisted based on iff it changed significantly from a previous iteration
/// - Parameter existingInstance: The previous iteration to check
/// - Returns: `true` if the iteration should be persisted, `false` if it represents the same data from `existingInstance`.
Expand Down

0 comments on commit 231a9fb

Please sign in to comment.