1use std::sync::OnceLock;
5
6use allocative::Allocative;
7#[cfg(with_metrics)]
8use linera_base::prometheus_util::MeasureLatency as _;
9use serde::{de::DeserializeOwned, Serialize};
10
11use crate::{
12 batch::Batch,
13 common::{from_bytes_option_or_default, HasherOutput},
14 context::Context,
15 hashable_wrapper::WrappedHashableContainerView,
16 store::ReadableKeyValueStore,
17 views::{ClonableView, HashableView, Hasher, ReplaceContext, View},
18 ViewError,
19};
20
21#[cfg(with_metrics)]
22pub(crate) mod metrics {
23 use linera_base::prometheus_util::{exponential_bucket_latencies, register_histogram_vec};
24 use prometheus::HistogramVec;
25
26 linera_base::declare_metrics! {
27 pub static LAZY_REGISTER_VIEW_HASH_RUNTIME: HistogramVec =
29 register_histogram_vec(
30 "lazy_register_view_hash_runtime",
31 "LazyRegisterView hash runtime",
32 &[],
33 exponential_bucket_latencies(5.0),
34 );
35 }
36}
37
38#[derive(Debug, Allocative)]
41#[allocative(bound = "C, T: Allocative")]
42pub struct LazyRegisterView<C, T> {
43 delete_storage_first: bool,
45 #[allocative(skip)]
47 context: C,
48 #[allocative(skip)]
52 stored_value: OnceLock<Box<T>>,
53 update: Option<Box<T>>,
55}
56
57impl<C, T, C2> ReplaceContext<C2> for LazyRegisterView<C, T>
58where
59 C: Context,
60 C2: Context,
61 T: Default + Send + Sync + Serialize + DeserializeOwned + Clone,
62{
63 type Target = LazyRegisterView<C2, T>;
64
65 async fn with_context(
66 &mut self,
67 ctx: impl FnOnce(&Self::Context) -> C2 + Clone,
68 ) -> Self::Target {
69 let stored_value = self.stored_value.clone();
70 LazyRegisterView {
71 delete_storage_first: self.delete_storage_first,
72 context: ctx(&self.context),
73 stored_value,
74 update: self.update.clone(),
75 }
76 }
77}
78
79impl<C, T> View for LazyRegisterView<C, T>
80where
81 C: Context,
82 T: Default + Send + Sync + Serialize + DeserializeOwned,
83{
84 const NUM_INIT_KEYS: usize = 0;
85
86 type Context = C;
87
88 fn context(&self) -> C {
89 self.context.clone()
90 }
91
92 fn pre_load(_context: &C) -> Result<Vec<Vec<u8>>, ViewError> {
93 Ok(vec![])
94 }
95
96 fn post_load(context: C, _values: &[Option<Vec<u8>>]) -> Result<Self, ViewError> {
97 Ok(Self {
98 delete_storage_first: false,
99 context,
100 stored_value: OnceLock::new(),
101 update: None,
102 })
103 }
104
105 fn rollback(&mut self) {
106 self.delete_storage_first = false;
107 self.update = None;
108 }
109
110 async fn has_pending_changes(&self) -> bool {
111 if self.delete_storage_first {
112 return true;
113 }
114 self.update.is_some()
115 }
116
117 fn pre_save(&self, batch: &mut Batch) -> Result<bool, ViewError> {
118 let mut delete_view = false;
119 if self.delete_storage_first {
120 batch.delete_key(self.context.base_key().bytes.clone());
121 delete_view = true;
122 } else if let Some(value) = &self.update {
123 let key = self.context.base_key().bytes.clone();
124 batch.put_key_value(key, value)?;
125 }
126 Ok(delete_view)
127 }
128
129 fn post_save(&mut self) {
130 if self.delete_storage_first {
131 self.stored_value = OnceLock::from(Box::<T>::default());
132 } else if let Some(value) = self.update.take() {
133 self.stored_value = OnceLock::from(value);
134 }
135 self.delete_storage_first = false;
136 self.update = None;
137 }
138
139 fn clear(&mut self) {
140 self.delete_storage_first = true;
141 self.update = Some(Box::default());
142 }
143}
144
145impl<C, T> ClonableView for LazyRegisterView<C, T>
146where
147 C: Context,
148 T: Clone + Default + Send + Sync + Serialize + DeserializeOwned,
149{
150 fn clone_unchecked(&mut self) -> Result<Self, ViewError> {
151 let stored_value = self.stored_value.clone();
152 Ok(LazyRegisterView {
153 delete_storage_first: self.delete_storage_first,
154 context: self.context.clone(),
155 stored_value,
156 update: self.update.clone(),
157 })
158 }
159}
160
161impl<C, T> LazyRegisterView<C, T>
162where
163 C: Context,
164 T: Default + DeserializeOwned,
165{
166 pub async fn get(&self) -> Result<&T, ViewError> {
179 if let Some(value) = &self.update {
180 return Ok(value);
181 }
182 if let Some(value) = self.stored_value.get() {
183 return Ok(value);
184 }
185 let key = self.context.base_key().bytes.clone();
186 let bytes = self.context.store().read_value_bytes(&key).await?;
187 let value = from_bytes_option_or_default(&bytes)?;
188 Ok(self.stored_value.get_or_init(|| Box::new(value)))
189 }
190
191 pub fn set(&mut self, value: T) {
205 self.delete_storage_first = false;
206 self.update = Some(Box::new(value));
207 }
208
209 pub fn extra(&self) -> &C::Extra {
211 self.context.extra()
212 }
213}
214
215impl<C, T> LazyRegisterView<C, T>
216where
217 C: Context,
218 T: Clone + Default + Serialize + DeserializeOwned,
219{
220 pub async fn get_mut(&mut self) -> Result<&mut T, ViewError> {
233 if self.update.is_none() {
234 let update = self.get().await?.clone();
235 self.update = Some(Box::new(update));
236 }
237 self.delete_storage_first = false;
238 Ok(self.update.as_mut().unwrap())
239 }
240
241 async fn compute_hash(&self) -> Result<<sha3::Sha3_256 as Hasher>::Output, ViewError> {
242 #[cfg(with_metrics)]
243 let _hash_latency = metrics::LAZY_REGISTER_VIEW_HASH_RUNTIME.measure_latency();
244 let mut hasher = sha3::Sha3_256::default();
245 hasher.update_with_bcs_bytes(self.get().await?)?;
246 Ok(hasher.finalize())
247 }
248}
249
250impl<C, T> HashableView for LazyRegisterView<C, T>
251where
252 C: Context,
253 T: Clone + Default + Send + Sync + Serialize + DeserializeOwned,
254{
255 type Hasher = sha3::Sha3_256;
256
257 async fn hash_mut(&mut self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
258 self.compute_hash().await
259 }
260
261 async fn hash(&self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
262 self.compute_hash().await
263 }
264}
265
266pub type HashedLazyRegisterView<C, T> =
268 WrappedHashableContainerView<C, LazyRegisterView<C, T>, HasherOutput>;
269
270#[cfg(with_graphql)]
271mod graphql {
272 use std::borrow::Cow;
273
274 use super::LazyRegisterView;
275 use crate::context::Context;
276
277 impl<C, T> async_graphql::OutputType for LazyRegisterView<C, T>
278 where
279 C: Context,
280 T: async_graphql::OutputType + Default + Send + Sync + serde::de::DeserializeOwned,
281 {
282 fn type_name() -> Cow<'static, str> {
283 T::type_name()
284 }
285
286 fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
287 T::create_type_info(registry)
288 }
289
290 async fn resolve(
291 &self,
292 ctx: &async_graphql::ContextSelectionSet<'_>,
293 field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
294 ) -> async_graphql::ServerResult<async_graphql::Value> {
295 self.get()
296 .await
297 .map_err(|e| async_graphql::ServerError::new(e.to_string(), Some(field.pos)))?
298 .resolve(ctx, field)
299 .await
300 }
301 }
302}