Skip to content

Commit c60bea0

Browse files
authored
fix(DataStore): redundant local metadata query in ReconcileAndLocalSaveOperation (#1217)
* fix(DataStore): redundant local metadata query in ReconcileAndLocalSaveOperation * fix(DataStore): remove unneeded mutationType parameter
1 parent 51f6563 commit c60bea0

File tree

8 files changed

+63
-84
lines changed

8 files changed

+63
-84
lines changed

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/ReconcileAndLocalSaveOperation+Action.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ extension ReconcileAndLocalSaveOperation {
2323

2424
/// Operation has applied the incoming RemoteModel to the local database per the reconciled disposition. This
2525
/// could result in either a save to the local database, or a delete from the local database.
26-
case applied(AppliedModel, existsLocally: Bool)
26+
case applied(AppliedModel, mutationType: MutationEvent.MutationType)
2727

2828
/// Operation dropped the remote model per the reconciled disposition.
2929
case dropped(modelName: String)

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/ReconcileAndLocalSaveOperation+Resolver.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ extension ReconcileAndLocalSaveOperation {
3434
case (.notifyingDropped, .notified):
3535
return .finished
3636

37-
case (.executing, .applied(let savedModel, let existsLocally)):
38-
return .notifying(savedModel, existsLocally)
37+
case (.executing, .applied(let savedModel, let mutationType)):
38+
return .notifying(savedModel, mutationType)
3939

4040
case (.notifying, .notified):
4141
return .finished

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/ReconcileAndLocalSaveOperation+State.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ extension ReconcileAndLocalSaveOperation {
2727
case notifyingDropped(String)
2828

2929
/// Notifying listeners and callbacks of completion
30-
case notifying(AppliedModel, Bool)
30+
case notifying(AppliedModel, MutationEvent.MutationType)
3131

3232
/// Operation has successfully completed
3333
case finished

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/ReconcileAndLocalSaveOperation.swift

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ class ReconcileAndLocalSaveOperation: AsynchronousOperation {
100100
case .notifyingDropped(let modelName):
101101
notifyDropped(modelName: modelName)
102102

103-
case .notifying(let savedModel, let existsLocally):
104-
notify(savedModel: savedModel, existsLocally: existsLocally)
103+
case .notifying(let savedModel, let mutationType):
104+
notify(savedModel: savedModel, mutationType: mutationType)
105105

106106
case .inError(let error):
107107
// Maybe we have to notify the Hub?
@@ -189,20 +189,18 @@ class ReconcileAndLocalSaveOperation: AsynchronousOperation {
189189
/// - dropped
190190
func execute(disposition: RemoteSyncReconciler.Disposition) {
191191
switch disposition {
192-
case .applyRemoteModel(let remoteModel):
193-
apply(remoteModel: remoteModel)
192+
case .applyRemoteModel(let remoteModel, let mutationType):
193+
apply(remoteModel: remoteModel, mutationType: mutationType)
194194
case .dropRemoteModel(let modelName):
195195
stateMachine.notify(action: .dropped(modelName: modelName))
196-
case .error(let dataStoreError):
197-
stateMachine.notify(action: .errored(dataStoreError))
198196
}
199197
}
200198

201199
/// Execution method for the `applyRemoteModel` disposition. Does not notify directly, but delegates to save or
202200
/// delete methods, which eventually notify with:
203201
/// - applied
204202
/// - errored
205-
private func apply(remoteModel: RemoteModel) {
203+
private func apply(remoteModel: RemoteModel, mutationType: MutationEvent.MutationType) {
206204
if log.logLevel == .verbose {
207205
log.verbose("\(#function): remoteModel")
208206
}
@@ -218,15 +216,17 @@ class ReconcileAndLocalSaveOperation: AsynchronousOperation {
218216
}
219217

220218
// TODO: Wrap this in a transaction
221-
if remoteModel.syncMetadata.deleted {
219+
if mutationType == .delete {
222220
saveDeleteMutation(storageAdapter: storageAdapter, remoteModel: remoteModel)
223221
} else {
224-
saveCreateOrUpdateMutation(storageAdapter: storageAdapter, remoteModel: remoteModel)
222+
saveCreateOrUpdateMutation(storageAdapter: storageAdapter,
223+
remoteModel: remoteModel,
224+
mutationType: mutationType)
225225
}
226-
227226
}
228227

229-
private func saveDeleteMutation(storageAdapter: StorageEngineAdapter, remoteModel: RemoteModel) {
228+
private func saveDeleteMutation(storageAdapter: StorageEngineAdapter,
229+
remoteModel: RemoteModel) {
230230
log.verbose(#function)
231231

232232
guard let modelType = ModelRegistry.modelType(from: modelSchema.name) else {
@@ -247,12 +247,16 @@ class ReconcileAndLocalSaveOperation: AsynchronousOperation {
247247
let errorAction = Action.errored(dataStoreError)
248248
self.stateMachine.notify(action: errorAction)
249249
case .success:
250-
self.saveMetadata(storageAdapter: storageAdapter, inProcessModel: remoteModel)
250+
self.saveMetadata(storageAdapter: storageAdapter,
251+
inProcessModel: remoteModel,
252+
mutationType: .delete)
251253
}
252254
}
253255
}
254256

255-
private func saveCreateOrUpdateMutation(storageAdapter: StorageEngineAdapter, remoteModel: RemoteModel) {
257+
private func saveCreateOrUpdateMutation(storageAdapter: StorageEngineAdapter,
258+
remoteModel: RemoteModel,
259+
mutationType: MutationEvent.MutationType) {
256260
log.verbose(#function)
257261
storageAdapter.save(untypedModel: remoteModel.model.instance) { response in
258262
if self.log.logLevel == .debug {
@@ -271,25 +275,18 @@ class ReconcileAndLocalSaveOperation: AsynchronousOperation {
271275
return
272276
}
273277
let inProcessModel = MutationSync(model: anyModel, syncMetadata: remoteModel.syncMetadata)
274-
self.saveMetadata(storageAdapter: storageAdapter, inProcessModel: inProcessModel)
278+
self.saveMetadata(storageAdapter: storageAdapter,
279+
inProcessModel: inProcessModel,
280+
mutationType: mutationType)
275281
}
276282
}
277283
}
278284

279285
private func saveMetadata(storageAdapter: StorageEngineAdapter,
280-
inProcessModel: AppliedModel) {
286+
inProcessModel: AppliedModel,
287+
mutationType: MutationEvent.MutationType) {
281288
log.verbose(#function)
282289

283-
/// Do a local metadata query before saving to check if the `AppliedModel` is of `create` or
284-
/// `update` MutationType from the perspective of the local store
285-
let existsLocally: Bool
286-
do {
287-
let localMetadata = try storageAdapter.queryMutationSyncMetadata(for: remoteModel.model.id)
288-
existsLocally = localMetadata != nil
289-
} catch {
290-
log.error("Failed to query for sync metadata")
291-
return
292-
}
293290
storageAdapter.save(remoteModel.syncMetadata, condition: nil) { result in
294291
if self.log.logLevel == .debug {
295292
self.log.debug("save metadata: \(self.stopwatch.lap())s")
@@ -300,7 +297,7 @@ class ReconcileAndLocalSaveOperation: AsynchronousOperation {
300297
self.stateMachine.notify(action: errorAction)
301298
case .success(let syncMetadata):
302299
let appliedModel = MutationSync(model: inProcessModel.model, syncMetadata: syncMetadata)
303-
self.stateMachine.notify(action: .applied(appliedModel, existsLocally: existsLocally))
300+
self.stateMachine.notify(action: .applied(appliedModel, mutationType: mutationType))
304301
}
305302
}
306303
}
@@ -313,23 +310,14 @@ class ReconcileAndLocalSaveOperation: AsynchronousOperation {
313310
/// Responder method for `notifying`. Notify actions:
314311
/// - notified
315312
func notify(savedModel: AppliedModel,
316-
existsLocally: Bool) {
313+
mutationType: MutationEvent.MutationType) {
317314
log.verbose(#function)
318315

319316
guard !isCancelled else {
320317
log.verbose("\(#function) - cancelled, aborting")
321318
return
322319
}
323-
324-
let mutationType: MutationEvent.MutationType
325320
let version = savedModel.syncMetadata.version
326-
if savedModel.syncMetadata.deleted {
327-
mutationType = .delete
328-
} else if !existsLocally {
329-
mutationType = .create
330-
} else {
331-
mutationType = .update
332-
}
333321

334322
// TODO: Dispatch/notify error if we can't erase to any model? Would imply an error in JSON decoding,
335323
// which shouldn't be possible this late in the process. Possibly notify global conflict/error handler?

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/RemoteSyncReconciler.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ struct RemoteSyncReconciler {
1515
typealias SavedModel = ReconcileAndLocalSaveOperation.AppliedModel
1616

1717
enum Disposition {
18-
case applyRemoteModel(RemoteModel)
18+
case applyRemoteModel(RemoteModel, MutationEvent.MutationType)
1919
case dropRemoteModel(String)
20-
case error(DataStoreError)
2120
}
2221

2322
static func reconcile(remoteModel: RemoteModel,
@@ -29,13 +28,21 @@ struct RemoteSyncReconciler {
2928
}
3029

3130
guard let localMetadata = localMetadata else {
32-
return .applyRemoteModel(remoteModel)
31+
if remoteModel.syncMetadata.deleted {
32+
return .dropRemoteModel(remoteModel.model.modelName)
33+
} else {
34+
return .applyRemoteModel(remoteModel, .create)
35+
}
3336
}
3437

3538
// Technically, we should never receive a subscription for a version we already have, but we'll be defensive
3639
// and make this check include the current version
3740
if remoteModel.syncMetadata.version >= localMetadata.version {
38-
return .applyRemoteModel(remoteModel)
41+
if remoteModel.syncMetadata.deleted {
42+
return .applyRemoteModel(remoteModel, .delete)
43+
} else {
44+
return .applyRemoteModel(remoteModel, .update)
45+
}
3946
}
4047

4148
return .dropRemoteModel(remoteModel.model.modelName)

AmplifyPlugins/DataStore/AWSDataStoreCategoryPluginTests/Sync/SubscriptionSync/ReconcileAndLocalSaveOperationTests.swift

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase {
132132

133133
func testReconcilingWithoutLocalModel() throws {
134134
let expect = expectation(description: "action .reconciled notified")
135-
let expectedDisposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostMutationSync)
135+
let expectedDisposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostMutationSync, .create)
136136
stateMachine.pushExpectActionCriteria { action in
137137
XCTAssertEqual(action, ReconcileAndLocalSaveOperation.Action.reconciled(expectedDisposition))
138138
expect.fulfill()
@@ -142,29 +142,28 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase {
142142
waitForExpectations(timeout: 1)
143143
}
144144

145-
func testExecuteApplyRemoteModelThatDoesNotExistLocally() throws {
145+
func testExecuteApplyRemoteModelCreate() throws {
146146
let expect = expectation(description: "action .execute applyRemoteModel")
147-
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostMutationSync)
147+
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostMutationSync, .create)
148148
storageAdapter.returnOnSave(dataStoreResult: .success(anyPostMutationSync.model))
149149
stateMachine.pushExpectActionCriteria { action in
150150
XCTAssertEqual(action, ReconcileAndLocalSaveOperation.Action.applied(self.anyPostMutationSync,
151-
existsLocally: false))
151+
mutationType: .create))
152152
expect.fulfill()
153153
}
154154

155155
stateMachine.state = .executing(disposition)
156156
waitForExpectations(timeout: 1)
157157
}
158158

159-
func testExecuteApplyRemoteModelThatExistsLocally() throws {
159+
func testExecuteApplyRemoteModelUpdate() throws {
160160
let expect = expectation(description: "action .execute applyRemoteModel")
161-
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostMutationSync)
161+
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostMutationSync, .update)
162162

163163
storageAdapter.returnOnSave(dataStoreResult: .success(anyPostMutationSync.model))
164-
storageAdapter.returnOnQueryMutationSyncMetadata(.some(anyPostMetadata))
165164
stateMachine.pushExpectActionCriteria { action in
166165
XCTAssertEqual(action, ReconcileAndLocalSaveOperation.Action.applied(self.anyPostMutationSync,
167-
existsLocally: true))
166+
mutationType: .update))
168167
expect.fulfill()
169168
}
170169
stateMachine.state = .executing(disposition)
@@ -174,7 +173,7 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase {
174173

175174
func testExecuteApplyRemoteModel_saveMutationFailed() throws {
176175
let expect = expectation(description: "action .execute error on save model")
177-
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostMutationSync)
176+
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostMutationSync, .create)
178177
let error = DataStoreError.invalidModelName("invModelName")
179178
storageAdapter.returnOnSave(dataStoreResult: .failure(error))
180179
stateMachine.pushExpectActionCriteria { action in
@@ -189,7 +188,7 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase {
189188

190189
func testExecuteApplyRemoteModel_saveMutationOK_MetadataFailed() throws {
191190
let expect = expectation(description: "action .execute error on save mutation")
192-
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostMutationSync)
191+
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostMutationSync, .create)
193192
let error = DataStoreError.invalidModelName("forceError")
194193
storageAdapter.returnOnSave(dataStoreResult: .success(anyPostMutationSync.model))
195194
storageAdapter.shouldReturnErrorOnSaveMetadata = true
@@ -204,11 +203,11 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase {
204203

205204
func testExecuteApplyRemoteModel_Delete() throws {
206205
let expect = expectation(description: "action .execute applyRemoteModel delete success case")
207-
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostDeletedMutationSync)
206+
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostDeletedMutationSync, .delete)
208207
storageAdapter.returnOnSave(dataStoreResult: .success(anyPostDeletedMutationSync.model))
209208
stateMachine.pushExpectActionCriteria { action in
210209
XCTAssertEqual(action, ReconcileAndLocalSaveOperation.Action.applied(self.anyPostDeletedMutationSync,
211-
existsLocally: false))
210+
mutationType: .delete))
212211
expect.fulfill()
213212
}
214213

@@ -218,7 +217,7 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase {
218217

219218
func testExecuteApplyRemoteModel_Delete_saveMutationFailed() throws {
220219
let expect = expectation(description: "action .execute applyRemoteModel delete mutation error")
221-
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostDeletedMutationSync)
220+
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostDeletedMutationSync, .delete)
222221
let error = DataStoreError.invalidModelName("DelMutate")
223222
storageAdapter.shouldReturnErrorOnDeleteMutation = true
224223
stateMachine.pushExpectActionCriteria { action in
@@ -233,7 +232,7 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase {
233232

234233
func testExecuteApplyRemoteModel_Delete_saveMutationOK_saveMetadataFailed() throws {
235234
let expect = expectation(description: "action .execute applyRemoteModel delete metadata error")
236-
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostDeletedMutationSync)
235+
let disposition = RemoteSyncReconciler.Disposition.applyRemoteModel(anyPostDeletedMutationSync, .delete)
237236
let error = DataStoreError.invalidModelName("forceError")
238237
storageAdapter.shouldReturnErrorOnSaveMetadata = true
239238
storageAdapter.returnOnSave(dataStoreResult: .failure(error))
@@ -259,20 +258,6 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase {
259258
waitForExpectations(timeout: 1)
260259
}
261260

262-
func testExecuteErrorOnDisposition() throws {
263-
let expect = expectation(description: "action .execute error")
264-
let error = DataStoreError.invalidModelName("invModelName")
265-
let disposition = RemoteSyncReconciler.Disposition.error(error)
266-
stateMachine.pushExpectActionCriteria { action in
267-
XCTAssertEqual(action, ReconcileAndLocalSaveOperation.Action.errored(error))
268-
expect.fulfill()
269-
}
270-
271-
stateMachine.state = .executing(disposition)
272-
273-
waitForExpectations(timeout: 1)
274-
}
275-
276261
func testNotifying() throws {
277262
let hubExpect = expectation(description: "Hub is notified")
278263
let notifyExpect = expectation(description: "action .notified notified")
@@ -286,7 +271,7 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase {
286271
notifyExpect.fulfill()
287272
}
288273

289-
stateMachine.state = .notifying(anyPostMutationSync, false)
274+
stateMachine.state = .notifying(anyPostMutationSync, .create)
290275

291276
waitForExpectations(timeout: 1)
292277
Amplify.Hub.removeListener(hubListener)

0 commit comments

Comments
 (0)