dogear/
store.rs

1// Copyright 2018-2019 Mozilla
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{time::Duration, time::Instant};
16
17use crate::driver::{
18    AbortSignal, DefaultAbortSignal, DefaultDriver, Driver, TelemetryEvent, TreeStats,
19};
20use crate::error::Error;
21use crate::guid::Guid;
22use crate::merge::{MergedRoot, Merger};
23use crate::tree::Tree;
24
25/// A store is the main interface to Dogear. It implements methods for building
26/// local and remote trees from a storage backend, fetching content info for
27/// matching items with similar contents, and persisting the merged tree.
28pub trait Store {
29    /// The type returned from a successful merge.
30    type Ok;
31
32    /// The type returned in the event of a store error.
33    type Error: From<Error>;
34
35    /// Builds a fully rooted, consistent tree from the items and tombstones in
36    /// the local store.
37    fn fetch_local_tree(&self) -> Result<Tree, Self::Error>;
38
39    /// Builds a fully rooted, consistent tree from the items and tombstones in
40    /// the mirror.
41    fn fetch_remote_tree(&self) -> Result<Tree, Self::Error>;
42
43    /// Applies the merged root to the local store, and stages items for
44    /// upload. On Desktop, this method inserts the merged tree into a temp
45    /// table, updates Places, and inserts outgoing items into another
46    /// temp table.
47    fn apply<'t>(&mut self, root: MergedRoot<'t>) -> Result<Self::Ok, Self::Error>;
48
49    /// Builds and applies a merged tree using the default merge driver.
50    fn merge(&mut self) -> Result<Self::Ok, Self::Error> {
51        self.merge_with_driver(&DefaultDriver, &DefaultAbortSignal)
52    }
53
54    /// Builds a complete merged tree from the local and remote trees, resolves
55    /// conflicts, dedupes local items, and applies the merged tree using the
56    /// given driver.
57    fn merge_with_driver(
58        &mut self,
59        driver: &impl Driver,
60        signal: &impl AbortSignal,
61    ) -> Result<Self::Ok, Self::Error> {
62        signal.err_if_aborted()?;
63        debug!(driver, "Building local tree");
64        let (local_tree, time) = with_timing(|| self.fetch_local_tree())?;
65        driver.record_telemetry_event(TelemetryEvent::FetchLocalTree(TreeStats {
66            items: local_tree.size(),
67            deletions: local_tree.deletions().len(),
68            problems: local_tree.problems().counts(),
69            time,
70        }));
71        trace!(driver, "Built local tree from mirror\n{}", local_tree);
72
73        signal.err_if_aborted()?;
74        debug!(driver, "Building remote tree");
75        let (remote_tree, time) = with_timing(|| self.fetch_remote_tree())?;
76        driver.record_telemetry_event(TelemetryEvent::FetchRemoteTree(TreeStats {
77            items: remote_tree.size(),
78            deletions: local_tree.deletions().len(),
79            problems: remote_tree.problems().counts(),
80            time,
81        }));
82        trace!(driver, "Built remote tree from mirror\n{}", remote_tree);
83
84        signal.err_if_aborted()?;
85        debug!(driver, "Building merged tree");
86        let merger = Merger::with_driver(driver, signal, &local_tree, &remote_tree);
87        let (merged_root, time) = with_timing(|| merger.merge())?;
88        driver.record_telemetry_event(TelemetryEvent::Merge(time, *merged_root.counts()));
89        trace!(
90            driver,
91            "Built new merged tree\n{}\nDelete Locally: [{}]\nDelete Remotely: [{}]",
92            merged_root.node().to_ascii_string(),
93            merged_root
94                .local_deletions()
95                .map(Guid::as_str)
96                .collect::<Vec<_>>()
97                .join(", "),
98            merged_root
99                .remote_deletions()
100                .map(Guid::as_str)
101                .collect::<Vec<_>>()
102                .join(", ")
103        );
104
105        signal.err_if_aborted()?;
106        debug!(driver, "Applying merged tree");
107        let (result, time) = with_timing(|| self.apply(merged_root))?;
108        driver.record_telemetry_event(TelemetryEvent::Apply(time));
109
110        Ok(result)
111    }
112}
113
114fn with_timing<T, E>(run: impl FnOnce() -> Result<T, E>) -> Result<(T, Duration), E> {
115    let now = Instant::now();
116    run().map(|value| (value, now.elapsed()))
117}