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 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 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, 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 pub fn get(&self) -> &T {
167 match &self.update {
168 None => &self.stored_value,
169 Some(value) => value,
170 }
171 }
172
173 pub fn set(&mut self, value: T) {
187 self.delete_storage_first = false;
188 self.update = Some(Box::new(value));
189 }
190
191 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 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
250pub 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}