|
|
|
|
@ -46,7 +46,7 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection { |
|
|
|
|
fileprivate var _attemptLoops: Int = 0 |
|
|
|
|
|
|
|
|
|
/// Indicates if the collection is currently retrying ApiCalls |
|
|
|
|
fileprivate var _isRescheduling: Bool = false |
|
|
|
|
fileprivate var _isExecutingCalls: Bool = false |
|
|
|
|
|
|
|
|
|
fileprivate var _schedulingTask: Task<(), Never>? = nil |
|
|
|
|
|
|
|
|
|
@ -140,7 +140,7 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection { |
|
|
|
|
|
|
|
|
|
/// Removes all objects in memory and deletes the JSON file |
|
|
|
|
func reset() { |
|
|
|
|
self._isRescheduling = false |
|
|
|
|
self._isExecutingCalls = false |
|
|
|
|
self.items.removeAll() |
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
@ -157,11 +157,11 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection { |
|
|
|
|
self._attemptLoops = -1 |
|
|
|
|
self.rescheduleApiCallsIfNecessary() |
|
|
|
|
|
|
|
|
|
// if self._schedulingTask != nil && self._attemptLoops > 2 { |
|
|
|
|
// self._schedulingTask?.cancel() |
|
|
|
|
// self._attemptLoops = -1 |
|
|
|
|
// self.rescheduleApiCallsIfNecessary() |
|
|
|
|
// } |
|
|
|
|
if self._schedulingTask != nil && self._attemptLoops > 2 { |
|
|
|
|
self._schedulingTask?.cancel() |
|
|
|
|
self._attemptLoops = -1 |
|
|
|
|
self.rescheduleApiCallsIfNecessary() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func rescheduleImmediately() { |
|
|
|
|
@ -171,7 +171,7 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection { |
|
|
|
|
|
|
|
|
|
/// Reschedule API calls if necessary |
|
|
|
|
func rescheduleApiCallsIfNecessary() { |
|
|
|
|
if self.items.isNotEmpty && !self._isRescheduling { |
|
|
|
|
if self.items.isNotEmpty && !self._isExecutingCalls { |
|
|
|
|
self._schedulingTask = Task { |
|
|
|
|
await self._waitAndExecuteApiCalls() |
|
|
|
|
} |
|
|
|
|
@ -182,15 +182,16 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection { |
|
|
|
|
fileprivate func _waitAndExecuteApiCalls() async { |
|
|
|
|
|
|
|
|
|
// Logger.log("\(T.resourceName()) > RESCHED") |
|
|
|
|
guard !self._isRescheduling, StoreCenter.main.collectionsCanSynchronize else { return } |
|
|
|
|
guard !self._isExecutingCalls, StoreCenter.main.collectionsCanSynchronize else { return } |
|
|
|
|
guard self.items.isNotEmpty else { return } |
|
|
|
|
|
|
|
|
|
self._isRescheduling = true |
|
|
|
|
self._isExecutingCalls = true |
|
|
|
|
|
|
|
|
|
self._attemptLoops += 1 |
|
|
|
|
|
|
|
|
|
await self._wait() |
|
|
|
|
|
|
|
|
|
// Logger.log("\(T.resourceName()) > EXECUTE CALLS: \(self.items.count)") |
|
|
|
|
let batches = Dictionary(grouping: self.items, by: { $0.transactionId }) |
|
|
|
|
|
|
|
|
|
for batch in batches.values { |
|
|
|
|
@ -209,7 +210,8 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
self._isRescheduling = false |
|
|
|
|
// Logger.log("\(T.resourceName()) > EXECUTE CALLS ENDED !") |
|
|
|
|
self._isExecutingCalls = false |
|
|
|
|
if self.items.isNotEmpty { |
|
|
|
|
await self._waitAndExecuteApiCalls() |
|
|
|
|
} |
|
|
|
|
@ -229,6 +231,8 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection { |
|
|
|
|
/// Wait for an exponentionnaly long time depending on the number of attemps |
|
|
|
|
fileprivate func _wait() async { |
|
|
|
|
|
|
|
|
|
guard self._attemptLoops > 0 else { return } |
|
|
|
|
|
|
|
|
|
var seconds = self._attemptLoops |
|
|
|
|
if self._attemptLoops > 5 { |
|
|
|
|
let delay = pow(2, self._attemptLoops - 2) // starts at 16s |
|
|
|
|
@ -313,7 +317,7 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func executeBatch(_ batch: OperationBatch<T>) async throws -> [OperationResult<T>] { |
|
|
|
|
func executeBatch(_ batch: OperationBatch<T>) async throws { |
|
|
|
|
|
|
|
|
|
var apiCalls: [ApiCall<T>] = [] |
|
|
|
|
let transactionId = Store.randomId() |
|
|
|
|
@ -329,7 +333,9 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection { |
|
|
|
|
let call = try self.callForInstance(delete, method: .delete, transactionId: transactionId) |
|
|
|
|
apiCalls.append(call) |
|
|
|
|
} |
|
|
|
|
return try await self._executeApiCalls(apiCalls) |
|
|
|
|
self.rescheduleApiCallsIfNecessary() |
|
|
|
|
|
|
|
|
|
// return try await self._executeApiCalls(apiCalls) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileprivate func _prepareAndSendGetCall(_ apiCall: ApiCall<T>) async throws { |
|
|
|
|
@ -346,6 +352,9 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection { |
|
|
|
|
/// Executes an API call |
|
|
|
|
/// For POST requests, potentially copies additional data coming from the server during the insert |
|
|
|
|
fileprivate func _executeApiCalls(_ apiCalls: [ApiCall<T>]) async throws -> [OperationResult<T>] { |
|
|
|
|
// for call in apiCalls { |
|
|
|
|
// Logger.log("execute call = \(call.id)") |
|
|
|
|
// } |
|
|
|
|
let results = try await StoreCenter.main.execute(apiCalls: apiCalls) |
|
|
|
|
for result in results { |
|
|
|
|
switch result.status { |
|
|
|
|
|