Skip to main content

coven/
handle_sync.rs

1//! Starting, stopping and requesting sync passes on the handle's loop (E5).
2use crate::*;
3
4impl CovenHandle {
5    /// Start syncing; the first pass builds an absent client from custody
6    /// credentials and backs off like any failed pass. Already running or
7    /// unconfigured stores do nothing.
8    pub async fn start_sync(&self) -> Result<(), SyncError> {
9        self.storage.start().await
10    }
11    /// Stop syncing: `coven_retry_effect` says sync isn't running and the
12    /// waiting retries are answered `NotSyncing` first; then the active pass
13    /// and transfers finish, the provider client is released and the
14    /// location recorded as custody holds it, and this returns. Once it has,
15    /// no pass or upload runs until sync starts again, whatever connects the
16    /// handle meanwhile. Keys remain unlocked for the
17    /// handle session. A `start_sync` asked meanwhile starts after it, and
18    /// dropping this future leaves stopping to finish. Stopping changes
19    /// nothing the sync state reads; a release failure is recorded as the
20    /// last pass's failure and returned. A failure writing either row has
21    /// failed the handle; stopping goes on and the failure is returned.
22    pub async fn stop_sync(&self) -> Result<(), SyncError> {
23        self.storage.stop().await
24    }
25    /// Request a pass while started. Requests during a pass, and local writes
26    /// committed during it, retain one more pass; the pass's own bookkeeping
27    /// retains none. A failed connection's backoff and a provider cooldown
28    /// hold requests until they end.
29    pub fn sync_now(&self) {
30        self.storage.sync_now();
31    }
32    /// The person asked to retry everything now, such as by pulling to
33    /// refresh. Every ordinary backoff ends, answered or not: the
34    /// connection's, operations' and uploads'; a pass runs now. Provider cooldowns hold, and failures that never retry stay. An
35    /// ended wait keeps its failure count, so a retry that fails again waits
36    /// the next longer delay. Requests coalesce as `sync_now` requests do.
37    ///
38    /// The retry takes effect once this returns, with the answer as a
39    /// future, which resolves at once as `coven_retry_effect` says when no
40    /// pass runs for it: `CoolingDown` with when the provider cooldown ends,
41    /// `Failed` with a failure only the person can clear, or `NotSyncing`
42    /// while sync is stopped. Otherwise it resolves when the first pass
43    /// after the retry ends: `Synced`, or `Failed` with why. A closed handle
44    /// answers `NotSyncing`. Dropping the future leaves the retry in place.
45    /// Deciding the answer writes the retry effect's row; a failed write has
46    /// failed the handle, and is returned.
47    pub async fn retry_now(&self) -> Result<RetryAnswer, SyncError> {
48        Ok(self.waits.retry_requested().await?)
49    }
50    /// Hold the connection's passes, or let them run again, with sync left
51    /// as it is: a test runs uploads with no pass beside them so.
52    #[cfg(test)]
53    pub(crate) async fn test_hold_passes(&self, held: bool) {
54        self.storage.hold_passes(held).await;
55    }
56    /// Follow `coven_retry_effect` with a live query, as an app does (E5).
57    #[cfg(any(test, feature = "test-utils"))]
58    pub fn test_retry_effect(&self) -> coven_database::test_utils::Followed<RetryEffect, DbError> {
59        RetryEffect::follow(&self.database)
60    }
61    /// Run idle passes at most `interval` apart, instead of every 30
62    /// seconds, until the returned guard drops, such as while a screen
63    /// waits for another device. With several guards held, the shortest
64    /// interval wins; once the last drops, idle passes return to every 30
65    /// seconds. The next idle pass comes `interval` after the last pass
66    /// ended, so one that is already due runs at once. Connection backoff
67    /// and provider cooldowns still hold every pass. The request outlives
68    /// stopping and starting sync, and does nothing while sync is stopped.
69    pub fn sync_often(&self, interval: std::time::Duration) -> SyncOftenGuard {
70        self.state.sync_often(interval)
71    }
72}
73
74#[cfg(test)]
75#[path = "handle_sync_tests.rs"]
76mod tests;