1#[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 views::{ClonableView, HashableView, Hasher, ReplaceContext, View},
14 ViewError,
15};
16
17#[cfg(with_metrics)]
18mod metrics {
19 use std::sync::LazyLock;
20
21 use linera_base::prometheus_util::{exponential_bucket_latencies, register_histogram_vec};
22 use prometheus::HistogramVec;
23
24 pub static REGISTER_VIEW_HASH_RUNTIME: LazyLock<HistogramVec> = LazyLock::new(|| {
26 register_histogram_vec(
27 "register_view_hash_runtime",
28 "RegisterView hash runtime",
29 &[],
30 exponential_bucket_latencies(5.0),
31 )
32 });
33}
34
35#[derive(Debug)]
37pub struct RegisterView<C, T> {
38 delete_storage_first: bool,
39 context: C,
40 stored_value: Box<T>,
41 update: Option<Box<T>>,
42}
43
44impl<C, T, C2> ReplaceContext<C2> for RegisterView<C, T>
45where
46 C: Context,
47 C2: Context,
48 T: Default + Send + Sync + Serialize + DeserializeOwned + Clone,
49{
50 type Target = RegisterView<C2, T>;
51
52 async fn with_context(
53 &mut self,
54 ctx: impl FnOnce(&Self::Context) -> C2 + Clone,
55 ) -> Self::Target {
56 RegisterView {
57 delete_storage_first: self.delete_storage_first,
58 context: ctx(self.context()),
59 stored_value: self.stored_value.clone(),
60 update: self.update.clone(),
61 }
62 }
63}
64
65impl<C, T> View for RegisterView<C, T>
66where
67 C: Context,
68 T: Default + Send + Sync + Serialize + DeserializeOwned,
69{
70 const NUM_INIT_KEYS: usize = 1;
71
72 type Context = C;
73
74 fn context(&self) -> &C {
75 &self.context
76 }
77
78 fn pre_load(context: &C) -> Result<Vec<Vec<u8>>, ViewError> {
79 Ok(vec![context.base_key().bytes.clone()])
80 }
81
82 fn post_load(context: C, values: &[Option<Vec<u8>>]) -> Result<Self, ViewError> {
83 let value =
84 from_bytes_option_or_default(values.first().ok_or(ViewError::PostLoadValuesError)?)?;
85 let stored_value = Box::new(value);
86 Ok(Self {
87 delete_storage_first: false,
88 context,
89 stored_value,
90 update: None,
91 })
92 }
93
94 fn rollback(&mut self) {
95 self.delete_storage_first = false;
96 self.update = None;
97 }
98
99 async fn has_pending_changes(&self) -> bool {
100 if self.delete_storage_first {
101 return true;
102 }
103 self.update.is_some()
104 }
105
106 fn flush(&mut self, batch: &mut Batch) -> Result<bool, ViewError> {
107 let mut delete_view = false;
108 if self.delete_storage_first {
109 batch.delete_key(self.context.base_key().bytes.clone());
110 self.stored_value = Box::default();
111 delete_view = true;
112 } else if let Some(value) = self.update.take() {
113 let key = self.context.base_key().bytes.clone();
114 batch.put_key_value(key, &value)?;
115 self.stored_value = value;
116 }
117 self.delete_storage_first = false;
118 self.update = None;
119 Ok(delete_view)
120 }
121
122 fn clear(&mut self) {
123 self.delete_storage_first = true;
124 self.update = Some(Box::default());
125 }
126}
127
128impl<C, T> ClonableView for RegisterView<C, T>
129where
130 C: Context,
131 T: Clone + Default + Send + Sync + Serialize + DeserializeOwned,
132{
133 fn clone_unchecked(&mut self) -> Result<Self, ViewError> {
134 Ok(RegisterView {
135 delete_storage_first: self.delete_storage_first,
136 context: self.context.clone(),
137 stored_value: self.stored_value.clone(),
138 update: self.update.clone(),
139 })
140 }
141}
142
143impl<C, T> RegisterView<C, T>
144where
145 C: Context,
146{
147 pub fn get(&self) -> &T {
160 match &self.update {
161 None => &self.stored_value,
162 Some(value) => value,
163 }
164 }
165
166 pub fn set(&mut self, value: T) {
180 self.delete_storage_first = false;
181 self.update = Some(Box::new(value));
182 }
183
184 pub fn extra(&self) -> &C::Extra {
186 self.context.extra()
187 }
188}
189
190impl<C, T> RegisterView<C, T>
191where
192 C: Context,
193 T: Clone + Serialize,
194{
195 pub fn get_mut(&mut self) -> &mut T {
208 self.delete_storage_first = false;
209 match &mut self.update {
210 Some(value) => value,
211 update => {
212 *update = Some(self.stored_value.clone());
213 update.as_mut().unwrap()
214 }
215 }
216 }
217
218 fn compute_hash(&self) -> Result<<sha3::Sha3_256 as Hasher>::Output, ViewError> {
219 #[cfg(with_metrics)]
220 let _hash_latency = metrics::REGISTER_VIEW_HASH_RUNTIME.measure_latency();
221 let mut hasher = sha3::Sha3_256::default();
222 hasher.update_with_bcs_bytes(self.get())?;
223 Ok(hasher.finalize())
224 }
225}
226
227impl<C, T> HashableView for RegisterView<C, T>
228where
229 C: Context,
230 T: Clone + Default + Send + Sync + Serialize + DeserializeOwned,
231{
232 type Hasher = sha3::Sha3_256;
233
234 async fn hash_mut(&mut self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
235 self.compute_hash()
236 }
237
238 async fn hash(&self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
239 self.compute_hash()
240 }
241}
242
243pub type HashedRegisterView<C, T> =
245 WrappedHashableContainerView<C, RegisterView<C, T>, HasherOutput>;
246
247#[cfg(with_graphql)]
248mod graphql {
249 use std::borrow::Cow;
250
251 use super::RegisterView;
252 use crate::context::Context;
253
254 impl<C, T> async_graphql::OutputType for RegisterView<C, T>
255 where
256 C: Context,
257 T: async_graphql::OutputType + Send + Sync,
258 {
259 fn type_name() -> Cow<'static, str> {
260 T::type_name()
261 }
262
263 fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
264 T::create_type_info(registry)
265 }
266
267 async fn resolve(
268 &self,
269 ctx: &async_graphql::ContextSelectionSet<'_>,
270 field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
271 ) -> async_graphql::ServerResult<async_graphql::Value> {
272 self.get().resolve(ctx, field).await
273 }
274 }
275}