1use std::{
5 collections::BTreeMap,
6 ops::{Bound, Range, RangeBounds},
7};
8
9use allocative::Allocative;
10#[cfg(with_metrics)]
11use linera_base::prometheus_util::MeasureLatency as _;
12use serde::{de::DeserializeOwned, Serialize};
13
14use crate::{
15 batch::Batch,
16 common::{from_bytes_option_or_default, HasherOutput},
17 context::Context,
18 hashable_wrapper::WrappedHashableContainerView,
19 historical_hash_wrapper::HistoricallyHashableView,
20 store::ReadableKeyValueStore as _,
21 views::{ClonableView, HashableView, Hasher, View, ViewError, MIN_VIEW_TAG},
22};
23
24#[cfg(with_metrics)]
25mod metrics {
26 use std::sync::LazyLock;
27
28 use linera_base::prometheus_util::{exponential_bucket_latencies, register_histogram_vec};
29 use prometheus::HistogramVec;
30
31 pub static LOG_VIEW_HASH_RUNTIME: LazyLock<HistogramVec> = LazyLock::new(|| {
33 register_histogram_vec(
34 "log_view_hash_runtime",
35 "LogView hash runtime",
36 &[],
37 exponential_bucket_latencies(5.0),
38 )
39 });
40}
41
42#[repr(u8)]
44enum KeyTag {
45 Count = MIN_VIEW_TAG,
47 Index,
49}
50
51#[derive(Debug, Allocative)]
53#[allocative(bound = "C, T: Allocative")]
54pub struct LogView<C, T> {
55 #[allocative(skip)]
57 context: C,
58 delete_storage_first: bool,
60 stored_count: usize,
62 new_values: Vec<T>,
64}
65
66impl<C, T> View for LogView<C, T>
67where
68 C: Context,
69 T: Send + Sync + Serialize,
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().base_tag(KeyTag::Count as u8)])
81 }
82
83 fn post_load(context: C, values: &[Option<Vec<u8>>]) -> Result<Self, ViewError> {
84 let stored_count =
85 from_bytes_option_or_default(values.first().ok_or(ViewError::PostLoadValuesError)?)?;
86 Ok(Self {
87 context,
88 delete_storage_first: false,
89 stored_count,
90 new_values: Vec::new(),
91 })
92 }
93
94 fn rollback(&mut self) {
95 self.delete_storage_first = false;
96 self.new_values.clear();
97 }
98
99 async fn has_pending_changes(&self) -> bool {
100 if self.delete_storage_first {
101 return true;
102 }
103 !self.new_values.is_empty()
104 }
105
106 fn pre_save(&self, batch: &mut Batch) -> Result<bool, ViewError> {
107 let mut delete_view = false;
108 if self.delete_storage_first {
109 batch.delete_key_prefix(self.context.base_key().bytes.clone());
110 delete_view = true;
111 }
112 if !self.new_values.is_empty() {
113 delete_view = false;
114 for (count, value) in (self.stored_count..).zip(&self.new_values) {
115 let key = self
116 .context
117 .base_key()
118 .derive_tag_key(KeyTag::Index as u8, &count)?;
119 batch.put_key_value(key, value)?;
120 }
121 let count = self.stored_count + self.new_values.len();
122 let key = self.context.base_key().base_tag(KeyTag::Count as u8);
123 batch.put_key_value(key, &count)?;
124 }
125 Ok(delete_view)
126 }
127
128 fn post_save(&mut self) {
129 if self.delete_storage_first {
130 self.stored_count = 0;
131 }
132 self.stored_count += self.new_values.len();
133 self.new_values.clear();
134 self.delete_storage_first = false;
135 }
136
137 fn clear(&mut self) {
138 self.delete_storage_first = true;
139 self.new_values.clear();
140 }
141}
142
143impl<C, T> ClonableView for LogView<C, T>
144where
145 C: Context,
146 T: Clone + Send + Sync + Serialize,
147{
148 fn clone_unchecked(&mut self) -> Result<Self, ViewError> {
149 Ok(LogView {
150 context: self.context.clone(),
151 delete_storage_first: self.delete_storage_first,
152 stored_count: self.stored_count,
153 new_values: self.new_values.clone(),
154 })
155 }
156}
157
158impl<C, T> LogView<C, T>
159where
160 C: Context,
161{
162 pub fn push(&mut self, value: T) {
174 self.new_values.push(value);
175 }
176
177 pub fn count(&self) -> usize {
191 if self.delete_storage_first {
192 self.new_values.len()
193 } else {
194 self.stored_count + self.new_values.len()
195 }
196 }
197
198 pub fn extra(&self) -> &C::Extra {
200 self.context.extra()
201 }
202}
203
204impl<C, T> LogView<C, T>
205where
206 C: Context,
207 T: Clone + DeserializeOwned + Serialize + Send + Sync,
208{
209 pub async fn get(&self, index: usize) -> Result<Option<T>, ViewError> {
222 let value = if self.delete_storage_first {
223 self.new_values.get(index).cloned()
224 } else if index < self.stored_count {
225 let key = self
226 .context
227 .base_key()
228 .derive_tag_key(KeyTag::Index as u8, &index)?;
229 self.context.store().read_value(&key).await?
230 } else {
231 self.new_values.get(index - self.stored_count).cloned()
232 };
233 Ok(value)
234 }
235
236 pub async fn multi_get(&self, indices: Vec<usize>) -> Result<Vec<Option<T>>, ViewError> {
253 let mut result = Vec::new();
254 if self.delete_storage_first {
255 for index in indices {
256 result.push(self.new_values.get(index).cloned());
257 }
258 } else {
259 let mut index_to_positions = BTreeMap::<usize, Vec<usize>>::new();
260 for (pos, index) in indices.into_iter().enumerate() {
261 if index < self.stored_count {
262 index_to_positions.entry(index).or_default().push(pos);
263 result.push(None);
264 } else {
265 result.push(self.new_values.get(index - self.stored_count).cloned());
266 }
267 }
268 let mut keys = Vec::new();
269 let mut vec_positions = Vec::new();
270 for (index, positions) in index_to_positions {
271 let key = self
272 .context
273 .base_key()
274 .derive_tag_key(KeyTag::Index as u8, &index)?;
275 keys.push(key);
276 vec_positions.push(positions);
277 }
278 let values = self.context.store().read_multi_values(&keys).await?;
279 for (positions, value) in vec_positions.into_iter().zip(values) {
280 if let Some((&last, rest)) = positions.split_last() {
281 for &position in rest {
282 *result.get_mut(position).unwrap() = value.clone();
283 }
284 *result.get_mut(last).unwrap() = value;
285 }
286 }
287 }
288 Ok(result)
289 }
290
291 async fn read_context(&self, range: Range<usize>) -> Result<Vec<T>, ViewError> {
292 let count = range.len();
293 let mut keys = Vec::with_capacity(count);
294 for index in range {
295 let key = self
296 .context
297 .base_key()
298 .derive_tag_key(KeyTag::Index as u8, &index)?;
299 keys.push(key);
300 }
301 let mut values = Vec::with_capacity(count);
302 for entry in self.context.store().read_multi_values(&keys).await? {
303 match entry {
304 None => {
305 return Err(ViewError::MissingEntries("LogView".into()));
306 }
307 Some(value) => values.push(value),
308 }
309 }
310 Ok(values)
311 }
312
313 pub async fn read<R>(&self, range: R) -> Result<Vec<T>, ViewError>
328 where
329 R: RangeBounds<usize>,
330 {
331 let effective_stored_count = if self.delete_storage_first {
332 0
333 } else {
334 self.stored_count
335 };
336 let end = match range.end_bound() {
337 Bound::Included(end) => *end + 1,
338 Bound::Excluded(end) => *end,
339 Bound::Unbounded => self.count(),
340 }
341 .min(self.count());
342 let start = match range.start_bound() {
343 Bound::Included(start) => *start,
344 Bound::Excluded(start) => *start + 1,
345 Bound::Unbounded => 0,
346 };
347 if start >= end {
348 return Ok(Vec::new());
349 }
350 if start < effective_stored_count {
351 if end <= effective_stored_count {
352 self.read_context(start..end).await
353 } else {
354 let mut values = self.read_context(start..effective_stored_count).await?;
355 values.extend(
356 self.new_values[0..(end - effective_stored_count)]
357 .iter()
358 .cloned(),
359 );
360 Ok(values)
361 }
362 } else {
363 Ok(
364 self.new_values[(start - effective_stored_count)..(end - effective_stored_count)]
365 .to_vec(),
366 )
367 }
368 }
369}
370
371impl<C, T> HashableView for LogView<C, T>
372where
373 C: Context,
374 T: Send + Sync + Clone + Serialize + DeserializeOwned,
375{
376 type Hasher = sha3::Sha3_256;
377
378 async fn hash_mut(&mut self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
379 self.hash().await
380 }
381
382 async fn hash(&self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
383 #[cfg(with_metrics)]
384 let _hash_latency = metrics::LOG_VIEW_HASH_RUNTIME.measure_latency();
385 let elements = self.read(..).await?;
386 let mut hasher = sha3::Sha3_256::default();
387 hasher.update_with_bcs_bytes(&elements)?;
388 Ok(hasher.finalize())
389 }
390}
391
392pub type HashedLogView<C, T> = WrappedHashableContainerView<C, LogView<C, T>, HasherOutput>;
394
395pub type HistoricallyHashedLogView<C, T> = HistoricallyHashableView<C, LogView<C, T>>;
397
398#[cfg(not(web))]
399mod graphql {
400 use std::borrow::Cow;
401
402 use super::LogView;
403 use crate::{
404 context::Context,
405 graphql::{hash_name, mangle},
406 };
407
408 impl<C: Send + Sync, T: async_graphql::OutputType> async_graphql::TypeName for LogView<C, T> {
409 fn type_name() -> Cow<'static, str> {
410 format!(
411 "LogView_{}_{:08x}",
412 mangle(T::type_name()),
413 hash_name::<T>()
414 )
415 .into()
416 }
417 }
418
419 #[async_graphql::Object(cache_control(no_cache), name_type)]
420 impl<C: Context, T: async_graphql::OutputType> LogView<C, T>
421 where
422 T: serde::ser::Serialize + serde::de::DeserializeOwned + Clone + Send + Sync,
423 {
424 #[graphql(derived(name = "count"))]
425 async fn count_(&self) -> Result<u32, async_graphql::Error> {
426 Ok(self.count() as u32)
427 }
428
429 async fn entries(
430 &self,
431 start: Option<usize>,
432 end: Option<usize>,
433 ) -> async_graphql::Result<Vec<T>> {
434 Ok(self
435 .read(start.unwrap_or_default()..end.unwrap_or_else(|| self.count()))
436 .await?)
437 }
438 }
439}