sync15/client/
status.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use crate::error::{Error, ErrorResponse};
6use crate::telemetry::SyncTelemetryPing;
7use std::collections::HashMap;
8use std::time::{Duration, SystemTime};
9
10/// The general status of sync - should probably be moved to the "sync manager"
11/// once we have one!
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum ServiceStatus {
14    /// Everything is fine.
15    Ok,
16    /// Some general network issue.
17    NetworkError,
18    /// Some apparent issue with the servers.
19    ServiceError,
20    /// Some external FxA action needs to be taken.
21    AuthenticationError,
22    /// We declined to do anything for backoff or rate-limiting reasons.
23    BackedOff,
24    /// We were interrupted.
25    Interrupted,
26    /// Something else - you need to check the logs for more details. May
27    /// or may not be transient, we really don't know.
28    OtherError,
29}
30
31impl ServiceStatus {
32    // This is a bit naive and probably will not survive in this form in the
33    // SyncManager - eg, we'll want to handle backoff etc.
34    pub fn from_err(err: &Error) -> ServiceStatus {
35        match err {
36            // HTTP based errors.
37            Error::TokenserverHttpError(status) => {
38                // bit of a shame the tokenserver is different to storage...
39                if *status == 401 {
40                    ServiceStatus::AuthenticationError
41                } else {
42                    ServiceStatus::ServiceError
43                }
44            }
45            // BackoffError is also from the tokenserver.
46            Error::BackoffError(_) => ServiceStatus::ServiceError,
47            Error::StorageHttpError(ref e) => match e {
48                ErrorResponse::Unauthorized { .. } => ServiceStatus::AuthenticationError,
49                _ => ServiceStatus::ServiceError,
50            },
51
52            // Network errors.
53            Error::RequestError(_) | Error::UnexpectedStatus(_) | Error::HawkError(_) => {
54                ServiceStatus::NetworkError
55            }
56
57            Error::Interrupted(_) => ServiceStatus::Interrupted,
58            _ => ServiceStatus::OtherError,
59        }
60    }
61}
62
63/// The result of a sync request. This too is from the "sync manager", but only
64/// has a fraction of the things it will have when we actually build that.
65#[derive(Debug)]
66pub struct SyncResult {
67    /// The general health.
68    pub service_status: ServiceStatus,
69
70    /// The set of declined engines, if we know them.
71    pub declined: Option<Vec<String>>,
72
73    /// The result of the sync.
74    pub result: Result<(), Error>,
75
76    /// The result for each engine.
77    /// Note that we expect the `String` to be replaced with an enum later.
78    pub engine_results: HashMap<String, Result<(), Error>>,
79
80    pub telemetry: SyncTelemetryPing,
81
82    pub next_sync_after: Option<std::time::SystemTime>,
83}
84
85// If `r` has a BackoffError, then returns the later backoff value.
86fn advance_backoff(cur_best: SystemTime, r: &Result<(), Error>) -> SystemTime {
87    if let Err(e) = r {
88        if let Some(time) = e.get_backoff() {
89            return std::cmp::max(time, cur_best);
90        }
91    }
92    cur_best
93}
94
95impl SyncResult {
96    pub(crate) fn set_sync_after(&mut self, backoff_duration: Duration) {
97        let now = SystemTime::now();
98        let toplevel = advance_backoff(now + backoff_duration, &self.result);
99        let sync_after = self.engine_results.values().fold(toplevel, advance_backoff);
100        if sync_after <= now {
101            self.next_sync_after = None;
102        } else {
103            self.next_sync_after = Some(sync_after);
104        }
105    }
106}