linera_views/views/
register_view.rs1#[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, 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 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#[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> View for RegisterView<C, T>
46where
47 C: Context,
48 T: Default + Send + Sync + Serialize + DeserializeOwned,
49{
50 const NUM_INIT_KEYS: usize = 1;
51
52 type Context = C;
53
54 fn context(&self) -> &C {
55 &self.context
56 }
57
58 fn pre_load(context: &C) -> Result<Vec<Vec<u8>>, ViewError> {
59 Ok(vec![context.base_key().bytes.clone()])
60 }
61
62 fn post_load(context: C, values: &[Option<Vec<u8>>]) -> Result<Self, ViewError> {
63 let value =
64 from_bytes_option_or_default(values.first().ok_or(ViewError::PostLoadValuesError)?)?;
65 let stored_value = Box::new(value);
66 Ok(Self {
67 delete_storage_first: false,
68 context,
69 stored_value,
70 update: None,
71 })
72 }
73
74 async fn load(context: C) -> Result<Self, ViewError> {
75 let keys = Self::pre_load(&context)?;
76 let values = context.store().read_multi_values_bytes(keys).await?;
77 Self::post_load(context, &values)
78 }
79
80 fn rollback(&mut self) {
81 self.delete_storage_first = false;
82 self.update = None;
83 }
84
85 async fn has_pending_changes(&self) -> bool {
86 if self.delete_storage_first {
87 return true;
88 }
89 self.update.is_some()
90 }
91
92 fn flush(&mut self, batch: &mut Batch) -> Result<bool, ViewError> {
93 let mut delete_view = false;
94 if self.delete_storage_first {
95 batch.delete_key(self.context.base_key().bytes.clone());
96 self.stored_value = Box::default();
97 delete_view = true;
98 } else if let Some(value) = self.update.take() {
99 let key = self.context.base_key().bytes.clone();
100 batch.put_key_value(key, &value)?;
101 self.stored_value = value;
102 }
103 self.delete_storage_first = false;
104 self.update = None;
105 Ok(delete_view)
106 }
107
108 fn clear(&mut self) {
109 self.delete_storage_first = true;
110 self.update = Some(Box::default());
111 }
112}
113
114impl<C, T> ClonableView for RegisterView<C, T>
115where
116 C: Context,
117 T: Clone + Default + Send + Sync + Serialize + DeserializeOwned,
118{
119 fn clone_unchecked(&mut self) -> Result<Self, ViewError> {
120 Ok(RegisterView {
121 delete_storage_first: self.delete_storage_first,
122 context: self.context.clone(),
123 stored_value: self.stored_value.clone(),
124 update: self.update.clone(),
125 })
126 }
127}
128
129impl<C, T> RegisterView<C, T>
130where
131 C: Context,
132{
133 pub fn get(&self) -> &T {
146 match &self.update {
147 None => &self.stored_value,
148 Some(value) => value,
149 }
150 }
151
152 pub fn set(&mut self, value: T) {
166 self.delete_storage_first = false;
167 self.update = Some(Box::new(value));
168 }
169
170 pub fn extra(&self) -> &C::Extra {
172 self.context.extra()
173 }
174}
175
176impl<C, T> RegisterView<C, T>
177where
178 C: Context,
179 T: Clone + Serialize,
180{
181 pub fn get_mut(&mut self) -> &mut T {
194 self.delete_storage_first = false;
195 match &mut self.update {
196 Some(value) => value,
197 update => {
198 *update = Some(self.stored_value.clone());
199 update.as_mut().unwrap()
200 }
201 }
202 }
203
204 fn compute_hash(&self) -> Result<<sha3::Sha3_256 as Hasher>::Output, ViewError> {
205 #[cfg(with_metrics)]
206 let _hash_latency = metrics::REGISTER_VIEW_HASH_RUNTIME.measure_latency();
207 let mut hasher = sha3::Sha3_256::default();
208 hasher.update_with_bcs_bytes(self.get())?;
209 Ok(hasher.finalize())
210 }
211}
212
213impl<C, T> HashableView for RegisterView<C, T>
214where
215 C: Context,
216 T: Clone + Default + Send + Sync + Serialize + DeserializeOwned,
217{
218 type Hasher = sha3::Sha3_256;
219
220 async fn hash_mut(&mut self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
221 self.compute_hash()
222 }
223
224 async fn hash(&self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
225 self.compute_hash()
226 }
227}
228
229pub type HashedRegisterView<C, T> =
231 WrappedHashableContainerView<C, RegisterView<C, T>, HasherOutput>;
232
233#[cfg(with_graphql)]
234mod graphql {
235 use std::borrow::Cow;
236
237 use super::RegisterView;
238 use crate::context::Context;
239
240 impl<C, T> async_graphql::OutputType for RegisterView<C, T>
241 where
242 C: Context,
243 T: async_graphql::OutputType + Send + Sync,
244 {
245 fn type_name() -> Cow<'static, str> {
246 T::type_name()
247 }
248
249 fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
250 T::create_type_info(registry)
251 }
252
253 async fn resolve(
254 &self,
255 ctx: &async_graphql::ContextSelectionSet<'_>,
256 field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
257 ) -> async_graphql::ServerResult<async_graphql::Value> {
258 self.get().resolve(ctx, field).await
259 }
260 }
261}