Skip to content

Commit c11c9f4

Browse files
Updated datastore root mutations to maintain change records for historical tracking
1 parent f25f924 commit c11c9f4

File tree

5 files changed

+142
-17
lines changed

5 files changed

+142
-17
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,12 @@ targets: [
4545
`CodableDatastore` is a collection of types that make it easy to interface with large data stores of independent types without loading the entire data store in memory.
4646

4747
> **Warning**
48-
> DO NOT USE THIS IN PRODOCUTION PROJECTS. As this project is currently still in its alpha phase, I cannot stress how important it is to not ship anything that relies on this code, or you will experience data loss. There is a chance the underlying model may continue to change day to day, or I will not be able to ever finish it.
48+
> DO NOT USE THIS IN PRODUCTION PROJECTS. As this project is currently still in its alpha phase, I cannot stress how important it is to not ship anything that relies on this code, or you will experience data loss. There is a chance the underlying model may continue to change day to day, or I will not be able to ever finish it.
4949
> Until then, please enjoy the code as a spectator or play around with it in toy projects to submit feedback!
5050
5151
### Road to 0.1 Betas
5252

5353
As this project matures towards its first beta, a number of features still need to be fleshed out:
54-
- Fleshing out historical edit metadata
5554
- Migrating entries
5655

5756
The above list will be kept up to date during development and will likely see additions during that process.

Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastoreRoot.swift

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ extension DiskPersistence.Datastore.RootObject {
155155

156156
var createdIndexes: Set<DiskPersistence.Datastore.Index> = []
157157

158+
if isPersisted {
159+
manifest.removedIndexes = []
160+
manifest.removedIndexManifests = []
161+
manifest.addedIndexes = []
162+
manifest.addedIndexManifests = []
163+
}
164+
158165
for (_, indexDescriptor) in descriptor.directIndexes {
159166
let key = indexDescriptor.key
160167
let indexType = indexDescriptor.indexType
@@ -177,6 +184,9 @@ extension DiskPersistence.Datastore.RootObject {
177184
)
178185
)
179186
createdIndexes.insert(index)
187+
manifest.addedIndexes.insert(.direct(index: indexInfo.id))
188+
manifest.addedIndexManifests.insert(.direct(index: indexInfo.id, manifest: indexInfo.root))
189+
manifest.directIndexManifests.append(indexInfo)
180190
}
181191

182192
manifest.descriptor.directIndexes[key] = DatastoreDescriptor.IndexDescriptor(
@@ -208,6 +218,9 @@ extension DiskPersistence.Datastore.RootObject {
208218
)
209219
)
210220
createdIndexes.insert(index)
221+
manifest.addedIndexes.insert(.secondary(index: indexInfo.id))
222+
manifest.addedIndexManifests.insert(.secondary(index: indexInfo.id, manifest: indexInfo.root))
223+
manifest.secondaryIndexManifests.append(indexInfo)
211224
}
212225

213226
manifest.descriptor.secondaryIndexes[key] = DatastoreDescriptor.IndexDescriptor(
@@ -218,10 +231,12 @@ extension DiskPersistence.Datastore.RootObject {
218231
}
219232

220233
if originalManifest.descriptor != manifest.descriptor {
221-
manifest.id = DatastoreRootIdentifier()
222-
manifest.modificationDate = Date()
234+
let modificationDate = Date()
235+
manifest.id = DatastoreRootIdentifier(date: modificationDate)
236+
manifest.modificationDate = modificationDate
237+
return (manifest: manifest, createdIndexes: createdIndexes)
223238
}
224-
return (manifest: manifest, createdIndexes: createdIndexes)
239+
return (manifest: originalManifest, createdIndexes: createdIndexes)
225240
}
226241

