From f124e1ab42091f8b9b2c2e83279412e9bfc679d7 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Tue, 25 Apr 2023 09:30:53 -0400 Subject: [PATCH] improve retry and clock check mechanisms --- .../Models/RemoteSynchronizing.swift | 38 ++++++++++++++-- Sources/ParseCareKit/ParseRemote.swift | 43 +++++++++++++------ 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/Sources/ParseCareKit/Models/RemoteSynchronizing.swift b/Sources/ParseCareKit/Models/RemoteSynchronizing.swift index c87f79a57..7bf7d6e71 100644 --- a/Sources/ParseCareKit/Models/RemoteSynchronizing.swift +++ b/Sources/ParseCareKit/Models/RemoteSynchronizing.swift @@ -10,7 +10,14 @@ import CareKitStore import Foundation actor RemoteSynchronizing { - var isSynchronizing = false + var isSynchronizing = false { + willSet { + if newValue { + resetLiveQueryRetry() + } + } + } + var liveQueryRetry = 0 var clock: PCKClock? var knowledgeVector: OCKRevisionRecord.KnowledgeVector? { clock?.knowledgeVector @@ -24,17 +31,40 @@ actor RemoteSynchronizing { isSynchronizing = false } + func resetLiveQueryRetry() { + liveQueryRetry = 0 + } + + func retryLiveQueryAfter() throws -> Int { + liveQueryRetry += 1 + guard liveQueryRetry <= 10 else { + throw ParseCareKitError.errorString("Max retries reached") + } + return Int.random(in: 0...liveQueryRetry) + } + func updateClock(_ clock: PCKClock?) { self.clock = clock } - func hasNewerClock(_ vector: OCKRevisionRecord.KnowledgeVector, for uuid: UUID) -> Bool { - guard !isSynchronizing else { - return false + func updateClockIfNeeded(_ clock: PCKClock) { + guard self.clock == nil else { + return } + self.clock = clock + } + + func hasNewerClock(_ vector: OCKRevisionRecord.KnowledgeVector, for uuid: UUID) -> Bool { guard let currentClock = knowledgeVector?.clock(for: uuid) else { return true } return vector.clock(for: uuid) > currentClock } + + func hasNewerVector(_ vector: OCKRevisionRecord.KnowledgeVector) -> Bool { + guard let currentVector = knowledgeVector else { + return true + } + return vector > currentVector + } } diff --git a/Sources/ParseCareKit/ParseRemote.swift b/Sources/ParseCareKit/ParseRemote.swift index 12ed9dbfd..374a40c16 100644 --- a/Sources/ParseCareKit/ParseRemote.swift +++ b/Sources/ParseCareKit/ParseRemote.swift @@ -254,7 +254,6 @@ public class ParseRemote: OCKRemoteSynchronizable { Task { do { let parseClock = try await PCKClock.fetchFromRemote(self.uuid, createNewIfNeeded: true) - guard let parseVector = parseClock.knowledgeVector else { await self.remoteStatus.updateClock(nil) await self.remoteStatus.notSynchronzing() @@ -262,11 +261,10 @@ public class ParseRemote: OCKRemoteSynchronizable { completion(ParseCareKitError.requiredValueCantBeUnwrapped) return } + await self.remoteStatus.updateClockIfNeeded(parseClock) // 6. Ensure there has not been any updates to remote clock before proceeding. - let hasNewerClock = await self.remoteStatus.hasNewerClock(parseVector, for: self.uuid) - let currentClock = await self.remoteStatus.clock - guard !hasNewerClock || currentClock == nil else { + guard await !self.remoteStatus.hasNewerVector(parseVector) else { let errorString = "New knowledge on server. Attempting to pull and try again" Logger.pushRevisions.error("\(errorString)") await self.remoteStatus.notSynchronzing() @@ -438,17 +436,22 @@ public class ParseRemote: OCKRemoteSynchronizable { switch event { case .created(let updatedClock), .updated(let updatedClock): Task { - guard let updatedVector = updatedClock.knowledgeVector, - await self.remoteStatus.hasNewerClock(updatedVector, for: self.uuid) else { + guard await !self.remoteStatus.isSynchronizing else { + // If currently syncing need to check in the future + do { + let delay = try await self.remoteStatus.retryLiveQueryAfter() + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(delay)) { + Task { + await self.requestSyncIfNewerClock(updatedClock) + } + } + } catch { + Logger.clockSubscription.error("\(error)") + await self.remoteStatus.notSynchronzing() + } return } - let random = Int.random(in: 0..<2) - DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(random)) { - self.parseDelegate?.didRequestSynchronization(self) - } - Logger - .clockSubscription - .log("Parse subscription is notifying that there are updates on the remote") + await self.requestSyncIfNewerClock(updatedClock) } default: return @@ -463,6 +466,20 @@ public class ParseRemote: OCKRemoteSynchronizable { } } + func requestSyncIfNewerClock(_ clock: PCKClock?) async { + guard let vector = clock?.knowledgeVector, + await self.remoteStatus.hasNewerClock(vector, for: self.uuid) else { + return + } + let random = Int.random(in: 0..<2) + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(random)) { + self.parseDelegate?.didRequestSynchronization(self) + } + Logger + .clockSubscription + .log("Parse subscription is notifying that there are updates on the remote") + } + func incrementVectorClock(_ vector: OCKRevisionRecord.KnowledgeVector) -> OCKRevisionRecord.KnowledgeVector { var mutableVector = vector mutableVector.increment(clockFor: self.uuid)