Fix nasty bug executing api calls forever

sync2
Laurent 1 year ago
parent e8f2b21563
commit e02b12e8e2
  1. 61
      LeStorage/ApiCallCollection.swift
  2. 2
      LeStorage/StoreCenter.swift

@ -7,7 +7,6 @@
import Foundation
protocol SomeCallCollection {
func findCallById(_ id: String) async -> (any SomeCall)?
@ -32,7 +31,7 @@ actor ApiCallCollection<T: Storable>: SomeCallCollection {
fileprivate var _attemptLoops: Int = 0
/// Indicates if the collection is currently retrying ApiCalls
fileprivate var _isRetryingCalls: Bool = false
fileprivate var _isRescheduling: Bool = false
/// The task of waiting and executing ApiCalls
fileprivate var _reschedulingTask: Task<Void, any Error>? = nil
@ -67,7 +66,7 @@ actor ApiCallCollection<T: Storable>: SomeCallCollection {
if FileManager.default.fileExists(atPath: fileURL.path()) {
let jsonString: String = try FileUtils.readFile(fileURL: fileURL)
let decoded: [ApiCall<T>] = try jsonString.decodeArray() ?? []
// Logger.log("loaded \(fileURL.lastPathComponent) with \(decoded.count) items")
// Logger.log("loaded \(fileURL.lastPathComponent) with \(decoded.count) items")
self.items = decoded
}
}
@ -76,14 +75,14 @@ actor ApiCallCollection<T: Storable>: SomeCallCollection {
fileprivate func _write() {
let fileName = ApiCall<T>.fileName()
DispatchQueue(label: "lestorage.queue.write", qos: .utility).asyncAndWait {
// Logger.log("Start write to \(fileName)...")
// Logger.log("Start write to \(fileName)...")
do {
let jsonString: String = try self.items.jsonString()
try T.writeToStorageDirectory(content: jsonString, fileName: fileName)
} catch {
Logger.error(error)
}
// Logger.log("End write")
// Logger.log("End write")
}
}
@ -100,6 +99,7 @@ actor ApiCallCollection<T: Storable>: SomeCallCollection {
/// Deletes an API call by [id]
func deleteById(_ id: String) {
self.items.removeAll(where: { $0.id == id })
Logger.log("\(T.resourceName()) > Delete by id, count after deletion = \(self.items.count)")
self._hasChanged = true
}
@ -136,25 +136,41 @@ actor ApiCallCollection<T: Storable>: SomeCallCollection {
}
}
fileprivate func _wait() async {
let delay = pow(2, self._attemptLoops)
let seconds = NSDecimalNumber(decimal: delay).intValue
Logger.log("\(T.resourceName()): wait for \(seconds) sec")
do {
try await Task.sleep(until: .now + .seconds(seconds))
} catch {
Logger.error(error)
}
}
/// Reschedule API calls if necessary
func rescheduleApiCallsIfNecessary() {
Task {
await self._rescheduleApiCalls()
}
}
/// Reschedule the execution of API calls
fileprivate func _rescheduleApiCalls() {
fileprivate func _rescheduleApiCalls() async {
guard !self._isRescheduling else { return }
self._isRescheduling = true
guard self.items.isNotEmpty else {
return
}
self._isRetryingCalls = true
self._attemptLoops += 1
self._reschedulingTask = Task {
let delay = pow(2, self._attemptLoops)
let seconds = NSDecimalNumber(decimal: delay).intValue
Logger.log("\(T.resourceName()): wait for \(seconds) sec")
try await Task.sleep(until: .now + .seconds(seconds))
await self._wait()
let apiCallsCopy = self.items
for apiCall in apiCallsCopy {
for (index, apiCall) in apiCallsCopy.enumerated() {
apiCall.attemptsCount += 1
apiCall.lastAttemptDate = Date()
@ -165,16 +181,12 @@ actor ApiCallCollection<T: Storable>: SomeCallCollection {
}
}
self._hasChanged = true
if self.items.isEmpty {
self._isRetryingCalls = false
} else {
self._rescheduleApiCalls()
self._isRescheduling = false
if self.items.isNotEmpty {
await self._rescheduleApiCalls()
}
}
}
// MARK: - Synchronization
@ -213,13 +225,6 @@ actor ApiCallCollection<T: Storable>: SomeCallCollection {
self.addOrUpdate(apiCall)
}
/// Reschedule API calls if necessary
func rescheduleApiCallsIfNecessary() {
if !self._isRetryingCalls {
self._rescheduleApiCalls()
}
}
/// Sends an insert api call for the provided [instance]
func sendInsertion(_ instance: T) async throws -> T? {
do {

@ -154,6 +154,7 @@ public class StoreCenter {
/// Instantiates and loads an ApiCallCollection with the provided type
public func loadApiCallCollection<T: Storable>(type: T.Type) {
if self._apiCallCollections[T.resourceName()] == nil {
let apiCallCollection = ApiCallCollection<T>()
self._apiCallCollections[T.resourceName()] = apiCallCollection
Task {
@ -164,6 +165,7 @@ public class StoreCenter {
}
}
}
}
/// Returns the ApiCall collection using the resource name of the provided T type
func apiCallCollection<T: Storable>() throws -> ApiCallCollection<T> {

Loading…
Cancel
Save