227242
func manifest(
@@ -230,30 +245,62 @@ extension DiskPersistence.Datastore.RootObject {
230245
let manifest = try await manifest
231246
var updatedManifest = manifest
232247

248+
var removedIndex: DatastoreRootManifest.IndexManifestID?
249+
var addedIndex: DatastoreRootManifest.IndexManifestID
250+
233251
switch index {
234252
case .primary(let manifestID):
253+
removedIndex = .primary(manifest: updatedManifest.primaryIndexManifest)
254+
addedIndex = .primary(manifest: manifestID)
235255
updatedManifest.primaryIndexManifest = manifestID
236256
case .direct(let indexID, let manifestID):
257+
var oldRoot: DatastoreIndexManifestIdentifier?
237258
updatedManifest.directIndexManifests = manifest.directIndexManifests.map { indexInfo in
238259
var indexInfo = indexInfo
239260
if indexInfo.id == indexID {
261+
oldRoot = indexInfo.root
240262
indexInfo.root = manifestID
241263
}
242264
return indexInfo
243265
}
266+
267+
removedIndex = oldRoot.map { .direct(index: indexID, manifest: $0) }
268+
addedIndex = .direct(index: indexID, manifest: manifestID)
244269
case .secondary(let indexID, let manifestID):
270+
var oldRoot: DatastoreIndexManifestIdentifier?
245271
updatedManifest.secondaryIndexManifests = updatedManifest.secondaryIndexManifests.map { indexInfo in
246272
var indexInfo = indexInfo
247273
if indexInfo.id == indexID {
274+
oldRoot = indexInfo.root
248275
indexInfo.root = manifestID
249276
}
250277
return indexInfo
251278
}
279+
280+
removedIndex = oldRoot.map { .secondary(index: indexID, manifest: $0) }
281+
addedIndex = .secondary(index: indexID, manifest: manifestID)
252282
}
253283

254284
if manifest != updatedManifest {
255-
updatedManifest.id = DatastoreRootIdentifier()
256-
updatedManifest.modificationDate = Date()
285+
let modificationDate = Date()
286+
updatedManifest.id = DatastoreRootIdentifier(date: modificationDate)
287+
updatedManifest.modificationDate = modificationDate
288+
289+
if isPersisted {
290+
updatedManifest.removedIndexes = []
291+
updatedManifest.removedIndexManifests = []
292+
updatedManifest.addedIndexes = []
293+
updatedManifest.addedIndexManifests = []
294+
}
295+
296+
if let removedIndex {
297+
if updatedManifest.addedIndexManifests.contains(removedIndex) {
298+
updatedManifest.addedIndexManifests.remove(removedIndex)
299+
} else {
300+
updatedManifest.removedIndexManifests.insert(removedIndex)
301+
}
302+
}
303+
updatedManifest.addedIndexManifests.insert(addedIndex)
257304
}
258305
return updatedManifest
259306
}

Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastoreRootManifest.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ struct DatastoreRootManifest: Codable, Equatable, Identifiable {
3636

3737
/// A pointer to the secondary indexes' root objects.
3838
var secondaryIndexManifests: [IndexInfo] = []
39+
40+
/// The indexes that have been added in this iteration of the snapshot.
41+
var addedIndexes: Set<IndexID> = []
42+
43+
/// The indexes that have been completely removed in this iteration of the snapshot.
44+
var removedIndexes: Set<IndexID> = []
45+
46+
/// The datastore roots that have been added in this iteration of the snapshot.
47+
var addedIndexManifests: Set<IndexManifestID> = []
48+
49+
/// The datastore roots that have been replaced in this iteration of the snapshot.
50+
var removedIndexManifests: Set<IndexManifestID> = []
3951
}
4052

4153
extension DatastoreRootManifest {
@@ -49,4 +61,27 @@ extension DatastoreRootManifest {
4961
/// The root object of the index.
5062
var root: DatastoreIndexManifestIdentifier
5163
}
64+
65+
enum IndexID: Codable, Hashable {
66+
case primary
67+
case direct(index: DatastoreIndexIdentifier)
68+
case secondary(index: DatastoreIndexIdentifier)
69+
}
70+
71+
enum IndexManifestID: Codable, Hashable {
72+
case primary(manifest: DatastoreIndexManifestIdentifier)
73+
case direct(index: DatastoreIndexIdentifier, manifest: DatastoreIndexManifestIdentifier)
74+
case secondary(index: DatastoreIndexIdentifier, manifest: DatastoreIndexManifestIdentifier)
75+
76+
init<AccessMode>(_ id: DiskPersistence<AccessMode>.Datastore.Index.ID) {
77+
switch id {
78+
case .primary(let manifest):
79+
self = .primary(manifest: manifest)
80+
case .direct(let index, let manifest):
81+
self = .direct(index: index, manifest: manifest)
82+
case .secondary(let index, let manifest):
83+
self = .secondary(index: index, manifest: manifest)
84+
}
85+
}
86+
}
5287
}

Sources/CodableDatastore/Persistence/Disk Persistence/DiskPersistence.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,9 @@ extension DiskPersistence {
462462

463463
func persist(
464464
actionName: String?,
465-
roots: [DatastoreKey : Datastore.RootObject]
465+
roots: [DatastoreKey : Datastore.RootObject],
466+
addedDatastoreRoots: Set<DatastoreRootIdentifier>,
467+
removedDatastoreRoots: Set<DatastoreRootIdentifier>
466468
) async throws {
467469
let containsEdits = try await withCurrentSnapshot { snapshot in
468470
try await snapshot.readingManifest { manifest, iteration in
@@ -485,6 +487,8 @@ extension DiskPersistence {
485487
try await self.withCurrentSnapshot { snapshot in
486488
try await snapshot.updatingManifest { manifest, iteration in
487489
iteration.actionName = actionName
490+
iteration.addedDatastoreRoots = addedDatastoreRoots
491+
iteration.removedDatastoreRoots = removedDatastoreRoots
488492
for (key, root) in roots {
489493
iteration.dataStores[key.rawValue] = SnapshotIteration.DatastoreInfo(
490494
key: key,

Sources/CodableDatastore/Persistence/Disk Persistence/Transaction/Transaction.swift

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,15 @@ extension DiskPersistence {
150150
try await root.persistIfNeeded()
151151
}
152152

153-
try await persistence.persist(actionName: actionName, roots: rootObjects)
153+
let addedDatastoreRoots = Set(createdRootObjects.map(\.id))
154+
let removedDatastoreRoots = Set(deletedRootObjects.map(\.id))
155+
156+
try await persistence.persist(
157+
actionName: actionName,
158+
roots: rootObjects,
159+
addedDatastoreRoots: addedDatastoreRoots,
160+
removedDatastoreRoots: removedDatastoreRoots
161+
)
154162

155163
var datastores: [DatastoreKey : Datastore] = [:]
156164
for (datastoreKey, event) in entryMutations {
@@ -324,7 +332,11 @@ extension DiskPersistence.Transaction: DatastoreInterfaceProtocol {
324332
rootObject: rootManifest
325333
)
326334
createdRootObjects.insert(newRootObject)
327-
deletedRootObjects.insert(existingRootObject)
335+
if createdRootObjects.contains(existingRootObject) {
336+
createdRootObjects.remove(existingRootObject)
337+
} else {
338+
deletedRootObjects.insert(existingRootObject)
339+
}
328340
await datastore.adopt(rootObject: newRootObject)
329341
rootObjects[datastoreKey] = newRootObject
330342
} else {
@@ -360,6 +372,11 @@ extension DiskPersistence.Transaction: DatastoreInterfaceProtocol {
360372
createdIndexes.insert(primaryIndex)
361373
await datastore.adopt(index: primaryIndex)
362374

375+
var addedIndexes: Set<DatastoreRootManifest.IndexID> = []
376+
var addedIndexManifests: Set<DatastoreRootManifest.IndexManifestID> = []
377+
addedIndexes.insert(.primary)
378+
addedIndexManifests.insert(.primary(manifest: primaryManifestIdentifier))
379+
363380
for indexInfo in directIndexManifests {
364381
let index = DiskPersistence.Datastore.Index(
365382
datastore: datastore,
@@ -370,6 +387,8 @@ extension DiskPersistence.Transaction: DatastoreInterfaceProtocol {
370387
)
371388
)
372389
createdIndexes.insert(index)
390+
addedIndexes.insert(.direct(index: indexInfo.id))
391+
addedIndexManifests.insert(.direct(index: indexInfo.id, manifest: indexInfo.root))
373392
await datastore.adopt(index: index)
374393
}
375394

@@ -383,20 +402,25 @@ extension DiskPersistence.Transaction: DatastoreInterfaceProtocol {
383402
)
384403
)
385404
createdIndexes.insert(index)
405+
addedIndexes.insert(.secondary(index: indexInfo.id))
406+
addedIndexManifests.insert(.secondary(index: indexInfo.id, manifest: indexInfo.root))
386407
await datastore.adopt(index: index)
387408
}
388409

389410
var descriptor = descriptor
390411
descriptor.size = 0
391412

413+
let modificationDate = Date()
392414
/// Create the root object from the indexes that were created
393415
let manifest = DatastoreRootManifest(
394-
id: DatastoreRootIdentifier(),
395-
modificationDate: Date(),
416+
id: DatastoreRootIdentifier(date: modificationDate),
417+
modificationDate: modificationDate,
396418
descriptor: descriptor,
397419
primaryIndexManifest: primaryManifestIdentifier,
398420
directIndexManifests: directIndexManifests,
399-
secondaryIndexManifests: secondaryIndexManifests
421+
secondaryIndexManifests: secondaryIndexManifests,
422+
addedIndexes: addedIndexes,
423+
addedIndexManifests: addedIndexManifests
400424
)
401425

402426
let newRoot = DiskPersistence.Datastore.RootObject(
@@ -823,7 +847,11 @@ extension DiskPersistence.Transaction {
823847
manifest: indexManifest
824848
)
825849
createdIndexes.insert(newIndex)
826-
deletedIndexes.insert(existingIndex)
850+
if createdIndexes.contains(existingIndex) {
851+
createdIndexes.insert(existingIndex)
852+
} else {
853+
deletedIndexes.insert(existingIndex)
854+
}
827855
await datastore.adopt(index: newIndex)
828856

829857
var rootManifest = try await existingRootObject.manifest(replacing: newIndex.id)
@@ -842,7 +870,11 @@ extension DiskPersistence.Transaction {
842870
rootObject: rootManifest
843871
)
844872
createdRootObjects.insert(newRootObject)
845-
deletedRootObjects.insert(existingRootObject)
873+
if createdRootObjects.contains(existingRootObject) {
874+
createdRootObjects.remove(existingRootObject)
875+
} else {
876+
deletedRootObjects.insert(existingRootObject)
877+
}
846878
await datastore.adopt(rootObject: newRootObject)
847879
rootObjects[datastoreKey] = newRootObject
848880
}
@@ -907,7 +939,11 @@ extension DiskPersistence.Transaction {
907939
manifest: indexManifest
908940
)
909941
createdIndexes.insert(newIndex)
910-
deletedIndexes.insert(existingIndex)
942+
if createdIndexes.contains(existingIndex) {
943+
createdIndexes.insert(existingIndex)
944+
} else {
945+
deletedIndexes.insert(existingIndex)
946+
}
911947
await datastore.adopt(index: newIndex)
912948

913949
var rootManifest = try await existingRootObject.manifest(replacing: newIndex.id)
@@ -926,7 +962,11 @@ extension DiskPersistence.Transaction {
926962
rootObject: rootManifest
927963
)
928964
createdRootObjects.insert(newRootObject)
929-
deletedRootObjects.insert(existingRootObject)
965+
if createdRootObjects.contains(existingRootObject) {
966+
createdRootObjects.remove(existingRootObject)
967+
} else {
968+
deletedRootObjects.insert(existingRootObject)
969+
}
930970
await datastore.adopt(rootObject: newRootObject)
931971
rootObjects[datastoreKey] = newRootObject
932972
}

0 commit comments

Comments
 (0)