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