1use std::{
5 collections::{BTreeMap, HashMap},
6 fmt::Debug,
7 sync::{Arc, OnceLock},
8};
9
10use async_trait::async_trait;
11#[cfg(with_metrics)]
12use linera_base::prometheus_util::MeasureLatency as _;
13use linera_base::{
14 crypto::CryptoHash,
15 data_types::{Blob, BlockHeight, NetworkDescription, TimeDelta, Timestamp},
16 identifiers::{ApplicationId, BlobId, ChainId, EventId, IndexAndEvent, StreamId},
17 time::Duration,
18};
19use linera_cache::{Arc as CacheArc, ValueCache};
20use linera_chain::{
21 types::{CertificateValue, ConfirmedBlock, ConfirmedBlockCertificate, LiteCertificate},
22 ChainStateView,
23};
24use linera_execution::{
25 BlobState, ExecutionRuntimeConfig, SharedCommittees, UserContractCode, UserServiceCode,
26 WasmRuntime,
27};
28use linera_views::{
29 backends::dual::{DualStoreRootKeyAssignment, StoreInUse},
30 batch::Batch,
31 context::ViewContext,
32 store::{
33 KeyValueDatabase, KeyValueStore, ReadableKeyValueStore as _, WritableKeyValueStore as _,
34 },
35 views::View,
36 ViewError,
37};
38use serde::{Deserialize, Serialize};
39use tracing::{debug, instrument};
40#[cfg(with_testing)]
41use {
42 futures::channel::oneshot::{self, Receiver},
43 linera_views::{random::generate_test_namespace, store::TestKeyValueDatabase},
44 std::cmp::Reverse,
45};
46
47use crate::{ChainRuntimeContext, Clock, Storage};
48
49#[cfg(with_metrics)]
51pub mod metrics {
52 use linera_base::prometheus_util::{
53 exponential_bucket_interval, exponential_bucket_latencies, linear_bucket_interval,
54 register_histogram, register_histogram_vec, register_int_counter, register_int_counter_vec,
55 };
56 use prometheus::{Histogram, HistogramVec, IntCounter, IntCounterVec};
57
58 pub(super) const SOURCE_LABEL: &str = "source";
60 pub(super) const CACHE: &str = "cache";
62 pub(super) const DB: &str = "db";
64
65 fn register_source_counter(name: &str, description: &str) -> IntCounterVec {
75 let counter = register_int_counter_vec(name, description, &[SOURCE_LABEL]);
76 counter.with_label_values(&[CACHE]);
77 counter.with_label_values(&[DB]);
78 counter
79 }
80
81 linera_base::declare_metrics! {
82 pub(super) static CONTAINS_BLOB_COUNTER: IntCounterVec =
84 register_source_counter(
85 "contains_blob",
86 "The metric counting how often a blob is tested for existence from storage",
87 );
88
89 pub(super) static CONTAINS_BLOBS_COUNTER: IntCounterVec =
91 register_source_counter(
92 "contains_blobs",
93 "The metric counting how often multiple blobs are tested for existence from storage",
94 );
95
96 pub(super) static CONTAINS_BLOB_STATE_COUNTER: IntCounterVec =
98 register_source_counter(
99 "contains_blob_state",
100 "The metric counting how often a blob state is tested for existence from storage",
101 );
102
103 pub(super) static CONTAINS_CERTIFICATE_COUNTER: IntCounterVec =
105 register_source_counter(
106 "contains_certificate",
107 "The metric counting how often a certificate is tested for existence from storage",
108 );
109
110 #[doc(hidden)]
112 pub static READ_CONFIRMED_BLOCK_COUNTER: IntCounterVec =
113 register_source_counter(
114 "read_confirmed_block",
115 "The metric counting how often a hashed confirmed block is read from storage",
116 );
117
118 #[doc(hidden)]
120 pub(super) static READ_CONFIRMED_BLOCKS_COUNTER: IntCounterVec =
121 register_source_counter(
122 "read_confirmed_blocks",
123 "The metric counting how often confirmed blocks are read from storage",
124 );
125
126 #[doc(hidden)]
128 pub(super) static READ_BLOB_COUNTER: IntCounterVec =
129 register_source_counter(
130 "read_blob",
131 "The metric counting how often a blob is read from storage",
132 );
133
134 #[doc(hidden)]
136 pub(super) static READ_BLOB_STATE_COUNTER: IntCounterVec =
137 register_source_counter(
138 "read_blob_state",
139 "The metric counting how often a blob state is read from storage",
140 );
141
142 #[doc(hidden)]
144 pub(super) static WRITE_BLOB_COUNTER: IntCounter =
145 register_int_counter(
146 "write_blob",
147 "The metric counting how often a blob is written to storage",
148 );
149
150 #[doc(hidden)]
152 pub static READ_CERTIFICATE_COUNTER: IntCounterVec =
153 register_source_counter(
154 "read_certificate",
155 "The metric counting how often a certificate is read from storage",
156 );
157
158 #[doc(hidden)]
160 pub(super) static READ_CERTIFICATES_COUNTER: IntCounterVec =
161 register_source_counter(
162 "read_certificates",
163 "The metric counting how often certificate are read from storage",
164 );
165
166 #[doc(hidden)]
168 pub static WRITE_CERTIFICATE_COUNTER: IntCounter =
169 register_int_counter(
170 "write_certificate",
171 "The metric counting how often a certificate is written to storage",
172 );
173
174 pub(super) static CERTIFICATE_LITE_BYTES: Histogram =
180 register_histogram(
181 "certificate_lite_bytes",
182 "Serialized size of the lite-certificate (signatures + metadata) in bytes",
183 exponential_bucket_interval(128.0, 2_097_152.0),
184 );
185
186 pub(super) static CERTIFICATE_VALUE_BYTES: Histogram =
190 register_histogram(
191 "certificate_value_bytes",
192 "Serialized size of the certificate value (block payload) in bytes",
193 exponential_bucket_interval(256.0, 16_777_216.0),
194 );
195
196 pub(super) static CERTIFICATE_SIGNER_COUNT: Histogram =
200 register_histogram(
201 "certificate_signer_count",
202 "Number of validator signatures attached to each confirmed certificate",
203 linear_bucket_interval(1.0, 1.0, 20.0),
204 );
205
206 #[doc(hidden)]
208 pub(crate) static LOAD_CHAIN_LATENCY: HistogramVec =
209 register_histogram_vec(
210 "load_chain_latency",
211 "The latency to load a chain state",
212 &[],
213 exponential_bucket_latencies(1000.0),
214 );
215
216 #[doc(hidden)]
218 pub(super) static READ_EVENT_COUNTER: IntCounterVec =
219 register_source_counter(
220 "read_event",
221 "The metric counting how often an event is read from storage",
222 );
223
224 pub(super) static CONTAINS_EVENT_COUNTER: IntCounterVec =
226 register_source_counter(
227 "contains_event",
228 "The metric counting how often an event is tested for existence from storage",
229 );
230
231 #[doc(hidden)]
233 pub(super) static WRITE_EVENT_COUNTER: IntCounter =
234 register_int_counter(
235 "write_event",
236 "The metric counting how often an event is written to storage",
237 );
238
239 #[doc(hidden)]
241 pub(super) static READ_BLOCK_HASH_BY_HEIGHT_COUNTER: IntCounterVec =
242 register_source_counter(
243 "read_block_hash_by_height",
244 "The metric counting how often a block hash is read by height from storage",
245 );
246
247 #[doc(hidden)]
249 pub(super) static READ_EVENT_BLOCK_HEIGHT_COUNTER: IntCounterVec =
250 register_source_counter(
251 "read_event_block_height",
252 "The metric counting how often an event block height is read from storage",
253 );
254
255 #[doc(hidden)]
257 pub(super) static READ_NETWORK_DESCRIPTION: IntCounterVec =
258 register_source_counter(
259 "network_description",
260 "The metric counting how often the network description is read from storage",
261 );
262
263 #[doc(hidden)]
265 pub(super) static WRITE_NETWORK_DESCRIPTION: IntCounter =
266 register_int_counter(
267 "write_network_description",
268 "The metric counting how often the network description is written to storage",
269 );
270 }
271}
272
273const BLOB_KEY: &[u8] = &[0];
275
276const BLOB_STATE_KEY: &[u8] = &[1];
278
279const LITE_CERTIFICATE_KEY: &[u8] = &[2];
281
282const BLOCK_KEY: &[u8] = &[3];
284
285const NETWORK_DESCRIPTION_KEY: &[u8] = &[4];
287
288fn get_block_keys() -> Vec<Vec<u8>> {
289 vec![LITE_CERTIFICATE_KEY.to_vec(), BLOCK_KEY.to_vec()]
290}
291
292#[derive(Default)]
293#[expect(clippy::type_complexity)]
294struct MultiPartitionBatch {
295 keys_value_bytes: BTreeMap<Vec<u8>, Vec<(Vec<u8>, Vec<u8>)>>,
296}
297
298impl MultiPartitionBatch {
299 fn new() -> Self {
300 Self::default()
301 }
302
303 fn put_key_values(&mut self, root_key: Vec<u8>, key_values: Vec<(Vec<u8>, Vec<u8>)>) {
304 let entry = self.keys_value_bytes.entry(root_key).or_default();
305 entry.extend(key_values);
306 }
307
308 fn put_key_value(&mut self, root_key: Vec<u8>, key: Vec<u8>, value: Vec<u8>) {
309 self.put_key_values(root_key, vec![(key, value)]);
310 }
311
312 fn add_blob(&mut self, blob: &Blob) {
313 #[cfg(with_metrics)]
314 metrics::WRITE_BLOB_COUNTER.inc();
315 let root_key = RootKey::BlobId(blob.id()).bytes();
316 let key = BLOB_KEY.to_vec();
317 self.put_key_value(root_key, key, blob.bytes().to_vec());
318 }
319
320 fn add_blob_state(&mut self, blob_id: BlobId, blob_state: &BlobState) -> Result<(), ViewError> {
321 let root_key = RootKey::BlobId(blob_id).bytes();
322 let key = BLOB_STATE_KEY.to_vec();
323 let value = bcs::to_bytes(blob_state)?;
324 self.put_key_value(root_key, key, value);
325 Ok(())
326 }
327
328 fn add_certificate(
337 &mut self,
338 certificate: &ConfirmedBlockCertificate,
339 ) -> Result<(), ViewError> {
340 #[cfg(with_metrics)]
341 {
342 metrics::WRITE_CERTIFICATE_COUNTER.inc();
343 metrics::CERTIFICATE_SIGNER_COUNT.observe(certificate.signatures().len() as f64);
344 }
345 let hash = certificate.hash();
346
347 let root_key = RootKey::BlockHash(hash).bytes();
349 let mut key_values = Vec::new();
350 let key = LITE_CERTIFICATE_KEY.to_vec();
351 let value = bcs::to_bytes(&certificate.lite_certificate())?;
352 #[cfg(with_metrics)]
353 metrics::CERTIFICATE_LITE_BYTES.observe(value.len() as f64);
354 key_values.push((key, value));
355 let key = BLOCK_KEY.to_vec();
356 let value = bcs::to_bytes(&certificate.value())?;
357 #[cfg(with_metrics)]
358 metrics::CERTIFICATE_VALUE_BYTES.observe(value.len() as f64);
359 key_values.push((key, value));
360 self.put_key_values(root_key, key_values);
361
362 let chain_id = certificate.value().block().header.chain_id;
364 let height = certificate.value().block().header.height;
365 let index_root_key = RootKey::BlockByHeight(chain_id).bytes();
366 let height_key = to_height_key(height);
367 let index_value = bcs::to_bytes(&hash)?;
368 self.put_key_value(index_root_key, height_key, index_value);
369
370 let event_index_root_key = RootKey::EventBlockHeight(chain_id).bytes();
372 let height_value = bcs::to_bytes(&height)?;
373 for event in certificate.value().block().body.events.iter().flatten() {
374 let event_key = to_event_key(&EventId {
375 chain_id,
376 stream_id: event.stream_id.clone(),
377 index: event.index,
378 });
379 self.put_key_value(
380 event_index_root_key.clone(),
381 event_key,
382 height_value.clone(),
383 );
384 }
385
386 Ok(())
387 }
388
389 fn add_event(&mut self, event_id: &EventId, value: Vec<u8>) {
390 #[cfg(with_metrics)]
391 metrics::WRITE_EVENT_COUNTER.inc();
392 let key = to_event_key(event_id);
393 let root_key = RootKey::Event(event_id.chain_id).bytes();
394 self.put_key_value(root_key, key, value);
395 }
396
397 fn add_network_description(
398 &mut self,
399 information: &NetworkDescription,
400 ) -> Result<(), ViewError> {
401 #[cfg(with_metrics)]
402 metrics::WRITE_NETWORK_DESCRIPTION.inc();
403 let root_key = RootKey::NetworkDescription.bytes();
404 let key = NETWORK_DESCRIPTION_KEY.to_vec();
405 let value = bcs::to_bytes(information)?;
406 self.put_key_value(root_key, key, value);
407 Ok(())
408 }
409}
410
411#[derive(Clone, Copy, Debug)]
413pub struct StorageCacheConfig {
414 pub blob_cache_size: usize,
416 pub confirmed_block_cache_size: usize,
418 pub certificate_cache_size: usize,
420 pub certificate_raw_cache_size: usize,
422 pub event_cache_size: usize,
424 pub block_hash_by_height_cache_size: usize,
426 pub event_block_height_cache_size: usize,
428 pub cache_cleanup_interval_secs: u64,
430}
431
432#[cfg(with_testing)]
434pub const DEFAULT_STORAGE_CACHE_CONFIG: StorageCacheConfig = StorageCacheConfig {
435 blob_cache_size: 1000,
436 confirmed_block_cache_size: 1000,
437 certificate_cache_size: 1000,
438 certificate_raw_cache_size: 1000,
439 event_cache_size: 1000,
440 block_hash_by_height_cache_size: 1000,
441 event_block_height_cache_size: 1000,
442 cache_cleanup_interval_secs: linera_cache::DEFAULT_CLEANUP_INTERVAL_SECS,
443};
444
445type RawCertificate = (Vec<u8>, Vec<u8>);
447
448#[derive(Clone)]
454pub struct StorageCaches {
455 pub(crate) blob: Arc<ValueCache<BlobId, Blob>>,
456 pub(crate) confirmed_block: Arc<ValueCache<CryptoHash, ConfirmedBlock>>,
457 pub(crate) certificate: Arc<ValueCache<CryptoHash, ConfirmedBlockCertificate>>,
458 pub(crate) certificate_raw: Arc<ValueCache<CryptoHash, RawCertificate>>,
459 pub(crate) event: Arc<ValueCache<EventId, Vec<u8>>>,
460 pub(crate) block_hash_by_height: Arc<ValueCache<(ChainId, BlockHeight), CryptoHash>>,
461 pub(crate) event_block_height: Arc<ValueCache<EventId, BlockHeight>>,
462 pub(crate) network_description: Arc<OnceLock<NetworkDescription>>,
463}
464
465impl StorageCaches {
466 pub fn new(sizes: StorageCacheConfig) -> Self {
468 let interval = sizes.cache_cleanup_interval_secs;
469 Self {
470 blob: Arc::new(ValueCache::new(
471 "storage_blob",
472 sizes.blob_cache_size,
473 interval,
474 )),
475 confirmed_block: Arc::new(ValueCache::new(
476 "storage_confirmed_block",
477 sizes.confirmed_block_cache_size,
478 interval,
479 )),
480 certificate: Arc::new(ValueCache::new(
481 "storage_certificate",
482 sizes.certificate_cache_size,
483 interval,
484 )),
485 certificate_raw: Arc::new(ValueCache::new(
486 "storage_certificate_raw",
487 sizes.certificate_raw_cache_size,
488 interval,
489 )),
490 event: Arc::new(ValueCache::new(
491 "storage_event",
492 sizes.event_cache_size,
493 interval,
494 )),
495 block_hash_by_height: Arc::new(ValueCache::new(
496 "storage_block_hash_by_height",
497 sizes.block_hash_by_height_cache_size,
498 interval,
499 )),
500 event_block_height: Arc::new(ValueCache::new(
501 "storage_event_block_height",
502 sizes.event_block_height_cache_size,
503 interval,
504 )),
505 network_description: Arc::new(OnceLock::new()),
506 }
507 }
508}
509
510#[derive(Clone)]
512pub struct DbStorage<Database, Clock = WallClock> {
513 database: Arc<Database>,
514 clock: Clock,
515 thread_pool: Arc<linera_execution::ThreadPool>,
516 wasm_runtime: Option<WasmRuntime>,
517 user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
518 user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
519 shared_committees: SharedCommittees,
520 caches: StorageCaches,
521 execution_runtime_config: ExecutionRuntimeConfig,
522}
523
524#[derive(Debug, Serialize, Deserialize)]
526pub enum RootKey {
527 NetworkDescription,
529 BlockExporterState(u32),
531 ChainState(ChainId),
533 BlockHash(CryptoHash),
535 BlobId(BlobId),
537 Event(ChainId),
539 BlockByHeight(ChainId),
541 EventBlockHeight(ChainId),
543}
544
545const CHAIN_ID_TAG: u8 = 2;
546const BLOB_ID_TAG: u8 = 4;
547const EVENT_ID_TAG: u8 = 5;
548
549impl RootKey {
550 pub fn bytes(&self) -> Vec<u8> {
552 bcs::to_bytes(self).unwrap()
553 }
554}
555
556#[derive(Debug, Serialize, Deserialize)]
557struct RestrictedEventId {
558 pub stream_id: StreamId,
559 pub index: u32,
560}
561
562fn to_event_key(event_id: &EventId) -> Vec<u8> {
563 let restricted_event_id = RestrictedEventId {
564 stream_id: event_id.stream_id.clone(),
565 index: event_id.index,
566 };
567 bcs::to_bytes(&restricted_event_id).unwrap()
568}
569
570pub(crate) fn to_height_key(height: BlockHeight) -> Vec<u8> {
571 bcs::to_bytes(&height).unwrap()
572}
573
574fn is_chain_state(root_key: &[u8]) -> bool {
575 if root_key.is_empty() {
576 return false;
577 }
578 root_key[0] == CHAIN_ID_TAG
579}
580
581#[derive(Clone, Copy)]
584pub struct ChainStatesFirstAssignment;
585
586impl DualStoreRootKeyAssignment for ChainStatesFirstAssignment {
587 fn assigned_store(root_key: &[u8]) -> Result<StoreInUse, bcs::Error> {
588 if root_key.is_empty() {
589 return Ok(StoreInUse::Second);
590 }
591 let store = match is_chain_state(root_key) {
592 true => StoreInUse::First,
593 false => StoreInUse::Second,
594 };
595 Ok(store)
596 }
597}
598
599#[derive(Clone)]
601pub struct WallClock;
602
603#[cfg_attr(not(web), async_trait)]
604#[cfg_attr(web, async_trait(?Send))]
605impl Clock for WallClock {
606 fn current_time(&self) -> Timestamp {
607 Timestamp::now()
608 }
609
610 async fn sleep_until(&self, timestamp: Timestamp) {
611 let delta = timestamp.delta_since(Timestamp::now());
612 if delta > TimeDelta::ZERO {
613 linera_base::time::timer::sleep(delta.as_duration()).await
614 }
615 }
616
617 async fn sleep_for(&self, duration: Duration) {
618 linera_base::time::timer::sleep(duration).await
619 }
620}
621
622#[cfg(with_testing)]
623#[derive(Default)]
624struct TestClockInner {
625 time: Timestamp,
626 sleeps: BTreeMap<Reverse<Timestamp>, Vec<oneshot::Sender<()>>>,
627 sleep_callback: Option<Box<dyn Fn(Timestamp) -> bool + Send + Sync>>,
630}
631
632#[cfg(with_testing)]
633impl TestClockInner {
634 fn set(&mut self, time: Timestamp) {
635 self.time = time;
636 let senders = self.sleeps.split_off(&Reverse(time));
637 for sender in senders.into_values().flatten() {
638 sender.send(()).ok();
640 }
641 }
642
643 fn add_sleep_until(&mut self, time: Timestamp) -> Receiver<()> {
644 let (sender, receiver) = oneshot::channel();
645 let should_auto_advance = self
646 .sleep_callback
647 .as_ref()
648 .is_some_and(|callback| callback(time));
649 if should_auto_advance && time > self.time {
650 self.set(time);
652 sender.send(()).ok();
654 } else if self.time >= time {
655 sender.send(()).ok();
657 } else {
658 self.sleeps.entry(Reverse(time)).or_default().push(sender);
659 }
660 receiver
661 }
662}
663
664#[cfg(with_testing)]
667#[derive(Clone, Default)]
668pub struct TestClock(Arc<std::sync::Mutex<TestClockInner>>);
669
670#[cfg(with_testing)]
671#[cfg_attr(not(web), async_trait)]
672#[cfg_attr(web, async_trait(?Send))]
673impl Clock for TestClock {
674 fn current_time(&self) -> Timestamp {
675 self.lock().time
676 }
677
678 async fn sleep_until(&self, timestamp: Timestamp) {
679 let receiver = self.lock().add_sleep_until(timestamp);
680 receiver.await.ok();
682 }
683}
684
685#[cfg(with_testing)]
686impl TestClock {
687 pub fn new() -> Self {
689 TestClock(Arc::default())
690 }
691
692 pub fn set(&self, time: Timestamp) {
694 self.lock().set(time);
695 }
696
697 pub fn add(&self, delta: TimeDelta) {
699 let mut guard = self.lock();
700 let time = guard.time.saturating_add(delta);
701 guard.set(time);
702 }
703
704 pub fn current_time(&self) -> Timestamp {
706 self.lock().time
707 }
708
709 #[cfg(with_testing)]
714 pub fn set_sleep_callback<F>(&self, callback: F)
715 where
716 F: Fn(Timestamp) -> bool + Send + Sync + 'static,
717 {
718 self.lock().sleep_callback = Some(Box::new(callback));
719 }
720
721 fn lock(&self) -> std::sync::MutexGuard<'_, TestClockInner> {
722 self.0.lock().expect("poisoned TestClock mutex")
723 }
724}
725
726#[cfg_attr(not(web), async_trait)]
727#[cfg_attr(web, async_trait(?Send))]
728impl<Database, C> Storage for DbStorage<Database, C>
729where
730 Database: KeyValueDatabase<
731 Store: KeyValueStore + Clone + linera_base::util::traits::AutoTraits + 'static,
732 Error: Send + Sync,
733 > + Clone
734 + linera_base::util::traits::AutoTraits
735 + 'static,
736 C: Clock + Clone + Send + Sync + 'static,
737{
738 type Context = ViewContext<ChainRuntimeContext<Self>, Database::Store>;
739 type Clock = C;
740 type BlockExporterContext = ViewContext<u32, Database::Store>;
741
742 fn clock(&self) -> &C {
743 &self.clock
744 }
745
746 fn thread_pool(&self) -> &Arc<linera_execution::ThreadPool> {
747 &self.thread_pool
748 }
749
750 fn shared_committees(&self) -> &SharedCommittees {
751 &self.shared_committees
752 }
753
754 #[instrument(level = "trace", skip_all, fields(chain_id = %chain_id))]
755 async fn load_chain(
756 &self,
757 chain_id: ChainId,
758 ) -> Result<ChainStateView<Self::Context>, ViewError> {
759 #[cfg(with_metrics)]
760 let _metric = metrics::LOAD_CHAIN_LATENCY.measure_latency();
761 let runtime_context = ChainRuntimeContext {
762 storage: self.clone(),
763 thread_pool: self.thread_pool.clone(),
764 chain_id,
765 execution_runtime_config: self.execution_runtime_config,
766 user_contracts: self.user_contracts.clone(),
767 user_services: self.user_services.clone(),
768 };
769 let root_key = RootKey::ChainState(chain_id).bytes();
770 let context =
771 ViewContext::create_root_context(&*self.database, &root_key, runtime_context).await?;
772 ChainStateView::load(context).await
773 }
774
775 #[instrument(level = "trace", skip_all, fields(%blob_id))]
776 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
777 if self.caches.blob.contains(&blob_id) {
778 #[cfg(with_metrics)]
779 metrics::CONTAINS_BLOB_COUNTER
780 .with_label_values(&[metrics::CACHE])
781 .inc();
782 return Ok(true);
783 }
784 let root_key = RootKey::BlobId(blob_id).bytes();
785 let store = self.database.open_shared(&root_key)?;
786 let test = store.contains_key(BLOB_KEY).await?;
787 #[cfg(with_metrics)]
788 metrics::CONTAINS_BLOB_COUNTER
789 .with_label_values(&[metrics::DB])
790 .inc();
791 Ok(test)
792 }
793
794 #[instrument(skip_all, fields(blob_count = blob_ids.len()))]
795 async fn missing_blobs(&self, blob_ids: &[BlobId]) -> Result<Vec<BlobId>, ViewError> {
796 let mut missing_blobs = Vec::new();
797 #[cfg(with_metrics)]
798 let mut cache_hits: u64 = 0;
799 #[cfg(with_metrics)]
800 let mut db_checks: u64 = 0;
801 for blob_id in blob_ids {
802 if self.caches.blob.contains(blob_id) {
803 #[cfg(with_metrics)]
804 {
805 cache_hits += 1;
806 }
807 continue;
808 }
809 #[cfg(with_metrics)]
810 {
811 db_checks += 1;
812 }
813 let root_key = RootKey::BlobId(*blob_id).bytes();
814 let store = self.database.open_shared(&root_key)?;
815 if !store.contains_key(BLOB_KEY).await? {
816 missing_blobs.push(*blob_id);
817 }
818 }
819 #[cfg(with_metrics)]
820 {
821 if cache_hits > 0 {
822 metrics::CONTAINS_BLOBS_COUNTER
823 .with_label_values(&[metrics::CACHE])
824 .inc_by(cache_hits);
825 }
826 if db_checks > 0 {
827 metrics::CONTAINS_BLOBS_COUNTER
828 .with_label_values(&[metrics::DB])
829 .inc_by(db_checks);
830 }
831 }
832 Ok(missing_blobs)
833 }
834
835 #[instrument(skip_all, fields(%blob_id))]
836 async fn contains_blob_state(&self, blob_id: BlobId) -> Result<bool, ViewError> {
837 let root_key = RootKey::BlobId(blob_id).bytes();
838 let store = self.database.open_shared(&root_key)?;
839 let test = store.contains_key(BLOB_STATE_KEY).await?;
840 #[cfg(with_metrics)]
841 metrics::CONTAINS_BLOB_STATE_COUNTER
842 .with_label_values(&[metrics::DB])
843 .inc();
844 Ok(test)
845 }
846
847 #[instrument(skip_all, fields(%hash))]
848 async fn read_confirmed_block(
849 &self,
850 hash: CryptoHash,
851 ) -> Result<Option<CacheArc<ConfirmedBlock>>, ViewError> {
852 if let Some(block) = self.caches.confirmed_block.get(&hash) {
853 #[cfg(with_metrics)]
854 metrics::READ_CONFIRMED_BLOCK_COUNTER
855 .with_label_values(&[metrics::CACHE])
856 .inc();
857 return Ok(Some(block));
858 }
859 let root_key = RootKey::BlockHash(hash).bytes();
860 let store = self.database.open_shared(&root_key)?;
861 let value = store.read_value::<ConfirmedBlock>(BLOCK_KEY).await?;
862 #[cfg(with_metrics)]
863 metrics::READ_CONFIRMED_BLOCK_COUNTER
864 .with_label_values(&[metrics::DB])
865 .inc();
866 match value {
867 Some(block) => Ok(Some(self.caches.confirmed_block.insert(&hash, block))),
868 None => Ok(None),
869 }
870 }
871
872 #[instrument(skip_all)]
873 async fn read_confirmed_blocks<I: IntoIterator<Item = CryptoHash> + Send>(
874 &self,
875 hashes: I,
876 ) -> Result<Vec<Option<CacheArc<ConfirmedBlock>>>, ViewError> {
877 let hashes = hashes.into_iter().collect::<Vec<_>>();
878 if hashes.is_empty() {
879 return Ok(Vec::new());
880 }
881 let mut results = vec![None; hashes.len()];
882 let mut misses = Vec::new();
883 for (i, hash) in hashes.iter().enumerate() {
884 if let Some(block) = self.caches.confirmed_block.get(hash) {
885 results[i] = Some(block);
886 } else {
887 misses.push(i);
888 }
889 }
890 if !misses.is_empty() {
891 let miss_hashes: Vec<_> = misses.iter().map(|&i| hashes[i]).collect();
892 let root_keys = Self::get_root_keys_for_certificates(&miss_hashes);
893 for (miss_idx, root_key) in misses.iter().zip(root_keys) {
894 let store = self.database.open_shared(&root_key)?;
895 if let Some(block) = store.read_value::<ConfirmedBlock>(BLOCK_KEY).await? {
896 results[*miss_idx] = Some(
897 self.caches
898 .confirmed_block
899 .insert(&hashes[*miss_idx], block),
900 );
901 }
902 }
903 }
904 #[cfg(with_metrics)]
905 {
906 let cache_hits = (hashes.len() - misses.len()) as u64;
907 if cache_hits > 0 {
908 metrics::READ_CONFIRMED_BLOCKS_COUNTER
909 .with_label_values(&[metrics::CACHE])
910 .inc_by(cache_hits);
911 }
912 let db_reads = misses.len() as u64;
913 if db_reads > 0 {
914 metrics::READ_CONFIRMED_BLOCKS_COUNTER
915 .with_label_values(&[metrics::DB])
916 .inc_by(db_reads);
917 }
918 }
919 Ok(results)
920 }
921
922 #[instrument(skip_all, fields(%blob_id))]
923 async fn read_blob(&self, blob_id: BlobId) -> Result<Option<CacheArc<Blob>>, ViewError> {
924 if let Some(blob) = self.caches.blob.get(&blob_id) {
925 #[cfg(with_metrics)]
926 metrics::READ_BLOB_COUNTER
927 .with_label_values(&[metrics::CACHE])
928 .inc();
929 return Ok(Some(blob));
930 }
931 let root_key = RootKey::BlobId(blob_id).bytes();
932 let store = self.database.open_shared(&root_key)?;
933 let maybe_blob_bytes = store.read_value_bytes(BLOB_KEY).await?;
934 #[cfg(with_metrics)]
935 metrics::READ_BLOB_COUNTER
936 .with_label_values(&[metrics::DB])
937 .inc();
938 match maybe_blob_bytes {
939 Some(blob_bytes) => {
940 let blob = Blob::new_with_id_unchecked(blob_id, blob_bytes);
941 Ok(Some(self.caches.blob.insert(&blob_id, blob)))
942 }
943 None => Ok(None),
944 }
945 }
946
947 #[instrument(skip_all, fields(blob_ids_len = %blob_ids.len()))]
948 async fn read_blobs(
949 &self,
950 blob_ids: &[BlobId],
951 ) -> Result<Vec<Option<CacheArc<Blob>>>, ViewError> {
952 if blob_ids.is_empty() {
953 return Ok(Vec::new());
954 }
955 futures::future::try_join_all(blob_ids.iter().map(|blob_id| self.read_blob(*blob_id))).await
961 }
962
963 #[instrument(skip_all, fields(%blob_id))]
964 async fn read_blob_state(&self, blob_id: BlobId) -> Result<Option<BlobState>, ViewError> {
965 let root_key = RootKey::BlobId(blob_id).bytes();
966 let store = self.database.open_shared(&root_key)?;
967 let blob_state = store.read_value::<BlobState>(BLOB_STATE_KEY).await?;
968 #[cfg(with_metrics)]
969 metrics::READ_BLOB_STATE_COUNTER
970 .with_label_values(&[metrics::DB])
971 .inc();
972 Ok(blob_state)
973 }
974
975 #[instrument(skip_all, fields(blob_ids_len = %blob_ids.len()))]
976 async fn read_blob_states(
977 &self,
978 blob_ids: &[BlobId],
979 ) -> Result<Vec<Option<BlobState>>, ViewError> {
980 if blob_ids.is_empty() {
981 return Ok(Vec::new());
982 }
983 futures::future::try_join_all(
984 blob_ids
985 .iter()
986 .map(|blob_id| self.read_blob_state(*blob_id)),
987 )
988 .await
989 }
990
991 #[instrument(skip_all, fields(blob_id = %blob.id()))]
992 async fn write_blob(&self, blob: &Blob) -> Result<(), ViewError> {
993 let mut batch = MultiPartitionBatch::new();
994 batch.add_blob(blob);
995 self.write_batch(batch).await?;
996 Ok(())
997 }
998
999 #[instrument(skip_all, fields(blob_ids_len = %blob_ids.len()))]
1000 async fn maybe_write_blob_states(
1001 &self,
1002 blob_ids: &[BlobId],
1003 blob_state: BlobState,
1004 ) -> Result<(), ViewError> {
1005 if blob_ids.is_empty() {
1006 return Ok(());
1007 }
1008 let mut maybe_blob_states = Vec::new();
1009 for blob_id in blob_ids {
1010 let root_key = RootKey::BlobId(*blob_id).bytes();
1011 let store = self.database.open_shared(&root_key)?;
1012 let maybe_blob_state = store.read_value::<BlobState>(BLOB_STATE_KEY).await?;
1013 maybe_blob_states.push(maybe_blob_state);
1014 }
1015 let mut batch = MultiPartitionBatch::new();
1016 for (maybe_blob_state, blob_id) in maybe_blob_states.iter().zip(blob_ids) {
1017 match maybe_blob_state {
1018 None => {
1019 batch.add_blob_state(*blob_id, &blob_state)?;
1020 }
1021 Some(state) => {
1022 if state.epoch < blob_state.epoch {
1023 batch.add_blob_state(*blob_id, &blob_state)?;
1024 }
1025 }
1026 }
1027 }
1028 self.write_batch(batch).await?;
1032 Ok(())
1033 }
1034
1035 #[instrument(skip_all, fields(blobs_len = %blobs.len()))]
1036 async fn maybe_write_blobs(&self, blobs: &[Blob]) -> Result<Vec<bool>, ViewError> {
1037 if blobs.is_empty() {
1038 return Ok(Vec::new());
1039 }
1040 let mut batch = MultiPartitionBatch::new();
1041 let mut blob_states = Vec::new();
1042 for blob in blobs {
1043 let root_key = RootKey::BlobId(blob.id()).bytes();
1044 let store = self.database.open_shared(&root_key)?;
1045 let has_state = store.contains_key(BLOB_STATE_KEY).await?;
1046 blob_states.push(has_state);
1047 if has_state {
1048 batch.add_blob(blob);
1049 }
1050 }
1051 self.write_batch(batch).await?;
1052 Ok(blob_states)
1053 }
1054
1055 #[instrument(skip_all, fields(blobs_len = %blobs.len()))]
1056 async fn write_blobs(&self, blobs: &[Blob]) -> Result<(), ViewError> {
1057 if blobs.is_empty() {
1058 return Ok(());
1059 }
1060 let mut batch = MultiPartitionBatch::new();
1061 for blob in blobs {
1062 batch.add_blob(blob);
1063 }
1064 self.write_batch(batch).await
1065 }
1066
1067 #[instrument(skip_all, fields(blobs_len = %blobs.len()))]
1068 async fn write_blobs_and_certificate(
1069 &self,
1070 blobs: &[Blob],
1071 certificate: &ConfirmedBlockCertificate,
1072 ) -> Result<(), ViewError> {
1073 let mut batch = MultiPartitionBatch::new();
1074 for blob in blobs {
1075 batch.add_blob(blob);
1076 }
1077 batch.add_certificate(certificate)?;
1078 self.write_batch(batch).await?;
1079 let block = certificate.value().block();
1081 let chain_id = block.header.chain_id;
1082 let height = block.header.height;
1083 let hash = certificate.hash();
1084 self.caches
1085 .block_hash_by_height
1086 .insert(&(chain_id, height), hash);
1087 for event in block.body.events.iter().flatten() {
1088 let event_id = EventId {
1089 chain_id,
1090 stream_id: event.stream_id.clone(),
1091 index: event.index,
1092 };
1093 self.caches.event_block_height.insert(&event_id, height);
1094 }
1095 Ok(())
1096 }
1097
1098 fn cache_certificate(
1099 &self,
1100 certificate: ConfirmedBlockCertificate,
1101 ) -> CacheArc<ConfirmedBlockCertificate> {
1102 self.caches
1103 .certificate
1104 .insert(&certificate.hash(), certificate)
1105 }
1106
1107 fn cache_blob(&self, blob: Blob) -> CacheArc<Blob> {
1108 self.caches.blob.insert(&blob.id(), blob)
1109 }
1110
1111 fn cache_confirmed_block(&self, block: ConfirmedBlock) -> CacheArc<ConfirmedBlock> {
1112 self.caches.confirmed_block.insert(&block.hash(), block)
1113 }
1114
1115 #[instrument(skip_all, fields(%hash))]
1116 async fn contains_certificate(&self, hash: CryptoHash) -> Result<bool, ViewError> {
1117 if self.caches.certificate.contains(&hash) || self.caches.certificate_raw.contains(&hash) {
1118 #[cfg(with_metrics)]
1119 metrics::CONTAINS_CERTIFICATE_COUNTER
1120 .with_label_values(&[metrics::CACHE])
1121 .inc();
1122 return Ok(true);
1123 }
1124 let root_key = RootKey::BlockHash(hash).bytes();
1125 let store = self.database.open_shared(&root_key)?;
1126 let results = store.contains_keys(&get_block_keys()).await?;
1127 #[cfg(with_metrics)]
1128 metrics::CONTAINS_CERTIFICATE_COUNTER
1129 .with_label_values(&[metrics::DB])
1130 .inc();
1131 Ok(results[0] && results[1])
1132 }
1133
1134 #[instrument(skip_all, fields(%hash))]
1135 async fn read_certificate(
1136 &self,
1137 hash: CryptoHash,
1138 ) -> Result<Option<CacheArc<ConfirmedBlockCertificate>>, ViewError> {
1139 if let Some(cert) = self.caches.certificate.get(&hash) {
1141 #[cfg(with_metrics)]
1142 metrics::READ_CERTIFICATE_COUNTER
1143 .with_label_values(&[metrics::CACHE])
1144 .inc();
1145 return Ok(Some(cert));
1146 }
1147 if let Some(raw) = self.caches.certificate_raw.get(&hash) {
1149 #[cfg(with_metrics)]
1150 metrics::READ_CERTIFICATE_COUNTER
1151 .with_label_values(&[metrics::CACHE])
1152 .inc();
1153 return self.deserialize_and_cache_certificate(&raw.0, &raw.1);
1154 }
1155 let root_key = RootKey::BlockHash(hash).bytes();
1157 let store = self.database.open_shared(&root_key)?;
1158 let values = store.read_multi_values_bytes(&get_block_keys()).await?;
1159 #[cfg(with_metrics)]
1160 metrics::READ_CERTIFICATE_COUNTER
1161 .with_label_values(&[metrics::DB])
1162 .inc();
1163 let Some(lite_cert_bytes) = values[0].as_ref() else {
1164 return Ok(None);
1165 };
1166 let Some(confirmed_block_bytes) = values[1].as_ref() else {
1167 return Ok(None);
1168 };
1169 self.caches.certificate_raw.insert(
1170 &hash,
1171 (lite_cert_bytes.clone(), confirmed_block_bytes.clone()),
1172 );
1173 self.deserialize_and_cache_certificate(lite_cert_bytes, confirmed_block_bytes)
1174 }
1175
1176 #[instrument(skip_all)]
1177 async fn read_certificates(
1178 &self,
1179 hashes: &[CryptoHash],
1180 ) -> Result<Vec<Option<CacheArc<ConfirmedBlockCertificate>>>, ViewError> {
1181 let raw_certs = self.read_certificates_raw(hashes).await?;
1182
1183 raw_certs
1184 .into_iter()
1185 .map(|maybe_raw| {
1186 let Some(raw) = maybe_raw else {
1187 return Ok(None);
1188 };
1189 self.deserialize_and_cache_certificate(&raw.0, &raw.1)
1190 })
1191 .collect()
1192 }
1193
1194 #[instrument(skip_all)]
1195 async fn read_certificates_raw(
1196 &self,
1197 hashes: &[CryptoHash],
1198 ) -> Result<Vec<Option<CacheArc<(Vec<u8>, Vec<u8>)>>>, ViewError> {
1199 if hashes.is_empty() {
1200 return Ok(Vec::new());
1201 }
1202 let mut results = vec![None; hashes.len()];
1203 let mut misses = Vec::new();
1204 for (i, hash) in hashes.iter().enumerate() {
1205 if let Some(raw) = self.caches.certificate_raw.get(hash) {
1206 results[i] = Some(raw);
1207 } else {
1208 misses.push(i);
1209 }
1210 }
1211 if !misses.is_empty() {
1212 let miss_hashes: Vec<_> = misses.iter().map(|&i| hashes[i]).collect();
1213 let root_keys = Self::get_root_keys_for_certificates(&miss_hashes);
1214 for (miss_idx, root_key) in misses.iter().zip(root_keys) {
1215 let store = self.database.open_shared(&root_key)?;
1216 let values = store.read_multi_values_bytes(&get_block_keys()).await?;
1217 if let (Some(lite), Some(block)) = (values[0].as_ref(), values[1].as_ref()) {
1218 results[*miss_idx] = Some(
1219 self.caches
1220 .certificate_raw
1221 .insert(&hashes[*miss_idx], (lite.clone(), block.clone())),
1222 );
1223 }
1224 }
1225 }
1226 #[cfg(with_metrics)]
1227 {
1228 let cache_hits = (hashes.len() - misses.len()) as u64;
1229 if cache_hits > 0 {
1230 metrics::READ_CERTIFICATES_COUNTER
1231 .with_label_values(&[metrics::CACHE])
1232 .inc_by(cache_hits);
1233 }
1234 let db_reads = misses.len() as u64;
1235 if db_reads > 0 {
1236 metrics::READ_CERTIFICATES_COUNTER
1237 .with_label_values(&[metrics::DB])
1238 .inc_by(db_reads);
1239 }
1240 }
1241 Ok(results)
1242 }
1243
1244 async fn read_certificate_hashes_by_heights(
1245 &self,
1246 chain_id: ChainId,
1247 heights: &[BlockHeight],
1248 ) -> Result<Vec<Option<CryptoHash>>, ViewError> {
1249 if heights.is_empty() {
1250 return Ok(Vec::new());
1251 }
1252
1253 let mut results = vec![None; heights.len()];
1254 let mut misses = Vec::new();
1255 for (i, &height) in heights.iter().enumerate() {
1256 if let Some(hash) = self.caches.block_hash_by_height.get(&(chain_id, height)) {
1257 results[i] = Some(*hash);
1258 } else {
1259 misses.push(i);
1260 }
1261 }
1262 #[cfg(with_metrics)]
1263 {
1264 let cache_hits = (heights.len() - misses.len()) as u64;
1265 if cache_hits > 0 {
1266 metrics::READ_BLOCK_HASH_BY_HEIGHT_COUNTER
1267 .with_label_values(&[metrics::CACHE])
1268 .inc_by(cache_hits);
1269 }
1270 }
1271 if !misses.is_empty() {
1272 let miss_keys: Vec<Vec<u8>> =
1273 misses.iter().map(|&i| to_height_key(heights[i])).collect();
1274 let index_root_key = RootKey::BlockByHeight(chain_id).bytes();
1275 let store = self.database.open_shared(&index_root_key)?;
1276 let hash_bytes = store.read_multi_values_bytes(&miss_keys).await?;
1277 #[cfg(with_metrics)]
1278 {
1279 let db_reads = misses.len() as u64;
1280 metrics::READ_BLOCK_HASH_BY_HEIGHT_COUNTER
1281 .with_label_values(&[metrics::DB])
1282 .inc_by(db_reads);
1283 }
1284 for (miss_idx, opt_bytes) in misses.iter().zip(hash_bytes) {
1285 if let Some(bytes) = opt_bytes {
1286 let hash = bcs::from_bytes::<CryptoHash>(&bytes)?;
1287 self.caches
1288 .block_hash_by_height
1289 .insert(&(chain_id, heights[*miss_idx]), hash);
1290 results[*miss_idx] = Some(hash);
1291 }
1292 }
1293 }
1294
1295 Ok(results)
1296 }
1297
1298 async fn read_event_block_heights(
1299 &self,
1300 event_ids: &[EventId],
1301 ) -> Result<Vec<Option<BlockHeight>>, ViewError> {
1302 if event_ids.is_empty() {
1303 return Ok(Vec::new());
1304 }
1305
1306 let mut results = vec![None; event_ids.len()];
1307 let mut misses: Vec<usize> = Vec::new();
1309 for (i, event_id) in event_ids.iter().enumerate() {
1310 if let Some(height) = self.caches.event_block_height.get(event_id) {
1311 results[i] = Some(*height);
1312 } else {
1313 misses.push(i);
1314 }
1315 }
1316 #[cfg(with_metrics)]
1317 {
1318 let cache_hits = (event_ids.len() - misses.len()) as u64;
1319 if cache_hits > 0 {
1320 metrics::READ_EVENT_BLOCK_HEIGHT_COUNTER
1321 .with_label_values(&[metrics::CACHE])
1322 .inc_by(cache_hits);
1323 }
1324 }
1325 if misses.is_empty() {
1326 return Ok(results);
1327 }
1328 let mut chain_groups = BTreeMap::<_, Vec<_>>::new();
1330 for &i in &misses {
1331 let event_id = &event_ids[i];
1332 chain_groups
1333 .entry(event_id.chain_id)
1334 .or_default()
1335 .push((i, to_event_key(event_id)));
1336 }
1337 for (chain_id, entries) in chain_groups {
1338 let root_key = RootKey::EventBlockHeight(chain_id).bytes();
1339 let store = self.database.open_shared(&root_key)?;
1340 let keys = entries
1341 .iter()
1342 .map(|(_, key)| key.clone())
1343 .collect::<Vec<_>>();
1344 let values = store.read_multi_values_bytes(&keys).await?;
1345 #[cfg(with_metrics)]
1346 {
1347 let db_reads = entries.len() as u64;
1348 metrics::READ_EVENT_BLOCK_HEIGHT_COUNTER
1349 .with_label_values(&[metrics::DB])
1350 .inc_by(db_reads);
1351 }
1352 for ((original_index, _), value) in entries.into_iter().zip(values) {
1353 if let Some(bytes) = value {
1354 let height = bcs::from_bytes::<BlockHeight>(&bytes)?;
1355 self.caches
1356 .event_block_height
1357 .insert(&event_ids[original_index], height);
1358 results[original_index] = Some(height);
1359 }
1360 }
1361 }
1362 Ok(results)
1363 }
1364
1365 #[instrument(skip_all)]
1366 async fn read_certificates_by_heights_raw(
1367 &self,
1368 chain_id: ChainId,
1369 heights: &[BlockHeight],
1370 ) -> Result<Vec<Option<CacheArc<(Vec<u8>, Vec<u8>)>>>, ViewError> {
1371 let hashes: Vec<Option<CryptoHash>> = self
1372 .read_certificate_hashes_by_heights(chain_id, heights)
1373 .await?;
1374
1375 let mut indices: HashMap<CryptoHash, Vec<usize>> = HashMap::new();
1377 for (index, maybe_hash) in hashes.iter().enumerate() {
1378 if let Some(hash) = maybe_hash {
1379 indices.entry(*hash).or_default().push(index);
1380 }
1381 }
1382
1383 let unique_hashes = indices.keys().copied().collect::<Vec<_>>();
1385
1386 let mut result = vec![None; heights.len()];
1387
1388 for (raw_cert, hash) in self
1389 .read_certificates_raw(&unique_hashes)
1390 .await?
1391 .into_iter()
1392 .zip(unique_hashes)
1393 {
1394 if let Some(idx_list) = indices.get(&hash) {
1395 for &index in idx_list {
1396 result[index] = raw_cert.clone();
1397 }
1398 } else {
1399 tracing::error!(?hash, "certificate hash not found in indices map",);
1401 }
1402 }
1403
1404 Ok(result)
1405 }
1406
1407 #[instrument(skip_all, fields(%chain_id, heights_len = heights.len()))]
1408 async fn read_certificates_by_heights(
1409 &self,
1410 chain_id: ChainId,
1411 heights: &[BlockHeight],
1412 ) -> Result<Vec<Option<CacheArc<ConfirmedBlockCertificate>>>, ViewError> {
1413 self.read_certificates_by_heights_raw(chain_id, heights)
1414 .await?
1415 .into_iter()
1416 .map(|maybe_raw| match maybe_raw {
1417 None => Ok(None),
1418 Some(raw) => self.deserialize_and_cache_certificate(&raw.0, &raw.1),
1419 })
1420 .collect()
1421 }
1422
1423 #[instrument(skip_all, fields(event_id = ?event_id))]
1424 async fn read_event(&self, event_id: EventId) -> Result<Option<CacheArc<Vec<u8>>>, ViewError> {
1425 if let Some(event) = self.caches.event.get(&event_id) {
1426 #[cfg(with_metrics)]
1427 metrics::READ_EVENT_COUNTER
1428 .with_label_values(&[metrics::CACHE])
1429 .inc();
1430 return Ok(Some(event));
1431 }
1432 let event_key = to_event_key(&event_id);
1433 let root_key = RootKey::Event(event_id.chain_id).bytes();
1434 let store = self.database.open_shared(&root_key)?;
1435 let event = store.read_value_bytes(&event_key).await?;
1436 #[cfg(with_metrics)]
1437 metrics::READ_EVENT_COUNTER
1438 .with_label_values(&[metrics::DB])
1439 .inc();
1440 match event {
1441 Some(event_bytes) => Ok(Some(self.caches.event.insert(&event_id, event_bytes))),
1442 None => Ok(None),
1443 }
1444 }
1445
1446 #[instrument(skip_all, fields(event_id = ?event_id))]
1447 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1448 if self.caches.event.contains(&event_id) {
1449 #[cfg(with_metrics)]
1450 metrics::CONTAINS_EVENT_COUNTER
1451 .with_label_values(&[metrics::CACHE])
1452 .inc();
1453 return Ok(true);
1454 }
1455 let event_key = to_event_key(&event_id);
1456 let root_key = RootKey::Event(event_id.chain_id).bytes();
1457 let store = self.database.open_shared(&root_key)?;
1458 let exists = store.contains_key(&event_key).await?;
1459 #[cfg(with_metrics)]
1460 metrics::CONTAINS_EVENT_COUNTER
1461 .with_label_values(&[metrics::DB])
1462 .inc();
1463 Ok(exists)
1464 }
1465
1466 #[instrument(skip_all, fields(chain_id = %chain_id, stream_id = %stream_id, start_index = %start_index))]
1467 async fn read_events_from_index(
1468 &self,
1469 chain_id: &ChainId,
1470 stream_id: &StreamId,
1471 start_index: u32,
1472 ) -> Result<Vec<IndexAndEvent>, ViewError> {
1473 let root_key = RootKey::Event(*chain_id).bytes();
1474 let store = self.database.open_shared(&root_key)?;
1475 let mut entries = Vec::new();
1478 let mut db_keys = Vec::new();
1479 let prefix = bcs::to_bytes(stream_id).unwrap();
1480 for short_key in store.find_keys_by_prefix(&prefix).await? {
1481 let index = bcs::from_bytes::<u32>(&short_key)?;
1482 if index >= start_index {
1483 let event_id = EventId {
1484 chain_id: *chain_id,
1485 stream_id: stream_id.clone(),
1486 index,
1487 };
1488 let cached = self.caches.event.get(&event_id).map(|arc| (*arc).clone());
1489 if cached.is_none() {
1490 let mut key = prefix.clone();
1491 key.extend(short_key);
1492 db_keys.push(key);
1493 }
1494 entries.push((index, cached));
1495 }
1496 }
1497 let mut db_values = if db_keys.is_empty() {
1498 Vec::new()
1499 } else {
1500 store.read_multi_values_bytes(&db_keys).await?
1501 }
1502 .into_iter();
1503 let mut returned_values = Vec::with_capacity(entries.len());
1504 for (index, cached) in entries {
1505 let event = match cached {
1506 Some(event) => event,
1507 None => {
1508 let event_bytes = db_values
1509 .next()
1510 .expect("one database value per cache miss")
1511 .unwrap();
1512 let event_id = EventId {
1513 chain_id: *chain_id,
1514 stream_id: stream_id.clone(),
1515 index,
1516 };
1517 self.caches.event.insert(&event_id, event_bytes.clone());
1518 event_bytes
1519 }
1520 };
1521 returned_values.push(IndexAndEvent { index, event });
1522 }
1523 Ok(returned_values)
1524 }
1525
1526 #[instrument(skip_all)]
1527 async fn write_events(
1528 &self,
1529 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1530 ) -> Result<(), ViewError> {
1531 let mut batch = MultiPartitionBatch::new();
1532 for (event_id, value) in events {
1533 batch.add_event(&event_id, value);
1534 }
1535 self.write_batch(batch).await
1536 }
1537
1538 #[instrument(skip_all)]
1539 async fn read_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1540 if let Some(desc) = self.caches.network_description.get() {
1541 #[cfg(with_metrics)]
1542 metrics::READ_NETWORK_DESCRIPTION
1543 .with_label_values(&[metrics::CACHE])
1544 .inc();
1545 return Ok(Some(desc.clone()));
1546 }
1547 let root_key = RootKey::NetworkDescription.bytes();
1548 let store = self.database.open_shared(&root_key)?;
1549 let maybe_value: Option<NetworkDescription> =
1550 store.read_value(NETWORK_DESCRIPTION_KEY).await?;
1551 #[cfg(with_metrics)]
1552 metrics::READ_NETWORK_DESCRIPTION
1553 .with_label_values(&[metrics::DB])
1554 .inc();
1555 if let Some(ref desc) = maybe_value {
1556 if self.caches.network_description.set(desc.clone()).is_err() {
1557 debug!("network description cache was already populated concurrently");
1558 }
1559 }
1560 Ok(maybe_value)
1561 }
1562
1563 #[instrument(skip_all)]
1564 async fn write_network_description(
1565 &self,
1566 information: &NetworkDescription,
1567 ) -> Result<(), ViewError> {
1568 let mut batch = MultiPartitionBatch::new();
1569 batch.add_network_description(information)?;
1570 self.write_batch(batch).await?;
1571 Ok(())
1572 }
1573
1574 fn wasm_runtime(&self) -> Option<WasmRuntime> {
1575 self.wasm_runtime
1576 }
1577
1578 #[instrument(skip_all)]
1579 async fn block_exporter_context(
1580 &self,
1581 block_exporter_id: u32,
1582 ) -> Result<Self::BlockExporterContext, ViewError> {
1583 let root_key = RootKey::BlockExporterState(block_exporter_id).bytes();
1584 Ok(ViewContext::create_root_context(&*self.database, &root_key, block_exporter_id).await?)
1585 }
1586
1587 async fn list_blob_ids(&self) -> Result<Vec<BlobId>, ViewError> {
1588 let root_keys = self.database.list_root_keys().await?;
1589 let mut blob_ids = Vec::new();
1590 for root_key in root_keys {
1591 if !root_key.is_empty() && root_key[0] == BLOB_ID_TAG {
1592 let root_key_red = &root_key[1..];
1593 let blob_id = bcs::from_bytes(root_key_red)?;
1594 blob_ids.push(blob_id);
1595 }
1596 }
1597 Ok(blob_ids)
1598 }
1599
1600 async fn list_chain_ids(&self) -> Result<Vec<ChainId>, ViewError> {
1601 let root_keys = self.database.list_root_keys().await?;
1602 let mut chain_ids = Vec::new();
1603 for root_key in root_keys {
1604 if !root_key.is_empty() && root_key[0] == CHAIN_ID_TAG {
1605 let root_key_red = &root_key[1..];
1606 let chain_id = bcs::from_bytes(root_key_red)?;
1607 chain_ids.push(chain_id);
1608 }
1609 }
1610 Ok(chain_ids)
1611 }
1612
1613 async fn list_event_ids(&self) -> Result<Vec<EventId>, ViewError> {
1614 let root_keys = self.database.list_root_keys().await?;
1615 let mut event_ids = Vec::new();
1616 for root_key in root_keys {
1617 if !root_key.is_empty() && root_key[0] == EVENT_ID_TAG {
1618 let root_key_red = &root_key[1..];
1619 let chain_id = bcs::from_bytes(root_key_red)?;
1620 let store = self.database.open_shared(&root_key)?;
1621 let keys = store.find_keys_by_prefix(&[]).await?;
1622 for key in keys {
1623 let restricted_event_id = bcs::from_bytes::<RestrictedEventId>(&key)?;
1624 let event_id = EventId {
1625 chain_id,
1626 stream_id: restricted_event_id.stream_id,
1627 index: restricted_event_id.index,
1628 };
1629 event_ids.push(event_id);
1630 }
1631 }
1632 }
1633 Ok(event_ids)
1634 }
1635}
1636
1637impl<Database, C> DbStorage<Database, C>
1638where
1639 Database: KeyValueDatabase + Clone,
1640 Database::Store: KeyValueStore + Clone,
1641 C: Clock,
1642 Database::Error: Send + Sync,
1643{
1644 #[instrument(skip_all)]
1645 fn get_root_keys_for_certificates(hashes: &[CryptoHash]) -> Vec<Vec<u8>> {
1646 hashes
1647 .iter()
1648 .map(|hash| RootKey::BlockHash(*hash).bytes())
1649 .collect()
1650 }
1651
1652 fn deserialize_and_cache_certificate(
1653 &self,
1654 lite_cert_bytes: &[u8],
1655 confirmed_block_bytes: &[u8],
1656 ) -> Result<Option<CacheArc<ConfirmedBlockCertificate>>, ViewError> {
1657 let lite = bcs::from_bytes::<LiteCertificate>(lite_cert_bytes)?;
1658 let block = bcs::from_bytes::<ConfirmedBlock>(confirmed_block_bytes)?;
1659 let hash = block.hash();
1660 self.caches.confirmed_block.insert(&hash, block.clone());
1661 let certificate = lite
1662 .into_confirmed_certificate(block)
1663 .ok_or(ViewError::InconsistentEntries)?;
1664 let arc = self.caches.certificate.insert(&hash, certificate);
1665 Ok(Some(arc))
1666 }
1667
1668 #[instrument(skip_all)]
1669 async fn write_entry(
1670 store: &Database::Store,
1671 key_values: Vec<(Vec<u8>, Vec<u8>)>,
1672 ) -> Result<(), ViewError> {
1673 let mut batch = Batch::new();
1674 for (key, value) in key_values {
1675 batch.put_key_value_bytes(key, value);
1676 }
1677 store.write_batch(batch).await?;
1678 Ok(())
1679 }
1680
1681 #[instrument(skip_all, fields(batch_size = batch.keys_value_bytes.len()))]
1682 async fn write_batch(&self, batch: MultiPartitionBatch) -> Result<(), ViewError> {
1683 if batch.keys_value_bytes.is_empty() {
1684 return Ok(());
1685 }
1686 let mut futures = Vec::new();
1687 for (root_key, key_values) in batch.keys_value_bytes {
1688 let store = self.database.open_shared(&root_key)?;
1689 futures.push(async move { Self::write_entry(&store, key_values).await });
1690 }
1691 futures::future::try_join_all(futures).await?;
1692 Ok(())
1693 }
1694}
1695
1696impl<Database, C> DbStorage<Database, C> {
1697 fn new(
1698 database: Database,
1699 wasm_runtime: Option<WasmRuntime>,
1700 cache_sizes: StorageCacheConfig,
1701 clock: C,
1702 ) -> Self {
1703 Self {
1704 database: Arc::new(database),
1705 clock,
1706 #[cfg_attr(web, expect(clippy::arc_with_non_send_sync))]
1708 thread_pool: Arc::new(linera_execution::ThreadPool::new(20)),
1709 wasm_runtime,
1710 user_contracts: Arc::new(papaya::HashMap::new()),
1711 user_services: Arc::new(papaya::HashMap::new()),
1712 shared_committees: SharedCommittees::new(),
1713 caches: StorageCaches::new(cache_sizes),
1714 execution_runtime_config: ExecutionRuntimeConfig::default(),
1715 }
1716 }
1717
1718 pub fn with_allow_application_logs(mut self, allow: bool) -> Self {
1720 self.execution_runtime_config.allow_application_logs = allow;
1721 self
1722 }
1723}
1724
1725impl<Database> DbStorage<Database, WallClock>
1726where
1727 Database: KeyValueDatabase + Clone + 'static,
1728 Database::Error: Send + Sync,
1729 Database::Store: KeyValueStore + Clone + 'static,
1730{
1731 pub async fn maybe_create_and_connect(
1733 config: &Database::Config,
1734 namespace: &str,
1735 wasm_runtime: Option<WasmRuntime>,
1736 cache_sizes: StorageCacheConfig,
1737 ) -> Result<Self, Database::Error> {
1738 let database = Database::maybe_create_and_connect(config, namespace).await?;
1739 Ok(Self::new(database, wasm_runtime, cache_sizes, WallClock))
1740 }
1741
1742 pub async fn connect(
1744 config: &Database::Config,
1745 namespace: &str,
1746 wasm_runtime: Option<WasmRuntime>,
1747 cache_sizes: StorageCacheConfig,
1748 ) -> Result<Self, Database::Error> {
1749 let database = Database::connect(config, namespace).await?;
1750 Ok(Self::new(database, wasm_runtime, cache_sizes, WallClock))
1751 }
1752}
1753
1754#[cfg(with_testing)]
1755impl<Database, C> DbStorage<Database, C>
1756where
1757 Database: linera_views::backends::DatabaseBackup,
1758{
1759 pub fn backup_to(&self, dir: &std::path::Path) -> anyhow::Result<()> {
1761 self.database.backup_to(dir)
1762 }
1763}
1764
1765#[cfg(with_testing)]
1766impl<Database> DbStorage<Database, TestClock>
1767where
1768 Database: TestKeyValueDatabase + Clone + Send + Sync + 'static,
1769 Database::Store: KeyValueStore + Clone + Send + Sync + 'static,
1770 Database::Error: Send + Sync,
1771{
1772 pub async fn make_test_storage(wasm_runtime: Option<WasmRuntime>) -> Self {
1774 let config = Database::new_test_config().await.unwrap();
1775 let namespace = generate_test_namespace();
1776 DbStorage::<Database, TestClock>::new_for_testing(
1777 config,
1778 &namespace,
1779 wasm_runtime,
1780 TestClock::new(),
1781 )
1782 .await
1783 .unwrap()
1784 }
1785
1786 pub async fn new_for_testing(
1788 config: Database::Config,
1789 namespace: &str,
1790 wasm_runtime: Option<WasmRuntime>,
1791 clock: TestClock,
1792 ) -> Result<Self, Database::Error> {
1793 let database = Database::recreate_and_connect(&config, namespace).await?;
1794 Ok(Self::new(
1795 database,
1796 wasm_runtime,
1797 DEFAULT_STORAGE_CACHE_CONFIG,
1798 clock,
1799 ))
1800 }
1801
1802 pub async fn connect_for_testing(
1804 config: Database::Config,
1805 namespace: &str,
1806 wasm_runtime: Option<WasmRuntime>,
1807 clock: TestClock,
1808 ) -> Result<Self, Database::Error> {
1809 let database = Database::connect(&config, namespace).await?;
1810 Ok(Self::new(
1811 database,
1812 wasm_runtime,
1813 DEFAULT_STORAGE_CACHE_CONFIG,
1814 clock,
1815 ))
1816 }
1817}
1818
1819#[cfg(test)]
1820mod tests {
1821 use linera_base::{
1822 crypto::{CryptoHash, TestString},
1823 data_types::{Amount, Blob, BlobContent, BlockHeight, Event, OracleResponse, Round},
1824 identifiers::{
1825 Account, AccountOwner, ApplicationId, BlobId, BlobType, ChainId, EventId,
1826 GenericApplicationId, StreamId, StreamName,
1827 },
1828 };
1829 use linera_chain::{
1830 block::{Block, ConfirmedBlock},
1831 data_types::{OperationResult, Transaction},
1832 test::BlockBuilder,
1833 types::ConfirmedBlockCertificate,
1834 };
1835 use linera_execution::{
1836 system::{SystemMessage, SystemOperation},
1837 Message, MessageKind, Operation, OutgoingMessage,
1838 };
1839 use linera_views::{
1840 memory::MemoryDatabase,
1841 store::{KeyValueDatabase, ReadableKeyValueStore as _},
1842 };
1843
1844 use crate::{
1845 db_storage::{
1846 to_event_key, to_height_key, MultiPartitionBatch, RootKey, BLOB_ID_TAG, CHAIN_ID_TAG,
1847 EVENT_ID_TAG,
1848 },
1849 DbStorage, Storage, TestClock,
1850 };
1851
1852 fn populated_block(chain_id: ChainId, height: u64) -> Block {
1856 let owner = AccountOwner::CHAIN;
1857 let stream_id = StreamId {
1858 application_id: GenericApplicationId::System,
1859 stream_name: StreamName(b"test_stream".to_vec()),
1860 };
1861 BlockBuilder::new(chain_id, BlockHeight(height))
1862 .with_state_hash(CryptoHash::new(&TestString::new(format!(
1863 "state_hash_{height}"
1864 ))))
1865 .with_transaction(Transaction::ExecuteOperation(Operation::System(Box::new(
1866 SystemOperation::Transfer {
1867 owner,
1868 recipient: Account::chain(chain_id),
1869 amount: Amount::ONE,
1870 },
1871 ))))
1872 .with_messages(vec![OutgoingMessage {
1873 destination: chain_id,
1874 authenticated_owner: None,
1875 grant: Amount::ZERO,
1876 refund_grant_to: None,
1877 kind: MessageKind::Simple,
1878 message: Message::System(SystemMessage::Credit {
1879 target: owner,
1880 amount: Amount::ONE,
1881 source: owner,
1882 }),
1883 }])
1884 .with_events(vec![Event {
1885 stream_id,
1886 index: 0,
1887 value: b"event".to_vec(),
1888 }])
1889 .with_oracle_responses(vec![OracleResponse::Round(Some(0))])
1890 .with_blobs(vec![Blob::new(BlobContent::new_data(b"blob".to_vec()))])
1891 .with_operation_result(OperationResult(b"result".to_vec()))
1892 .build()
1893 }
1894
1895 #[test]
1902 fn test_root_key_blob_serialization() {
1903 let hash = CryptoHash::default();
1904 let blob_type = BlobType::default();
1905 let blob_id = BlobId::new(hash, blob_type);
1906 let root_key = RootKey::BlobId(blob_id).bytes();
1907 assert_eq!(root_key[0], BLOB_ID_TAG);
1908 assert_eq!(bcs::from_bytes::<BlobId>(&root_key[1..]).unwrap(), blob_id);
1909 }
1910
1911 #[test]
1914 fn test_root_key_chainstate_serialization() {
1915 let hash = CryptoHash::default();
1916 let chain_id = ChainId(hash);
1917 let root_key = RootKey::ChainState(chain_id).bytes();
1918 assert_eq!(root_key[0], CHAIN_ID_TAG);
1919 assert_eq!(
1920 bcs::from_bytes::<ChainId>(&root_key[1..]).unwrap(),
1921 chain_id
1922 );
1923 }
1924
1925 #[test]
1928 fn test_root_key_event_serialization() {
1929 let hash = CryptoHash::test_hash("49");
1930 let chain_id = ChainId(hash);
1931 let application_description_hash = CryptoHash::test_hash("42");
1932 let application_id = ApplicationId::new(application_description_hash);
1933 let application_id = GenericApplicationId::User(application_id);
1934 let stream_name = StreamName(bcs::to_bytes("linera_stream").unwrap());
1935 let stream_id = StreamId {
1936 application_id,
1937 stream_name,
1938 };
1939 let prefix = bcs::to_bytes(&stream_id).unwrap();
1940
1941 let index = 1567;
1942 let event_id = EventId {
1943 chain_id,
1944 stream_id,
1945 index,
1946 };
1947 let root_key = RootKey::Event(chain_id).bytes();
1948 assert_eq!(root_key[0], EVENT_ID_TAG);
1949 let key = to_event_key(&event_id);
1950 assert!(key.starts_with(&prefix));
1951 }
1952
1953 #[test]
1956 fn test_root_key_block_by_height_serialization() {
1957 use linera_base::data_types::BlockHeight;
1958
1959 let hash = CryptoHash::default();
1960 let chain_id = ChainId(hash);
1961 let height = BlockHeight(42);
1962
1963 let root_key = RootKey::BlockByHeight(chain_id).bytes();
1965 let deserialized_chain_id: ChainId = bcs::from_bytes(&root_key[1..]).unwrap();
1966 assert_eq!(deserialized_chain_id, chain_id);
1967
1968 let height_key = to_height_key(height);
1970 let deserialized_height: BlockHeight = bcs::from_bytes(&height_key).unwrap();
1971 assert_eq!(deserialized_height, height);
1972 }
1973
1974 #[cfg(with_testing)]
1975 #[tokio::test]
1976 async fn test_add_certificate_creates_height_index() {
1977 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
1979
1980 let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
1982 let height = BlockHeight(5);
1983 let block = populated_block(chain_id, height.0);
1984 let confirmed_block = ConfirmedBlock::new(block);
1985 let certificate = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
1986
1987 let mut batch = MultiPartitionBatch::new();
1989 batch.add_certificate(&certificate).unwrap();
1990 storage.write_batch(batch).await.unwrap();
1991
1992 let hash = certificate.hash();
1994 let index_root_key = RootKey::BlockByHeight(chain_id).bytes();
1995 let store = storage.database.open_shared(&index_root_key).unwrap();
1996 let height_key = to_height_key(height);
1997 let value_bytes = store.read_value_bytes(&height_key).await.unwrap();
1998
1999 assert!(value_bytes.is_some(), "Height index was not created");
2000 let stored_hash: CryptoHash = bcs::from_bytes(&value_bytes.unwrap()).unwrap();
2001 assert_eq!(stored_hash, hash, "Height index contains wrong hash");
2002 }
2003
2004 #[cfg(with_testing)]
2005 #[tokio::test]
2006 async fn test_read_certificates_by_heights() {
2007 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
2008 let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
2009
2010 let mut batch = MultiPartitionBatch::new();
2012 let mut expected_certs = vec![];
2013
2014 for height in [1, 3, 5] {
2015 let block = populated_block(chain_id, height);
2016 let confirmed_block = ConfirmedBlock::new(block);
2017 let cert = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
2018 expected_certs.push((height, cert.clone()));
2019 batch.add_certificate(&cert).unwrap();
2020 }
2021 storage.write_batch(batch).await.unwrap();
2022
2023 let heights = vec![BlockHeight(1), BlockHeight(3), BlockHeight(5)];
2025 let result = storage
2026 .read_certificates_by_heights(chain_id, &heights)
2027 .await
2028 .unwrap();
2029 assert_eq!(result.len(), 3);
2030 assert_eq!(
2031 result[0].as_ref().unwrap().hash(),
2032 expected_certs[0].1.hash()
2033 );
2034 assert_eq!(
2035 result[1].as_ref().unwrap().hash(),
2036 expected_certs[1].1.hash()
2037 );
2038 assert_eq!(
2039 result[2].as_ref().unwrap().hash(),
2040 expected_certs[2].1.hash()
2041 );
2042
2043 let heights = vec![BlockHeight(5), BlockHeight(1), BlockHeight(3)];
2045 let result = storage
2046 .read_certificates_by_heights(chain_id, &heights)
2047 .await
2048 .unwrap();
2049 assert_eq!(result.len(), 3);
2050 assert_eq!(
2051 result[0].as_ref().unwrap().hash(),
2052 expected_certs[2].1.hash()
2053 );
2054 assert_eq!(
2055 result[1].as_ref().unwrap().hash(),
2056 expected_certs[0].1.hash()
2057 );
2058 assert_eq!(
2059 result[2].as_ref().unwrap().hash(),
2060 expected_certs[1].1.hash()
2061 );
2062
2063 let heights = vec![
2065 BlockHeight(1),
2066 BlockHeight(2),
2067 BlockHeight(3),
2068 BlockHeight(3),
2069 ];
2070 let result = storage
2071 .read_certificates_by_heights(chain_id, &heights)
2072 .await
2073 .unwrap();
2074 assert_eq!(result.len(), 4); assert!(result[0].is_some());
2076 assert!(result[1].is_none()); assert!(result[2].is_some());
2078 assert!(result[3].is_some());
2079 assert_eq!(
2080 result[2].as_ref().unwrap().hash(),
2081 result[3].as_ref().unwrap().hash()
2082 ); let heights = vec![];
2086 let result = storage
2087 .read_certificates_by_heights(chain_id, &heights)
2088 .await
2089 .unwrap();
2090 assert_eq!(result.len(), 0);
2091 }
2092
2093 #[cfg(with_testing)]
2094 #[tokio::test]
2095 async fn test_read_certificates_by_heights_multiple_chains() {
2096 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
2097
2098 let chain_a = ChainId(CryptoHash::test_hash("chain_a"));
2100 let chain_b = ChainId(CryptoHash::test_hash("chain_b"));
2101
2102 let mut batch = MultiPartitionBatch::new();
2103
2104 let block_a = populated_block(chain_a, 10);
2105 let confirmed_block_a = ConfirmedBlock::new(block_a);
2106 let cert_a = ConfirmedBlockCertificate::new(confirmed_block_a, Round::Fast, vec![]);
2107 batch.add_certificate(&cert_a).unwrap();
2108
2109 let block_b = populated_block(chain_b, 10);
2110 let confirmed_block_b = ConfirmedBlock::new(block_b);
2111 let cert_b = ConfirmedBlockCertificate::new(confirmed_block_b, Round::Fast, vec![]);
2112 batch.add_certificate(&cert_b).unwrap();
2113
2114 storage.write_batch(batch).await.unwrap();
2115
2116 let result = storage
2118 .read_certificates_by_heights(chain_a, &[BlockHeight(10)])
2119 .await
2120 .unwrap();
2121 assert_eq!(result[0].as_ref().unwrap().hash(), cert_a.hash());
2122
2123 let result = storage
2125 .read_certificates_by_heights(chain_b, &[BlockHeight(10)])
2126 .await
2127 .unwrap();
2128 assert_eq!(result[0].as_ref().unwrap().hash(), cert_b.hash());
2129
2130 let result = storage
2132 .read_certificates_by_heights(chain_a, &[BlockHeight(20)])
2133 .await
2134 .unwrap();
2135 assert!(result[0].is_none());
2136 }
2137
2138 #[cfg(with_testing)]
2139 #[tokio::test]
2140 async fn test_read_certificates_by_heights_consistency() {
2141 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
2142 let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
2143
2144 let mut batch = MultiPartitionBatch::new();
2146 let block = populated_block(chain_id, 7);
2147 let confirmed_block = ConfirmedBlock::new(block);
2148 let cert = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
2149 let hash = cert.hash();
2150 batch.add_certificate(&cert).unwrap();
2151 storage.write_batch(batch).await.unwrap();
2152
2153 let cert_by_hash = storage.read_certificate(hash).await.unwrap().unwrap();
2155
2156 let certs_by_height = storage
2158 .read_certificates_by_heights(chain_id, &[BlockHeight(7)])
2159 .await
2160 .unwrap();
2161 let cert_by_height = certs_by_height[0].as_ref().unwrap();
2162
2163 assert_eq!(cert_by_hash.hash(), cert_by_height.hash());
2165 assert_eq!(
2166 cert_by_hash.value().block().header,
2167 cert_by_height.value().block().header
2168 );
2169 }
2170
2171 #[cfg(with_metrics)]
2175 #[test]
2176 fn source_labelled_counters_export_both_sources_before_any_read() {
2177 crate::init_metrics();
2178
2179 let families = prometheus::gather();
2180 for name in [
2181 "linera_contains_blob_state",
2182 "linera_contains_certificate",
2183 "linera_read_event_block_height",
2184 "linera_contains_blobs",
2185 "linera_read_blob_state",
2186 ] {
2187 let family = families
2188 .iter()
2189 .find(|family| family.get_name() == name)
2190 .unwrap_or_else(|| panic!("{name} must be exported once init_metrics has run"));
2191 let mut sources = family
2192 .get_metric()
2193 .iter()
2194 .flat_map(|metric| metric.get_label())
2195 .filter(|label| label.get_name() == super::metrics::SOURCE_LABEL)
2196 .map(|label| label.get_value())
2197 .collect::<Vec<_>>();
2198 sources.sort_unstable();
2199 assert_eq!(
2200 sources,
2201 [super::metrics::CACHE, super::metrics::DB],
2202 "{name} must export both sources"
2203 );
2204 }
2205 }
2206}