Skip to main content

inc_complete/db/
handle.rs

1use crate::{
2    Cell, Computation, Db, Storage,
3    accumulate::{Accumulate, Accumulated},
4    storage::StorageFor,
5};
6
7use super::DbGet;
8
9/// A handle to the database during some operation.
10///
11/// This wraps calls to the Db so that any `get` calls
12/// will be automatically registered as dependencies of
13/// the current operation.
14pub struct DbHandle<'db, S> {
15    db: &'db Db<S>,
16    current_operation: Cell,
17}
18
19impl<'db, S> DbHandle<'db, S> {
20    pub(crate) fn new(db: &'db Db<S>, current_operation: Cell) -> Self {
21        // We're re-running a cell so remove any past dependencies
22        let mut cell = db.cells.get_mut(&current_operation).unwrap();
23
24        cell.dependencies.clear();
25        cell.input_dependencies.clear();
26
27        Self {
28            db,
29            current_operation,
30        }
31    }
32
33    /// Retrieve an immutable reference to this `Db`'s storage
34    ///
35    /// Note that any mutations made to the storage using this are _not_ tracked by the database!
36    /// Using this incorrectly may break correctness!
37    pub fn storage(&self) -> &S {
38        self.db.storage()
39    }
40}
41
42impl<S: Storage> DbHandle<'_, S> {
43    /// Locking behavior: This function locks the cell corresponding to the given computation. This
44    /// can cause a deadlock if the computation recursively depends on itself.
45    pub fn get<C: Computation>(&self, compute: C) -> C::Output
46    where
47        S: StorageFor<C>,
48    {
49        // Register the dependency
50        let dependency = self.db.get_or_insert_cell(compute);
51        self.update_and_register_dependency::<C>(dependency);
52
53        // Fetch the current value of the dependency, running it if out of date
54        self.db.get_with_cell(dependency)
55    }
56
57    /// Registers the given cell as a dependency, running it and updating any required metadata
58    fn update_and_register_dependency<C: Computation>(&self, dependency: Cell) {
59        self.update_and_register_dependency_inner(dependency, C::IS_INPUT);
60    }
61
62    fn update_and_register_dependency_inner(&self, dependency: Cell, is_input: bool) {
63        let mut cell = self.db.cells.get_mut(&self.current_operation).unwrap();
64
65        // If `dependency` is an input it must be remembered both as a dependency
66        // and as an input dependency. Otherwise we cannot differentiate between
67        // computations which directly depend on inputs and those that only indirectly
68        // depend on them.
69        cell.dependencies.push(dependency);
70        if is_input {
71            cell.input_dependencies.insert(dependency);
72        }
73
74        drop(cell);
75
76        // Run the computation to update its dependencies before we query them afterward
77        self.db.update_cell(dependency);
78
79        let dependency = self.db.cells.get(&dependency).unwrap();
80        let dependency_inputs = dependency.input_dependencies.clone();
81        drop(dependency);
82
83        let mut cell = self.db.cells.get_mut(&self.current_operation).unwrap();
84        for input in dependency_inputs {
85            cell.input_dependencies.insert(input);
86        }
87    }
88
89    /// Accumulate an item in the current computation. This item can be retrieved along
90    /// with all other accumulated items in this computation and its dependencies via
91    /// a call to `get_accumulated`.
92    ///
93    /// This is most often used for operations like pushing diagnostics or logs.
94    pub fn accumulate<Item>(&self, item: Item)
95    where
96        S: Accumulate<Item>,
97    {
98        self.storage().accumulate(self.current_operation, item);
99    }
100
101    /// Retrieve an accumulated value in a container of the user's choice.
102    /// This will return all the accumulated items after the given computation.
103    ///
104    /// This is most often used for operations like retrieving diagnostics or logs.
105    pub fn get_accumulated<Item, C>(&self, compute: C) -> Vec<Item>
106    where
107        C: Computation,
108        Item: 'static,
109        S: StorageFor<Accumulated<Item>> + StorageFor<C> + Accumulate<Item>,
110    {
111        let dependency = self.db.get_or_insert_cell(compute);
112        self.get_accumulated_with_cell::<Item>(dependency)
113    }
114
115    /// Retrieve an accumulated value in a container of the user's choice.
116    /// This will return all the accumulated items after the given computation.
117    /// The order of accumulated items will be in the order dependencies are called,
118    /// with any items in the current computation itself being last. Within a single
119    /// computation, items are in the order they are emitted.
120    ///
121    /// This is the implementation of the publically accessible `db.get(Accumulated::<Item>(MyComputation))`.
122    ///
123    /// This is most often used for operations like retrieving diagnostics or logs.
124    pub(crate) fn get_accumulated_with_cell<Item>(&self, cell_id: Cell) -> Vec<Item>
125    where
126        Item: 'static,
127        S: StorageFor<Accumulated<Item>> + Accumulate<Item>,
128    {
129        self.update_and_register_dependency_inner(cell_id, false);
130        let dependencies = self.db.with_cell(cell_id, |cell| cell.dependencies.clone());
131
132        // Collect `Accumulator` results from each dependency. This should also ensure we
133        // rerun this if any dependency changes, even if `cell_id` is updated such that it
134        // uses different dependencies but its output remains the same.
135        let computation_id = Accumulated::<Item>::computation_id();
136        let mut result: Vec<Item> = dependencies
137            .into_iter()
138            // Filter out `Accumulated<Item>` cells from the dep list — they exist for staleness
139            // tracking only and must not be traversed for value collection, or we'd get duplicates.
140            .filter(|&dep| self.db.with_cell(dep, |cell| cell.computation_id) != computation_id)
141            .flat_map(|dependency| self.get(Accumulated::<Item>::new(dependency)))
142            .collect();
143
144        result.extend(self.storage().get_accumulated::<Vec<Item>>(cell_id));
145        result
146    }
147}
148
149impl<'db, S, C> DbGet<C> for DbHandle<'db, S>
150where
151    C: Computation,
152    S: Storage + StorageFor<C>,
153{
154    fn get(&self, key: C) -> C::Output {
155        self.get(key)
156    }
157}