Skip to content

Commit fe260ab

Browse files
Fixed an issue where migrations involving reference indexes could fail
1 parent 10889c9 commit fe260ab

File tree

1 file changed

+131
-137
lines changed

1 file changed

+131
-137
lines changed

Sources/CodableDatastore/Datastore/Datastore.swift

Lines changed: 131 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -190,161 +190,155 @@ extension Datastore {
190190
handler(.evaluating)
191191
}
192192

193-
var newDescriptor: DatastoreDescriptor?
194-
195-
let primaryIndex = _load(IndexRange(), order: .ascending, awaitWarmup: false)
193+
/// Grab an up-to-date descriptor and check the indexes against it
194+
let updatedDescriptor = try generateUpdatedDescriptor()
196195

197196
var rebuildPrimaryIndex = false
198197
var directIndexesToBuild: Set<IndexName> = []
199198
var secondaryIndexesToBuild: Set<IndexName> = []
200199
var index = 0
201200

202-
let versionData = try Data(self.version)
201+
/// Check the primary index for compatibility.
202+
if persistedDescriptor.identifierType != updatedDescriptor.identifierType {
203+
try await transaction.resetPrimaryIndex(datastoreKey: key)
204+
rebuildPrimaryIndex = true
205+
}
203206

204-
for try await (idenfifier, instance) in primaryIndex {
205-
defer { index += 1 }
206-
/// Use the first index to grab an up-to-date descriptor
207-
if newDescriptor == nil {
208-
let updatedDescriptor = try generateUpdatedDescriptor()
209-
newDescriptor = updatedDescriptor
210-
211-
/// Check the primary index for compatibility.
212-
if persistedDescriptor.identifierType != updatedDescriptor.identifierType {
213-
try await transaction.resetPrimaryIndex(datastoreKey: key)
214-
rebuildPrimaryIndex = true
207+
/// Check existing direct indexes for compatibility
208+
for (_, persistedIndex) in persistedDescriptor.directIndexes {
209+
if let updatedIndex = updatedDescriptor.directIndexes[persistedIndex.name] {
210+
/// If the index still exists, make sure it is compatible by checking their types, or checking if the primary index must be re-built.
211+
if persistedIndex.type != updatedIndex.type || rebuildPrimaryIndex {
212+
/// They were not compatible, so delete the bad index, and queue it to be re-built.
213+
try await transaction.deleteDirectIndex(indexName: persistedIndex.name, datastoreKey: key)
214+
directIndexesToBuild.insert(persistedIndex.name)
215215
}
216-
217-
/// Check existing direct indexes for compatibility
218-
for (_, persistedIndex) in persistedDescriptor.directIndexes {
219-
if let updatedIndex = updatedDescriptor.directIndexes[persistedIndex.name] {
220-
/// If the index still exists, make sure it is compatible by checking their types, or checking if the primary index must be re-built.
221-
if persistedIndex.type != updatedIndex.type || rebuildPrimaryIndex {
222-
/// They were not compatible, so delete the bad index, and queue it to be re-built.
223-
try await transaction.deleteDirectIndex(indexName: persistedIndex.name, datastoreKey: key)
224-
directIndexesToBuild.insert(persistedIndex.name)
225-
}
226-
} else {
227-
/// The index is no longer needed, delete it.
228-
try await transaction.deleteDirectIndex(indexName: persistedIndex.name, datastoreKey: key)
229-
}
230-
}
231-
232-
/// Check for new direct indexes to build
233-
for (_, updatedIndex) in updatedDescriptor.directIndexes {
234-
guard persistedDescriptor.directIndexes[updatedIndex.name] == nil else { continue }
235-
/// The index does not yet exist, so queue it to be built.
236-
directIndexesToBuild.insert(updatedIndex.name)
237-
}
238-
239-
/// Check existing secondary indexes for compatibility
240-
for (_, persistedIndex) in persistedDescriptor.referenceIndexes {
241-
if let updatedIndex = updatedDescriptor.referenceIndexes[persistedIndex.name] {
242-
/// If the index still exists, make sure it is compatible
243-
if persistedIndex.type != updatedIndex.type {
244-
/// They were not compatible, so delete the bad index, and queue it to be re-built.
245-
try await transaction.deleteDirectIndex(indexName: persistedIndex.name, datastoreKey: key)
246-
secondaryIndexesToBuild.insert(persistedIndex.name)
247-
}
248-
} else {
249-
/// The index is no longer needed, delete it.
250-
try await transaction.deleteDirectIndex(indexName: persistedIndex.name, datastoreKey: key)
251-
}
216+
} else {
217+
/// The index is no longer needed, delete it.
218+
try await transaction.deleteDirectIndex(indexName: persistedIndex.name, datastoreKey: key)
219+
}
220+
}
221+
222+
/// Check for new direct indexes to build
223+
for (_, updatedIndex) in updatedDescriptor.directIndexes {
224+
guard persistedDescriptor.directIndexes[updatedIndex.name] == nil else { continue }
225+
/// The index does not yet exist, so queue it to be built.
226+
directIndexesToBuild.insert(updatedIndex.name)
227+
}
228+
229+
/// Check existing reference indexes for compatibility
230+
for (_, persistedIndex) in persistedDescriptor.referenceIndexes {
231+
if let updatedIndex = updatedDescriptor.referenceIndexes[persistedIndex.name] {
232+
/// If the index still exists, make sure it is compatible
233+
if persistedIndex.type != updatedIndex.type {
234+
/// They were not compatible, so delete the bad index, and queue it to be re-built.
235+
try await transaction.deleteSecondaryIndex(indexName: persistedIndex.name, datastoreKey: key)
236+
secondaryIndexesToBuild.insert(persistedIndex.name)
252237
}
238+
} else {
239+
/// The index is no longer needed, delete it.
240+
try await transaction.deleteSecondaryIndex(indexName: persistedIndex.name, datastoreKey: key)
241+
}
242+
}
243+
244+
/// Check for new reference indexes to build
245+
for (_, updatedIndex) in updatedDescriptor.referenceIndexes {
246+
guard persistedDescriptor.referenceIndexes[updatedIndex.name] == nil else { continue }
247+
/// The index does not yet exist, so queue it to be built.
248+
secondaryIndexesToBuild.insert(updatedIndex.name)
249+
}
250+
251+
/// Remove any direct indexes from the secondary ones we may have requested.
252+
secondaryIndexesToBuild.subtract(directIndexesToBuild)
253+
254+
/// Only perform work if we need to rebuild anything.
255+
if rebuildPrimaryIndex || !directIndexesToBuild.isEmpty || !secondaryIndexesToBuild.isEmpty {
256+
/// Create any missing indexes and prime the datastore for writing.
257+
try await transaction.apply(descriptor: updatedDescriptor, for: key)
258+
259+
let primaryIndex = _load(IndexRange(), order: .ascending, awaitWarmup: false)
260+
261+
let versionData = try Data(self.version)
262+
263+
for try await (idenfifier, instance) in primaryIndex {
264+
defer { index += 1 }
253265

254-
/// Check for new secondary indexes to build
255-
for (_, updatedIndex) in updatedDescriptor.referenceIndexes {
256-
guard persistedDescriptor.referenceIndexes[updatedIndex.name] == nil else { continue }
257-
/// The index does not yet exist, so queue it to be built.
258-
secondaryIndexesToBuild.insert(updatedIndex.name)
266+
/// Notify progress handlers we are starting an entry.
267+
for handler in warmupProgressHandlers {
268+
handler(.working(current: index, total: persistedDescriptor.size))
259269
}
260270

261-
/// Remove any direct indexes from the secondary ones we may have requested.
262-
secondaryIndexesToBuild.subtract(directIndexesToBuild)
271+
let instanceData = try await encoder(instance)
263272

264-
/// If we don't need to migrate anything, stop here.
265-
if rebuildPrimaryIndex == false, directIndexesToBuild.isEmpty, secondaryIndexesToBuild.isEmpty {
266-
break
273+
if rebuildPrimaryIndex {
274+
let insertionCursor = try await transaction.primaryIndexCursor(inserting: idenfifier, datastoreKey: key)
275+
276+
try await transaction.persistPrimaryIndexEntry(
277+
versionData: versionData,
278+
identifierValue: idenfifier,
279+
instanceData: instanceData,
280+
cursor: insertionCursor,
281+
datastoreKey: key
282+
)
267283
}
268284

269-
/// Create any missing indexes and prime the datastore for writing.
270-
try await transaction.apply(descriptor: updatedDescriptor, for: key)
271-
}
272-
273-
/// Notify progress handlers we are starting an entry.
274-
for handler in warmupProgressHandlers {
275-
handler(.working(current: index, total: persistedDescriptor.size))
276-
}
277-
278-
let instanceData = try await encoder(instance)
279-
280-
if rebuildPrimaryIndex {
281-
let insertionCursor = try await transaction.primaryIndexCursor(inserting: idenfifier, datastoreKey: key)
285+
var queriedIndexes: Set<IndexName> = []
282286

283-
try await transaction.persistPrimaryIndexEntry(
284-
versionData: versionData,
285-
identifierValue: idenfifier,
286-
instanceData: instanceData,
287-
cursor: insertionCursor,
288-
datastoreKey: key
289-
)
290-
}
291-
292-
var queriedIndexes: Set<IndexName> = []
293-
294-
for (_, generatedRepresentation) in indexRepresentations {
295-
let indexName = generatedRepresentation.indexName
296-
switch generatedRepresentation.storage {
297-
case .direct:
298-
guard
299-
directIndexesToBuild.contains(indexName),
300-
!queriedIndexes.contains(indexName)
301-
else { return }
302-
queriedIndexes.insert(indexName)
303-
304-
for updatedValue in instance[index: generatedRepresentation.index] {
305-
/// Grab a cursor to insert the new value in the index.
306-
let updatedValueCursor = try await transaction.directIndexCursor(
307-
inserting: updatedValue.indexed,
308-
identifier: idenfifier,
309-
indexName: indexName,
310-
datastoreKey: key
311-
)
287+
for (_, generatedRepresentation) in indexRepresentations {
288+
let indexName = generatedRepresentation.indexName
289+
switch generatedRepresentation.storage {
290+
case .direct:
291+
guard
292+
directIndexesToBuild.contains(indexName),
293+
!queriedIndexes.contains(indexName)
294+
else { return }
295+
queriedIndexes.insert(indexName)
312296

313-
/// Insert it.
314-
try await transaction.persistDirectIndexEntry(
315-
versionData: versionData,
316-
indexValue: updatedValue.indexed,
317-
identifierValue: idenfifier,
318-
instanceData: instanceData,
319-
cursor: updatedValueCursor,
320-
indexName: indexName,
321-
datastoreKey: key
322-
)
323-
}
324-
case .reference:
325-
guard
326-
secondaryIndexesToBuild.contains(indexName),
327-
!queriedIndexes.contains(indexName)
328-
else { return }
329-
queriedIndexes.insert(indexName)
330-
331-
for updatedValue in instance[index: generatedRepresentation.index] {
332-
/// Grab a cursor to insert the new value in the index.
333-
let updatedValueCursor = try await transaction.secondaryIndexCursor(
334-
inserting: updatedValue.indexed,
335-
identifier: idenfifier,
336-
indexName: indexName,
337-
datastoreKey: self.key
338-
)
297+
for updatedValue in instance[index: generatedRepresentation.index] {
298+
/// Grab a cursor to insert the new value in the index.
299+
let updatedValueCursor = try await transaction.directIndexCursor(
300+
inserting: updatedValue.indexed,
301+
identifier: idenfifier,
302+
indexName: indexName,
303+
datastoreKey: key
304+
)
305+
306+
/// Insert it.
307+
try await transaction.persistDirectIndexEntry(
308+
versionData: versionData,
309+
indexValue: updatedValue.indexed,
310+
identifierValue: idenfifier,
311+
instanceData: instanceData,
312+
cursor: updatedValueCursor,
313+
indexName: indexName,
314+
datastoreKey: key
315+
)
316+
}
317+
case .reference:
318+
guard
319+
secondaryIndexesToBuild.contains(indexName),
320+
!queriedIndexes.contains(indexName)
321+
else { return }
322+
queriedIndexes.insert(indexName)
339323

340-
/// Insert it.
341-
try await transaction.persistSecondaryIndexEntry(
342-
indexValue: updatedValue.indexed,
343-
identifierValue: idenfifier,
344-
cursor: updatedValueCursor,
345-
indexName: indexName,
346-
datastoreKey: self.key
347-
)
324+
for updatedValue in instance[index: generatedRepresentation.index] {
325+
/// Grab a cursor to insert the new value in the index.
326+
let updatedValueCursor = try await transaction.secondaryIndexCursor(
327+
inserting: updatedValue.indexed,
328+
identifier: idenfifier,
329+
indexName: indexName,
330+
datastoreKey: self.key
331+
)
332+
333+
/// Insert it.
334+
try await transaction.persistSecondaryIndexEntry(
335+
indexValue: updatedValue.indexed,
336+
identifierValue: idenfifier,
337+
cursor: updatedValueCursor,
338+
indexName: indexName,
339+
datastoreKey: self.key
340+
)
341+
}
348342
}
349343
}
350344
}

0 commit comments

Comments
 (0)