Skip to main content

linera_views/
store.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! This provides the trait definitions for the stores.
5
6use std::{fmt::Debug, future::Future};
7
8use serde::{de::DeserializeOwned, Serialize};
9
10#[cfg(with_testing)]
11use crate::random::generate_test_namespace;
12use crate::{
13    batch::{Batch, SimplifiedBatch},
14    common::from_bytes_option,
15    ViewError,
16};
17
18/// The error type for the key-value stores.
19pub trait KeyValueStoreError:
20    std::error::Error + From<bcs::Error> + Debug + Send + Sync + 'static
21{
22    /// The name of the backend.
23    const BACKEND: &'static str;
24
25    /// Returns `true` if this error represents a journal resolution failure,
26    /// which may leave storage in an inconsistent state requiring a view reload.
27    fn must_reload_view(&self) -> bool {
28        false
29    }
30}
31
32impl<E: KeyValueStoreError> From<E> for ViewError {
33    fn from(error: E) -> Self {
34        let must_reload_view = error.must_reload_view();
35        Self::StoreError {
36            backend: E::BACKEND,
37            error: Box::new(error),
38            must_reload_view,
39        }
40    }
41}
42
43/// Define an associated [`KeyValueStoreError`].
44pub trait WithError {
45    /// The error type.
46    type Error: KeyValueStoreError;
47}
48
49/// Asynchronous read key-value operations.
50#[cfg_attr(not(web), trait_variant::make(Send + Sync))]
51pub trait ReadableKeyValueStore: WithError {
52    /// The maximal size of keys that can be stored.
53    const MAX_KEY_SIZE: usize;
54
55    /// Gets the root key of the store.
56    fn root_key(&self) -> Result<Vec<u8>, Self::Error>;
57
58    /// Retrieves a `Vec<u8>` from the database using the provided `key`.
59    async fn read_value_bytes(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error>;
60
61    /// Tests whether a key exists in the database
62    async fn contains_key(&self, key: &[u8]) -> Result<bool, Self::Error>;
63
64    /// Tests whether a list of keys exist in the database
65    async fn contains_keys(&self, keys: &[Vec<u8>]) -> Result<Vec<bool>, Self::Error>;
66
67    /// Retrieves multiple `Vec<u8>` from the database using the provided `keys`.
68    async fn read_multi_values_bytes(
69        &self,
70        keys: &[Vec<u8>],
71    ) -> Result<Vec<Option<Vec<u8>>>, Self::Error>;
72
73    /// Finds the `key` matching the prefix. The prefix is not included in the returned keys.
74    async fn find_keys_by_prefix(&self, key_prefix: &[u8]) -> Result<Vec<Vec<u8>>, Self::Error>;
75
76    /// Finds the `(key,value)` pairs matching the prefix. The prefix is not included in the returned keys.
77    async fn find_key_values_by_prefix(
78        &self,
79        key_prefix: &[u8],
80    ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, Self::Error>;
81
82    // We can't use `async fn` here in the below implementations due to
83    // https://github.com/rust-lang/impl-trait-utils/issues/17, but once that bug is fixed
84    // we can revert them to `async fn` syntax, which is neater.
85
86    /// Reads a single `key` and deserializes the result if present.
87    fn read_value<V: DeserializeOwned>(
88        &self,
89        key: &[u8],
90    ) -> impl Future<Output = Result<Option<V>, Self::Error>> {
91        async { Ok(from_bytes_option(&self.read_value_bytes(key).await?)?) }
92    }
93
94    /// Reads multiple `keys` and deserializes the results if present.
95    fn read_multi_values<V: DeserializeOwned + Send + Sync>(
96        &self,
97        keys: &[Vec<u8>],
98    ) -> impl Future<Output = Result<Vec<Option<V>>, Self::Error>> {
99        async {
100            let mut values = Vec::with_capacity(keys.len());
101            for entry in self.read_multi_values_bytes(keys).await? {
102                values.push(from_bytes_option(&entry)?);
103            }
104            Ok(values)
105        }
106    }
107}
108
109/// Asynchronous write key-value operations.
110#[cfg_attr(not(web), trait_variant::make(Send + Sync))]
111pub trait WritableKeyValueStore: WithError {
112    /// The maximal size of values that can be stored.
113    const MAX_VALUE_SIZE: usize;
114
115    /// Writes the `batch` in the database.
116    async fn write_batch(&self, batch: Batch) -> Result<(), Self::Error>;
117
118    /// Clears any journal entry that may remain.
119    /// The journal is located at the `root_key`.
120    async fn clear_journal(&self) -> Result<(), Self::Error>;
121}
122
123/// Asynchronous direct write key-value operations with simplified batch.
124///
125/// Some backend cannot implement `WritableKeyValueStore` directly and will require
126/// journaling.
127#[cfg_attr(not(web), trait_variant::make(Send + Sync))]
128pub trait DirectWritableKeyValueStore: WithError {
129    /// The maximal number of items in a batch.
130    const MAX_BATCH_SIZE: usize;
131
132    /// The maximal number of bytes of a batch.
133    const MAX_BATCH_TOTAL_SIZE: usize;
134
135    /// The maximal size of values that can be stored.
136    const MAX_VALUE_SIZE: usize;
137
138    /// The batch type.
139    type Batch: SimplifiedBatch + Serialize + DeserializeOwned + Default;
140
141    /// Writes the batch to the database.
142    async fn write_batch(&self, batch: Self::Batch) -> Result<(), Self::Error>;
143}
144
145/// The definition of a key-value database.
146#[cfg_attr(not(web), trait_variant::make(Send + Sync))]
147pub trait KeyValueDatabase: WithError + linera_base::util::traits::AutoTraits + Sized {
148    /// The configuration needed to interact with a new backend.
149    type Config: Send + Sync;
150
151    /// The result of opening a partition.
152    type Store;
153
154    /// The name of this database.
155    fn get_name() -> String;
156
157    /// Connects to an existing namespace using the given configuration.
158    async fn connect(config: &Self::Config, namespace: &str) -> Result<Self, Self::Error>;
159
160    /// Opens a shared partition starting at `root_key`. It is understood that the
161    /// partition MAY be read and written simultaneously from other clients.
162    fn open_shared(&self, root_key: &[u8]) -> Result<Self::Store, Self::Error>;
163
164    /// Opens an exclusive partition starting at `root_key`. It is assumed that the
165    /// partition WILL NOT be read and written simultaneously by other clients.
166    ///
167    /// IMPORTANT: This assumption is not enforced at the moment. However, future
168    /// implementations may choose to return an error if another client is detected.
169    fn open_exclusive(&self, root_key: &[u8]) -> Result<Self::Store, Self::Error>;
170
171    /// Obtains the list of existing namespaces.
172    async fn list_all(config: &Self::Config) -> Result<Vec<String>, Self::Error>;
173
174    /// Lists the root keys of the namespace.
175    /// It is possible that some root keys have no keys.
176    async fn list_root_keys(&self) -> Result<Vec<Vec<u8>>, Self::Error>;
177
178    /// Deletes all the existing namespaces.
179    fn delete_all(config: &Self::Config) -> impl Future<Output = Result<(), Self::Error>> {
180        async {
181            let namespaces = Self::list_all(config).await?;
182            for namespace in namespaces {
183                Self::delete(config, &namespace).await?;
184            }
185            Ok(())
186        }
187    }
188
189    /// Tests if a given namespace exists.
190    async fn exists(config: &Self::Config, namespace: &str) -> Result<bool, Self::Error>;
191
192    /// Creates a namespace. Returns an error if the namespace exists.
193    async fn create(config: &Self::Config, namespace: &str) -> Result<(), Self::Error>;
194
195    /// Deletes the given namespace.
196    async fn delete(config: &Self::Config, namespace: &str) -> Result<(), Self::Error>;
197
198    /// Initializes a storage if missing and provides it.
199    fn maybe_create_and_connect(
200        config: &Self::Config,
201        namespace: &str,
202    ) -> impl Future<Output = Result<Self, Self::Error>> {
203        async {
204            if !Self::exists(config, namespace).await? {
205                Self::create(config, namespace).await?;
206            }
207            Self::connect(config, namespace).await
208        }
209    }
210
211    /// Creates a new storage. Overwrites it if this namespace already exists.
212    fn recreate_and_connect(
213        config: &Self::Config,
214        namespace: &str,
215    ) -> impl Future<Output = Result<Self, Self::Error>> {
216        async {
217            if Self::exists(config, namespace).await? {
218                Self::delete(config, namespace).await?;
219            }
220            Self::create(config, namespace).await?;
221            Self::connect(config, namespace).await
222        }
223    }
224}
225
226/// A key-value store that can perform both read and direct write operations.
227///
228/// This trait combines the capabilities of [`ReadableKeyValueStore`] and
229/// [`DirectWritableKeyValueStore`], providing a full interface for stores
230/// that can handle simplified batches directly without journaling.
231pub trait DirectKeyValueStore: ReadableKeyValueStore + DirectWritableKeyValueStore {}
232
233impl<T> DirectKeyValueStore for T where T: ReadableKeyValueStore + DirectWritableKeyValueStore {}
234
235/// A key-value store that can perform both read and write operations.
236///
237/// This trait combines the capabilities of [`ReadableKeyValueStore`] and
238/// [`WritableKeyValueStore`], providing a full interface for stores that
239/// can handle complex batches with journaling support.
240pub trait KeyValueStore: ReadableKeyValueStore + WritableKeyValueStore {}
241
242impl<T> KeyValueStore for T where T: ReadableKeyValueStore + WritableKeyValueStore {}
243
244/// The functions needed for testing purposes
245#[cfg(with_testing)]
246pub trait TestKeyValueDatabase: KeyValueDatabase {
247    /// Obtains a test config
248    async fn new_test_config() -> Result<Self::Config, Self::Error>;
249
250    /// Creates a database for testing purposes
251    async fn connect_test_namespace() -> Result<Self, Self::Error> {
252        let config = Self::new_test_config().await?;
253        let namespace = generate_test_namespace();
254        Self::recreate_and_connect(&config, &namespace).await
255    }
256
257    /// Creates a store for testing purposes
258    async fn new_test_store() -> Result<Self::Store, Self::Error> {
259        let database = Self::connect_test_namespace().await?;
260        database.open_shared(&[])
261    }
262}
263
264/// A module containing a dummy store used for caching views.
265pub mod inactive_store {
266    use super::*;
267
268    /// A store which does not actually store anything - used for caching views.
269    pub struct InactiveStore;
270
271    /// An error struct for the inactive store.
272    #[derive(Clone, Copy, Debug)]
273    pub struct InactiveStoreError;
274
275    impl std::fmt::Display for InactiveStoreError {
276        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
277            write!(f, "inactive store error")
278        }
279    }
280
281    impl From<bcs::Error> for InactiveStoreError {
282        fn from(_other: bcs::Error) -> Self {
283            Self
284        }
285    }
286
287    impl std::error::Error for InactiveStoreError {}
288
289    impl KeyValueStoreError for InactiveStoreError {
290        const BACKEND: &'static str = "inactive";
291    }
292
293    impl WithError for InactiveStore {
294        type Error = InactiveStoreError;
295    }
296
297    impl ReadableKeyValueStore for InactiveStore {
298        const MAX_KEY_SIZE: usize = 0;
299
300        fn root_key(&self) -> Result<Vec<u8>, Self::Error> {
301            panic!("attempt to read from an inactive store!")
302        }
303
304        async fn read_value_bytes(&self, _key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
305            panic!("attempt to read from an inactive store!")
306        }
307
308        async fn contains_key(&self, _key: &[u8]) -> Result<bool, Self::Error> {
309            panic!("attempt to read from an inactive store!")
310        }
311
312        async fn contains_keys(&self, _keys: &[Vec<u8>]) -> Result<Vec<bool>, Self::Error> {
313            panic!("attempt to read from an inactive store!")
314        }
315
316        async fn read_multi_values_bytes(
317            &self,
318            _keys: &[Vec<u8>],
319        ) -> Result<Vec<Option<Vec<u8>>>, Self::Error> {
320            panic!("attempt to read from an inactive store!")
321        }
322
323        async fn find_keys_by_prefix(
324            &self,
325            _key_prefix: &[u8],
326        ) -> Result<Vec<Vec<u8>>, Self::Error> {
327            panic!("attempt to read from an inactive store!")
328        }
329
330        /// Finds the `(key,value)` pairs matching the prefix. The prefix is not included in the returned keys.
331        async fn find_key_values_by_prefix(
332            &self,
333            _key_prefix: &[u8],
334        ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, Self::Error> {
335            panic!("attempt to read from an inactive store!")
336        }
337    }
338
339    impl WritableKeyValueStore for InactiveStore {
340        const MAX_VALUE_SIZE: usize = 0;
341
342        async fn write_batch(&self, _batch: Batch) -> Result<(), Self::Error> {
343            panic!("attempt to write to an inactive store!")
344        }
345
346        async fn clear_journal(&self) -> Result<(), Self::Error> {
347            panic!("attempt to write to an inactive store!")
348        }
349    }
350}