Persist cache with pull-to-refresh + Sync rate limiting
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
|
||||
class SyncUpdate {
|
||||
private var lastSync: TimeInterval = 0
|
||||
private var timer: Timer!
|
||||
private var paused: Int = 1 // first start() will decrement
|
||||
private(set) var tsEarliest: Timestamp
|
||||
@@ -9,47 +10,65 @@ class SyncUpdate {
|
||||
tsEarliest = Pref.DateFilter.lastXMinTimestamp() ?? 0
|
||||
NotifyDateFilterChanged.observe(call: #selector(didChangeDateFilter), on: self)
|
||||
timer = Timer.repeating(interval, call: #selector(periodicUpdate), on: self)
|
||||
syncNow() // because timer will only fire after interval
|
||||
}
|
||||
|
||||
@objc private func periodicUpdate() { if paused == 0 { syncNow() } }
|
||||
|
||||
@objc private func didChangeDateFilter() {
|
||||
let lastXFilter = Pref.DateFilter.lastXMinTimestamp() ?? 0
|
||||
let before = tsEarliest
|
||||
tsEarliest = lastXFilter
|
||||
if before < lastXFilter {
|
||||
DispatchQueue.global().async {
|
||||
if let excess = AppDB?.dnsLogsRowRange(between: before, and: lastXFilter) {
|
||||
NotifySyncRemove.postAsyncMain(excess)
|
||||
}
|
||||
}
|
||||
} else if before > lastXFilter {
|
||||
DispatchQueue.global().async {
|
||||
if let missing = AppDB?.dnsLogsRowRange(between: lastXFilter, and: before) {
|
||||
NotifySyncInsert.postAsyncMain(missing)
|
||||
}
|
||||
}
|
||||
DispatchQueue.global().async {
|
||||
self.set(newEarliest: Pref.DateFilter.lastXMinTimestamp() ?? 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// This will immediately resume timer updates, ignoring previous `pause()` requests.
|
||||
func start() { paused = 0 }
|
||||
|
||||
/// All calls must be balanced with `continue()` calls.
|
||||
/// Can be nested within other `pause-continue` pairs.
|
||||
/// - Warning: An execution branch that results in unbalanced pairs will completely disable updates!
|
||||
func pause() { paused += 1 }
|
||||
|
||||
/// Must be balanced with a `pause()` call. A `continue()` without a `pause()` is a `nop`.
|
||||
/// - Note: Internally the sync timer keeps running. The `pause` will simply ignore execution during that time.
|
||||
func `continue`() { if paused > 0 { paused -= 1 } }
|
||||
|
||||
/// Persist logs from cache and notify all observers. (`NotifySyncInsert`)
|
||||
/// Determine rows of outdated entries that should be removed and notify observers as well. (`NotifySyncRemove`)
|
||||
/// - Note: This method is rate limited. Sync will be performed at most once per second.
|
||||
/// - Note: This method returns immediatelly. Syncing is done in a background thread.
|
||||
func syncNow() {
|
||||
self.pause() // reduce concurrent load
|
||||
let now = Date().timeIntervalSince1970
|
||||
guard (now - lastSync) > 1 else { return } // rate limiting
|
||||
lastSync = now
|
||||
|
||||
if let inserted = AppDB?.dnsLogsPersist() { // move cache -> heap
|
||||
NotifySyncInsert.post(inserted)
|
||||
}
|
||||
if let lastXFilter = Pref.DateFilter.lastXMinTimestamp(), tsEarliest < lastXFilter {
|
||||
if let removed = AppDB?.dnsLogsRowRange(between: tsEarliest, and: lastXFilter) {
|
||||
NotifySyncRemove.post(removed)
|
||||
DispatchQueue.global().async {
|
||||
self.pause() // reduce concurrent load
|
||||
|
||||
if let inserted = AppDB?.dnsLogsPersist() { // move cache -> heap
|
||||
NotifySyncInsert.postAsyncMain(inserted)
|
||||
}
|
||||
tsEarliest = lastXFilter
|
||||
if let lastXFilter = Pref.DateFilter.lastXMinTimestamp() {
|
||||
self.set(newEarliest: lastXFilter)
|
||||
}
|
||||
// TODO: periodic hard delete old logs (will reset rowids!)
|
||||
|
||||
self.continue()
|
||||
}
|
||||
// TODO: periodic hard delete old logs (will reset rowids!)
|
||||
|
||||
self.continue()
|
||||
}
|
||||
|
||||
/// - Warning: Always call from a background thread!
|
||||
private func set(newEarliest: Timestamp) {
|
||||
let current = tsEarliest
|
||||
tsEarliest = newEarliest
|
||||
if current < newEarliest {
|
||||
if let excess = AppDB?.dnsLogsRowRange(between: current, and: newEarliest) {
|
||||
NotifySyncRemove.postAsyncMain(excess)
|
||||
}
|
||||
} else if current > newEarliest {
|
||||
if let missing = AppDB?.dnsLogsRowRange(between: newEarliest, and: current) {
|
||||
NotifySyncInsert.postAsyncMain(missing)
|
||||
}
|
||||
} // else: nothing changed
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user