turbo_tasks_backend/
backing_storage.rs

1use std::{any::type_name, sync::Arc};
2
3use anyhow::Result;
4use either::Either;
5use smallvec::SmallVec;
6use turbo_tasks::{TaskId, backend::CachedTaskType};
7
8use crate::{
9    backend::{AnyOperation, TaskDataCategory},
10    data::CachedDataItem,
11    utils::chunked_vec::ChunkedVec,
12};
13
14/// Represents types accepted by [`TurboTasksBackend::new`]. Typically this is the value returned by
15/// [`default_backing_storage`] or [`noop_backing_storage`].
16///
17/// This trait is [sealed]. External crates are not allowed to implement it.
18///
19/// [`default_backing_storage`]: crate::default_backing_storage
20/// [`noop_backing_storage`]: crate::noop_backing_storage
21/// [`TurboTasksBackend::new`]: crate::TurboTasksBackend::new
22/// [sealed]: https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/
23pub trait BackingStorage: BackingStorageSealed {
24    /// Called when the database should be invalidated upon re-initialization.
25    ///
26    /// This typically means that we'll restart the process or `turbo-tasks` soon with a fresh
27    /// database. If this happens, there's no point in writing anything else to disk, or flushing
28    /// during [`KeyValueDatabase::shutdown`].
29    ///
30    /// This can be implemented by calling [`invalidate_db`] with
31    /// the database's non-versioned base path.
32    ///
33    /// [`KeyValueDatabase::shutdown`]: crate::database::key_value_database::KeyValueDatabase::shutdown
34    /// [`invalidate_db`]: crate::database::db_invalidation::invalidate_db
35    fn invalidate(&self, reason_code: &str) -> Result<()>;
36}
37
38/// Private methods used by [`BackingStorage`]. This trait is `pub` (because of the sealed-trait
39/// pattern), but should not be exported outside of the crate.
40///
41/// [`BackingStorage`] is exported for documentation reasons and to expose the public
42/// [`BackingStorage::invalidate`] method.
43pub trait BackingStorageSealed: 'static + Send + Sync {
44    type ReadTransaction<'l>;
45    fn next_free_task_id(&self) -> Result<TaskId>;
46    fn uncompleted_operations(&self) -> Result<Vec<AnyOperation>>;
47    #[allow(clippy::ptr_arg)]
48    fn serialize(&self, task: TaskId, data: &Vec<CachedDataItem>) -> Result<SmallVec<[u8; 16]>>;
49    fn save_snapshot<I>(
50        &self,
51        operations: Vec<Arc<AnyOperation>>,
52        task_cache_updates: Vec<ChunkedVec<(Arc<CachedTaskType>, TaskId)>>,
53        snapshots: Vec<I>,
54    ) -> Result<()>
55    where
56        I: Iterator<
57                Item = (
58                    TaskId,
59                    Option<SmallVec<[u8; 16]>>,
60                    Option<SmallVec<[u8; 16]>>,
61                ),
62            > + Send
63            + Sync;
64    fn start_read_transaction(&self) -> Option<Self::ReadTransaction<'_>>;
65    /// # Safety
66    ///
67    /// `tx` must be a transaction from this BackingStorage instance.
68    unsafe fn forward_lookup_task_cache(
69        &self,
70        tx: Option<&Self::ReadTransaction<'_>>,
71        key: &CachedTaskType,
72    ) -> Result<Option<TaskId>>;
73    /// # Safety
74    ///
75    /// `tx` must be a transaction from this BackingStorage instance.
76    unsafe fn reverse_lookup_task_cache(
77        &self,
78        tx: Option<&Self::ReadTransaction<'_>>,
79        task_id: TaskId,
80    ) -> Result<Option<Arc<CachedTaskType>>>;
81    /// # Safety
82    ///
83    /// `tx` must be a transaction from this BackingStorage instance.
84    unsafe fn lookup_data(
85        &self,
86        tx: Option<&Self::ReadTransaction<'_>>,
87        task_id: TaskId,
88        category: TaskDataCategory,
89    ) -> Result<Vec<CachedDataItem>>;
90
91    fn shutdown(&self) -> Result<()> {
92        Ok(())
93    }
94}
95
96impl<L, R> BackingStorage for Either<L, R>
97where
98    L: BackingStorage,
99    R: BackingStorage,
100{
101    fn invalidate(&self, reason_code: &str) -> Result<()> {
102        either::for_both!(self, this => this.invalidate(reason_code))
103    }
104}
105
106impl<L, R> BackingStorageSealed for Either<L, R>
107where
108    L: BackingStorageSealed,
109    R: BackingStorageSealed,
110{
111    type ReadTransaction<'l> = Either<L::ReadTransaction<'l>, R::ReadTransaction<'l>>;
112
113    fn next_free_task_id(&self) -> Result<TaskId> {
114        either::for_both!(self, this => this.next_free_task_id())
115    }
116
117    fn uncompleted_operations(&self) -> Result<Vec<AnyOperation>> {
118        either::for_both!(self, this => this.uncompleted_operations())
119    }
120
121    fn serialize(&self, task: TaskId, data: &Vec<CachedDataItem>) -> Result<SmallVec<[u8; 16]>> {
122        either::for_both!(self, this => this.serialize(task, data))
123    }
124
125    fn save_snapshot<I>(
126        &self,
127        operations: Vec<Arc<AnyOperation>>,
128        task_cache_updates: Vec<ChunkedVec<(Arc<CachedTaskType>, TaskId)>>,
129        snapshots: Vec<I>,
130    ) -> Result<()>
131    where
132        I: Iterator<
133                Item = (
134                    TaskId,
135                    Option<SmallVec<[u8; 16]>>,
136                    Option<SmallVec<[u8; 16]>>,
137                ),
138            > + Send
139            + Sync,
140    {
141        either::for_both!(self, this => this.save_snapshot(
142            operations,
143            task_cache_updates,
144            snapshots,
145        ))
146    }
147
148    fn start_read_transaction(&self) -> Option<Self::ReadTransaction<'_>> {
149        Some(match self {
150            Either::Left(this) => Either::Left(this.start_read_transaction()?),
151            Either::Right(this) => Either::Right(this.start_read_transaction()?),
152        })
153    }
154
155    unsafe fn forward_lookup_task_cache(
156        &self,
157        tx: Option<&Self::ReadTransaction<'_>>,
158        key: &CachedTaskType,
159    ) -> Result<Option<TaskId>> {
160        match self {
161            Either::Left(this) => {
162                let tx = tx.map(|tx| read_transaction_left_or_panic(tx.as_ref()));
163                unsafe { this.forward_lookup_task_cache(tx, key) }
164            }
165            Either::Right(this) => {
166                let tx = tx.map(|tx| read_transaction_right_or_panic(tx.as_ref()));
167                unsafe { this.forward_lookup_task_cache(tx, key) }
168            }
169        }
170    }
171
172    unsafe fn reverse_lookup_task_cache(
173        &self,
174        tx: Option<&Self::ReadTransaction<'_>>,
175        task_id: TaskId,
176    ) -> Result<Option<Arc<CachedTaskType>>> {
177        match self {
178            Either::Left(this) => {
179                let tx = tx.map(|tx| read_transaction_left_or_panic(tx.as_ref()));
180                unsafe { this.reverse_lookup_task_cache(tx, task_id) }
181            }
182            Either::Right(this) => {
183                let tx = tx.map(|tx| read_transaction_right_or_panic(tx.as_ref()));
184                unsafe { this.reverse_lookup_task_cache(tx, task_id) }
185            }
186        }
187    }
188
189    unsafe fn lookup_data(
190        &self,
191        tx: Option<&Self::ReadTransaction<'_>>,
192        task_id: TaskId,
193        category: TaskDataCategory,
194    ) -> Result<Vec<CachedDataItem>> {
195        match self {
196            Either::Left(this) => {
197                let tx = tx.map(|tx| read_transaction_left_or_panic(tx.as_ref()));
198                unsafe { this.lookup_data(tx, task_id, category) }
199            }
200            Either::Right(this) => {
201                let tx = tx.map(|tx| read_transaction_right_or_panic(tx.as_ref()));
202                unsafe { this.lookup_data(tx, task_id, category) }
203            }
204        }
205    }
206
207    fn shutdown(&self) -> Result<()> {
208        either::for_both!(self, this => this.shutdown())
209    }
210}
211
212// similar to `Either::unwrap_left`, but does not require `R: Debug`.
213fn read_transaction_left_or_panic<L, R>(either: Either<L, R>) -> L {
214    match either {
215        Either::Left(l) => l,
216        Either::Right(_) => panic!(
217            "expected ReadTransaction of Either::Left containing {}, received Either::Right type \
218             of {}",
219            type_name::<L>(),
220            type_name::<R>(),
221        ),
222    }
223}
224
225// similar to `Either::unwrap_right`, but does not require `R: Debug`.
226fn read_transaction_right_or_panic<L, R>(either: Either<L, R>) -> R {
227    match either {
228        Either::Left(_) => panic!(
229            "expected ReadTransaction of Either::Right containing {}, received Either::Left type \
230             of {}",
231            type_name::<R>(),
232            type_name::<L>(),
233        ),
234        Either::Right(r) => r,
235    }
236}