Prevent newly added sync status records from being deleted and process them incrementally

This commit is contained in:
Maurice Parker 2020-05-02 22:52:13 -05:00
parent e681fd6402
commit 578d22f3c2
3 changed files with 61 additions and 46 deletions

View File

@ -16,7 +16,8 @@ import SyncDatabase
class CloudKitSendStatusOperation: MainThreadOperation { class CloudKitSendStatusOperation: MainThreadOperation {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit") private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")
private let blockSize = 300
// MainThreadOperation // MainThreadOperation
public var isCanceled = false public var isCanceled = false
public var id: Int? public var id: Int?
@ -40,51 +41,61 @@ class CloudKitSendStatusOperation: MainThreadOperation {
func run() { func run() {
os_log(.debug, log: log, "Sending article statuses...") os_log(.debug, log: log, "Sending article statuses...")
database.selectForProcessing { result in if showProgress {
func processStatuses(_ syncStatuses: [SyncStatus]) { database.selectPendingCount() { result in
guard syncStatuses.count > 0 else { switch result {
os_log(.debug, log: self.log, "Done sending article statuses.") case .success(let count):
self.operationDelegate?.operationDidComplete(self) let ticks = count / self.blockSize
return self.refreshProgress?.addToNumberOfTasksAndRemaining(ticks)
self.selectForProcessing()
case .failure(let databaseError):
os_log(.error, log: self.log, "Send status count pending error: %@.", databaseError.localizedDescription)
self.operationDelegate?.cancelOperation(self)
} }
let group = DispatchGroup()
let syncStatusChunks = syncStatuses.chunked(into: 300)
if self.showProgress {
self.refreshProgress?.addToNumberOfTasksAndRemaining(syncStatusChunks.count)
}
for syncStatusChunk in syncStatusChunks {
group.enter()
self.sendArticleStatusChunk(syncStatuses: syncStatusChunk) {
group.leave()
}
}
group.notify(queue: DispatchQueue.global(qos: .background)) {
os_log(.debug, log: self.log, "Done sending article statuses.")
DispatchQueue.main.async {
self.operationDelegate?.operationDidComplete(self)
}
}
}
switch result {
case .success(let syncStatuses):
processStatuses(syncStatuses)
case .failure(let databaseError):
os_log(.error, log: self.log, "Send status error: %@.", databaseError.localizedDescription)
self.operationDelegate?.cancelOperation(self)
} }
} else {
selectForProcessing()
} }
} }
func sendArticleStatusChunk(syncStatuses: [SyncStatus], completion: @escaping () -> Void) { }
private extension CloudKitSendStatusOperation {
func selectForProcessing() {
database.selectForProcessing(limit: blockSize) { result in
switch result {
case .success(let syncStatuses):
guard syncStatuses.count > 0 else {
if self.showProgress {
self.refreshProgress?.completeTask()
}
os_log(.debug, log: self.log, "Done sending article statuses.")
self.operationDelegate?.operationDidComplete(self)
return
}
self.processStatuses(syncStatuses) {
self.selectForProcessing()
}
case .failure(let databaseError):
os_log(.error, log: self.log, "Send status error: %@.", databaseError.localizedDescription)
self.operationDelegate?.cancelOperation(self)
}
}
}
func processStatuses(_ syncStatuses: [SyncStatus], completion: @escaping () -> Void) {
guard let account = account, let articlesZone = articlesZone else { guard let account = account, let articlesZone = articlesZone else {
completion() completion()
return return
@ -107,7 +118,8 @@ class CloudKitSendStatusOperation: MainThreadOperation {
switch result { switch result {
case .success: case .success:
self.database.deleteSelectedForProcessing(syncStatuses.map({ $0.articleID })) { _ in self.database.deleteSelectedForProcessing(syncStatuses.map({ $0.articleID })) { _ in
if self.showProgress { // Don't clear the last one since we might have had additional ticks added
if self.showProgress && self.refreshProgress?.numberRemaining ?? 0 > 1 {
self.refreshProgress?.completeTask() self.refreshProgress?.completeTask()
} }
os_log(.debug, log: self.log, "Done sending article status block...") os_log(.debug, log: self.log, "Done sending article status block...")

View File

@ -36,8 +36,8 @@ public struct SyncDatabase {
try syncStatusTable.insertStatuses(statuses) try syncStatusTable.insertStatuses(statuses)
} }
public func selectForProcessing(completion: @escaping SyncStatusesCompletionBlock) { public func selectForProcessing(limit: Int? = nil, completion: @escaping SyncStatusesCompletionBlock) {
return syncStatusTable.selectForProcessing(completion) return syncStatusTable.selectForProcessing(limit: limit, completion: completion)
} }
public func selectPendingCount(completion: @escaping DatabaseIntCompletionBlock) { public func selectPendingCount(completion: @escaping DatabaseIntCompletionBlock) {

View File

@ -20,7 +20,7 @@ struct SyncStatusTable: DatabaseTable {
self.queue = queue self.queue = queue
} }
func selectForProcessing(_ completion: @escaping SyncStatusesCompletionBlock) { func selectForProcessing(limit: Int?, completion: @escaping SyncStatusesCompletionBlock) {
queue.runInTransaction { databaseResult in queue.runInTransaction { databaseResult in
var statuses = Set<SyncStatus>() var statuses = Set<SyncStatus>()
var error: DatabaseError? var error: DatabaseError?
@ -29,7 +29,10 @@ struct SyncStatusTable: DatabaseTable {
let updateSQL = "update syncStatus set selected = true" let updateSQL = "update syncStatus set selected = true"
database.executeUpdate(updateSQL, withArgumentsIn: nil) database.executeUpdate(updateSQL, withArgumentsIn: nil)
let selectSQL = "select * from syncStatus where selected == true" var selectSQL = "select * from syncStatus where selected == true"
if let limit = limit {
selectSQL = "\(selectSQL) limit \(limit)"
}
if let resultSet = database.executeQuery(selectSQL, withArgumentsIn: nil) { if let resultSet = database.executeQuery(selectSQL, withArgumentsIn: nil) {
statuses = resultSet.mapToSet(self.statusWithRow) statuses = resultSet.mapToSet(self.statusWithRow)
} }
@ -135,7 +138,7 @@ struct SyncStatusTable: DatabaseTable {
func makeDatabaseCall(_ database: FMDatabase) { func makeDatabaseCall(_ database: FMDatabase) {
let parameters = articleIDs.map { $0 as AnyObject } let parameters = articleIDs.map { $0 as AnyObject }
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(articleIDs.count))! let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(articleIDs.count))!
let deleteSQL = "delete from syncStatus where articleID in \(placeholders)" let deleteSQL = "delete from syncStatus where selected = true and articleID in \(placeholders)"
database.executeUpdate(deleteSQL, withArgumentsIn: parameters) database.executeUpdate(deleteSQL, withArgumentsIn: parameters)
} }