Skip to main content

origin_sync/
policy.rs

1use crate::Backoff;
2use time::Duration;
3
4/// When a target should be synchronised.
5///
6/// The same engine serves a one-minute notification poll and a six-hour analytics
7/// refresh — only these numbers differ.
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub struct SyncPolicy {
10    /// Cadence while everything is healthy.
11    pub interval: Duration,
12
13    /// Floor between two runs, however often a refresh is requested.
14    ///
15    /// Protects against a user holding the refresh button and against a UI that
16    /// re-syncs on every window focus.
17    pub min_interval: Duration,
18
19    /// How to back off after failures.
20    pub backoff: Backoff,
21
22    /// Retry delay while the machine appears to be offline.
23    ///
24    /// Short and flat rather than exponential: connectivity usually returns in one
25    /// step, and backing off for half an hour would leave the app stale long after
26    /// the network came back.
27    pub offline_retry: Duration,
28}
29
30impl Default for SyncPolicy {
31    fn default() -> Self {
32        Self {
33            interval: Duration::minutes(5),
34            min_interval: Duration::seconds(30),
35            backoff: Backoff::default(),
36            offline_retry: Duration::seconds(20),
37        }
38    }
39}
40
41impl SyncPolicy {
42    /// A policy that runs every `interval`.
43    pub fn every(interval: Duration) -> Self {
44        Self {
45            interval,
46            // A floor of a tenth of the cadence, but never below five seconds.
47            min_interval: (interval / 10_i32).max(Duration::seconds(5)),
48            ..Self::default()
49        }
50    }
51
52    pub fn with_min_interval(mut self, min_interval: Duration) -> Self {
53        self.min_interval = min_interval;
54        self
55    }
56
57    pub fn with_backoff(mut self, backoff: Backoff) -> Self {
58        self.backoff = backoff;
59        self
60    }
61
62    pub fn with_offline_retry(mut self, offline_retry: Duration) -> Self {
63        self.offline_retry = offline_retry;
64        self
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn a_fast_cadence_still_keeps_a_sane_floor() {
74        assert_eq!(
75            SyncPolicy::every(Duration::seconds(10)).min_interval,
76            Duration::seconds(5)
77        );
78    }
79
80    #[test]
81    fn a_slow_cadence_scales_its_floor() {
82        assert_eq!(
83            SyncPolicy::every(Duration::hours(1)).min_interval,
84            Duration::minutes(6)
85        );
86    }
87}