linera_views/views/
register_view.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4#[cfg(with_metrics)]
5use linera_base::prometheus_util::MeasureLatency as _;
6use serde::{de::DeserializeOwned, Serialize};
7
8use crate::{
9    batch::Batch,
10    common::{from_bytes_option_or_default, HasherOutput},
11    context::Context,
12    hashable_wrapper::WrappedHashableContainerView,
13    store::ReadableKeyValueStore as _,
14    views::{ClonableView, HashableView, Hasher, ReplaceContext, View},
15    ViewError,
16};
17
18#[cfg(with_metrics)]
19mod metrics {
20    use std::sync::LazyLock;
21
22    use linera_base::prometheus_util::{exponential_bucket_latencies, register_histogram_vec};
23    use prometheus::HistogramVec;
24
25    /// The runtime of hash computation
26    pub static REGISTER_VIEW_HASH_RUNTIME: LazyLock<HistogramVec> = LazyLock::new(|| {
27        register_histogram_vec(
28            "register_view_hash_runtime",
29            "RegisterView hash runtime",
30            &[],
31            exponential_bucket_latencies(5.0),
32        )
33    });
34}
35
36/// A view that supports modifying a single value of type `T`.
37#[derive(Debug)]
38pub struct RegisterView<C, T> {
39    delete_storage_first: bool,
40    context: C,
41    stored_value: Box<T>,
42    update: Option<Box<T>>,
43}
44
45impl<C, T, C2> ReplaceContext<C2> for RegisterView<C, T>
46where
47    C: Context,
48    C2: Context,
49    T: Default + Send + Sync + Serialize + DeserializeOwned + Clone,
50{
51    type Target = RegisterView<C2, T>;
52
53    async fn with_context(
54        &mut self,
55        ctx: impl FnOnce(&Self::Context) -> C2 + Clone,
56    ) -> Self::Target {
57        RegisterView {
58            delete_storage_first: self.delete_storage_first,
59            context: ctx(self.context()),
60            stored_value: self.stored_value.clone(),
61            update: self.update.clone(),
62        }
63    }
64}
65
66impl<C, T> View for RegisterView<C, T>
67where
68    C: Context,
69    T: Default + Send + Sync + Serialize + DeserializeOwned,
70{
71    const NUM_INIT_KEYS: usize = 1;
72
73    type Context = C;
74
75    fn context(&self) -> &C {
76        &self.context
77    }
78
79    fn pre_load(context: &C) -> Result<Vec<Vec<u8>>, ViewError> {
80        Ok(vec![context.base_key().bytes.clone()])
81    }
82
83    fn post_load(context: C, values: &[Option<Vec<u8>>]) -> Result<Self, ViewError> {
84        let value =
85            from_bytes_option_or_default(values.first().ok_or(ViewError::PostLoadValuesError)?)?;
86        let stored_value = Box::new(value);
87        Ok(Self {
88            delete_storage_first: false,
89            context,
90            stored_value,
91            update: None,
92        })
93    }
94
95    async fn load(context: C) -> Result<Self, ViewError> {
96        let keys = Self::pre_load(&context)?;
97        let values = context.store().read_multi_values_bytes(keys).await?;
98        Self::post_load(context, &values)
99    }
100
101    fn rollback(&mut self) {
102        self.delete_storage_first = false;
103        self.update = None;
104    }
105
106    async fn has_pending_changes(&self) -> bool {
107        if self.delete_storage_first {
108            return true;
109        }
110        self.update.is_some()
111    }
112
113    fn flush(&mut self, batch: &mut Batch) -> Result<bool, ViewError> {
114        let mut delete_view = false;
115        if self.delete_storage_first {
116            batch.delete_key(self.context.base_key().bytes.clone());
117            self.stored_value = Box::default();
118            delete_view = true;
119        } else if let Some(value) = self.update.take() {
120            let key = self.context.base_key().bytes.clone();
121            batch.put_key_value(key, &value)?;
122            self.stored_value = value;
123        }
124        self.delete_storage_first = false;
125        self.update = None;
126        Ok(delete_view)
127    }
128
129    fn clear(&mut self) {
130        self.delete_storage_first = true;
131        self.update = Some(Box::default());
132    }
133}
134
135impl<C, T> ClonableView for RegisterView<C, T>
136where
137    C: Context,
138    T: Clone + Default + Send + Sync + Serialize + DeserializeOwned,
139{
140    fn clone_unchecked(&mut self) -> Self {
141        RegisterView {
142            delete_storage_first: self.delete_storage_first,
143            context: self.context.clone(),
144            stored_value: self.stored_value.clone(),
145            update: self.update.clone(),
146        }
147    }
148}
149
150impl<C, T> RegisterView<C, T>
151where
152    C: Context,
153{
154    /// Access the current value in the register.
155    /// ```rust
156    /// # tokio_test::block_on(async {
157    /// # use linera_views::context::MemoryContext;
158    /// # use linera_views::register_view::RegisterView;
159    /// # use linera_views::views::View;
160    /// # let context = MemoryContext::new_for_testing(());
161    /// let mut register = RegisterView::<_, u32>::load(context).await.unwrap();
162    /// let value = register.get();
163    /// assert_eq!(*value, 0);
164    /// # })
165    /// ```
166    pub fn get(&self) -> &T {
167        match &self.update {
168            None => &self.stored_value,
169            Some(value) => value,
170        }
171    }
172
173    /// Sets the value in the register.
174    /// ```rust
175    /// # tokio_test::block_on(async {
176    /// # use linera_views::context::MemoryContext;
177    /// # use linera_views::register_view::RegisterView;
178    /// # use linera_views::views::View;
179    /// # let context = MemoryContext::new_for_testing(());
180    /// let mut register = RegisterView::load(context).await.unwrap();
181    /// register.set(5);
182    /// let value = register.get();
183    /// assert_eq!(*value, 5);
184    /// # })
185    /// ```
186    pub fn set(&mut self, value: T) {
187        self.delete_storage_first = false;
188        self.update = Some(Box::new(value));
189    }
190
191    /// Obtains the extra data.
192    pub fn extra(&self) -> &C::Extra {
193        self.context.extra()
194    }
195}
196
197impl<C, T> RegisterView<C, T>
198where
199    C: Context,
200    T: Clone + Serialize,
201{
202    /// Obtains a mutable reference to the value in the register.
203    /// ```rust
204    /// # tokio_test::block_on(async {
205    /// # use linera_views::context::MemoryContext;
206    /// # use linera_views::register_view::RegisterView;
207    /// # use linera_views::views::View;
208    /// # let context = MemoryContext::new_for_testing(());
209    /// let mut register: RegisterView<_, u32> = RegisterView::load(context).await.unwrap();
210    /// let value = register.get_mut();
211    /// assert_eq!(*value, 0);
212    /// # })
213    /// ```
214    pub fn get_mut(&mut self) -> &mut T {
215        self.delete_storage_first = false;
216        match &mut self.update {
217            Some(value) => value,
218            update => {
219                *update = Some(self.stored_value.clone());
220                update.as_mut().unwrap()
221            }
222        }
223    }
224
225    fn compute_hash(&self) -> Result<<sha3::Sha3_256 as Hasher>::Output, ViewError> {
226        #[cfg(with_metrics)]
227        let _hash_latency = metrics::REGISTER_VIEW_HASH_RUNTIME.measure_latency();
228        let mut hasher = sha3::Sha3_256::default();
229        hasher.update_with_bcs_bytes(self.get())?;
230        Ok(hasher.finalize())
231    }
232}
233
234impl<C, T> HashableView for RegisterView<C, T>
235where
236    C: Context,
237    T: Clone + Default + Send + Sync + Serialize + DeserializeOwned,
238{
239    type Hasher = sha3::Sha3_256;
240
241    async fn hash_mut(&mut self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
242        self.compute_hash()
243    }
244
245    async fn hash(&self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
246        self.compute_hash()
247    }
248}
249
250/// Type wrapping `RegisterView` while memoizing the hash.
251pub type HashedRegisterView<C, T> =
252    WrappedHashableContainerView<C, RegisterView<C, T>, HasherOutput>;
253
254#[cfg(with_graphql)]
255mod graphql {
256    use std::borrow::Cow;
257
258    use super::RegisterView;
259    use crate::context::Context;
260
261    impl<C, T> async_graphql::OutputType for RegisterView<C, T>
262    where
263        C: Context,
264        T: async_graphql::OutputType + Send + Sync,
265    {
266        fn type_name() -> Cow<'static, str> {
267            T::type_name()
268        }
269
270        fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
271            T::create_type_info(registry)
272        }
273
274        async fn resolve(
275            &self,
276            ctx: &async_graphql::ContextSelectionSet<'_>,
277            field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
278        ) -> async_graphql::ServerResult<async_graphql::Value> {
279            self.get().resolve(ctx, field).await
280        }
281    }
282}