Skip to content

Commit 4018194

Browse files
committed
Cache deserialized immutable data on storage
1 parent 80865e3 commit 4018194

17 files changed

Lines changed: 309 additions & 74 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

linera-base/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ hex.workspace = true
4949
k256.workspace = true
5050
linera-kywasmtime = { workspace = true, optional = true }
5151
linera-witty = { workspace = true, features = ["macros"] }
52+
lru.workspace = true
5253
prometheus = { workspace = true, optional = true }
54+
quick_cache.workspace = true
5355
proptest = { workspace = true, optional = true, features = ["alloc"] }
5456
rand.workspace = true
5557
reqwest = { workspace = true, optional = true }

linera-base/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub mod time;
3939
#[cfg(test)]
4040
mod unit_tests;
4141
pub mod util;
42+
pub mod value_cache;
4243
pub mod vm;
4344

4445
pub use graphql::BcsHexParseError;
Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,24 @@
33

44
//! Concurrent caches for values.
55
6-
#[cfg(test)]
7-
#[path = "unit_tests/value_cache_tests.rs"]
8-
mod unit_tests;
9-
106
#[cfg(with_metrics)]
117
use std::any::type_name;
128
use std::{borrow::Cow, hash::Hash, num::NonZeroUsize, sync::Mutex};
139

14-
use linera_base::{crypto::CryptoHash, hashed::Hashed};
1510
use lru::LruCache;
1611
use quick_cache::sync::Cache;
1712

13+
use crate::{crypto::CryptoHash, hashed::Hashed};
14+
1815
/// A counter metric for the number of cache hits in the [`ValueCache`].
1916
#[cfg(with_metrics)]
2017
mod metrics {
2118
use std::sync::LazyLock;
2219

23-
use linera_base::prometheus_util::register_int_counter_vec;
2420
use prometheus::IntCounterVec;
2521

22+
use crate::prometheus_util::register_int_counter_vec;
23+
2624
pub static CACHE_HIT_COUNT: LazyLock<IntCounterVec> = LazyLock::new(|| {
2725
register_int_counter_vec(
2826
"value_cache_hit",
@@ -65,11 +63,27 @@ where
6563
}
6664
}
6765

66+
/// Inserts a value into the cache with the given key.
67+
/// Returns `true` if the value was newly inserted, `false` if it already existed.
68+
pub fn insert(&self, key: K, value: V) -> bool {
69+
if self.contains_key(&key) {
70+
false
71+
} else {
72+
self.cache.insert(key, value);
73+
true
74+
}
75+
}
76+
6877
/// Returns a `V` from the cache, if present.
6978
pub fn get(&self, key: &K) -> Option<V> {
7079
Self::track_cache_usage(self.cache.get(key))
7180
}
7281

82+
/// Returns `true` if the cache contains the given key.
83+
pub fn contains_key(&self, key: &K) -> bool {
84+
self.cache.get(key).is_some()
85+
}
86+
7387
fn track_cache_usage(maybe_value: Option<V>) -> Option<V> {
7488
#[cfg(with_metrics)]
7589
{
@@ -94,7 +108,7 @@ impl<T: Clone> ValueCache<CryptoHash, Hashed<T>> {
94108
/// inserted in the cache.
95109
///
96110
/// Returns [`true`] if the value was not already present in the cache.
97-
pub fn insert(&self, value: Cow<Hashed<T>>) -> bool {
111+
pub fn insert_hashed(&self, value: Cow<Hashed<T>>) -> bool {
98112
let hash = (*value).hash();
99113
if self.cache.get(&hash).is_some() {
100114
false
@@ -108,7 +122,7 @@ impl<T: Clone> ValueCache<CryptoHash, Hashed<T>> {
108122
///
109123
/// The `values` are wrapped in [`Cow`]s so that each `value` is only cloned if it
110124
/// needs to be inserted in the cache.
111-
#[cfg(test)]
125+
#[cfg(with_testing)]
112126
pub fn insert_all<'a>(&self, values: impl IntoIterator<Item = Cow<'a, Hashed<T>>>)
113127
where
114128
T: 'a,
@@ -122,7 +136,7 @@ impl<T: Clone> ValueCache<CryptoHash, Hashed<T>> {
122136
}
123137
}
124138

125-
#[cfg(test)]
139+
#[cfg(with_testing)]
126140
impl<K, V> ValueCache<K, V>
127141
where
128142
K: Hash + Eq + Clone,
@@ -137,6 +151,11 @@ where
137151
pub fn len(&self) -> usize {
138152
self.cache.len()
139153
}
154+
155+
/// Returns [`true`] if the cache is empty.
156+
pub fn is_empty(&self) -> bool {
157+
self.cache.len() == 0
158+
}
140159
}
141160

142161
/// A cache for values that need to be "parked" temporarily and taken out for exclusive use.

linera-core/src/chain_worker/actor.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@ use linera_views::context::InactiveContext;
3232
use tokio::sync::{mpsc, oneshot, OwnedRwLockReadGuard};
3333
use tracing::{debug, instrument, trace, Instrument as _};
3434

35+
use linera_base::value_cache::{ParkingCache, ValueCache};
36+
3537
use super::{config::ChainWorkerConfig, state::ChainWorkerState, DeliveryNotifier};
3638
use crate::{
3739
chain_worker::BlockOutcome,
3840
data_types::{ChainInfoQuery, ChainInfoResponse},
39-
value_cache::{ParkingCache, ValueCache},
4041
worker::{NetworkActions, WorkerError},
4142
};
4243

linera-core/src/chain_worker/state.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ use linera_views::{
4343
use tokio::sync::{oneshot, OwnedRwLockReadGuard, RwLock, RwLockWriteGuard};
4444
use tracing::{debug, instrument, trace, warn};
4545

46+
use linera_base::value_cache::{ParkingCache, ValueCache};
47+
4648
use super::{ChainWorkerConfig, ChainWorkerRequest, DeliveryNotifier, EventSubscriptionsResult};
4749
use crate::{
4850
data_types::{ChainInfo, ChainInfoQuery, ChainInfoResponse, CrossChainRequest},
49-
value_cache::{ParkingCache, ValueCache},
5051
worker::{NetworkActions, Notification, Reason, WorkerError},
5152
};
5253

@@ -589,7 +590,7 @@ where
589590
for block in blocks {
590591
let hashed_block = block.into_inner();
591592
let height = hashed_block.inner().header.height;
592-
self.block_values.insert(Cow::Owned(hashed_block.clone()));
593+
self.block_values.insert_hashed(Cow::Owned(hashed_block.clone()));
593594
height_to_blocks.insert(height, hashed_block);
594595
}
595596
}
@@ -782,7 +783,7 @@ where
782783
}
783784

784785
self.block_values
785-
.insert(Cow::Borrowed(certificate.inner().inner()));
786+
.insert_hashed(Cow::Borrowed(certificate.inner().inner()));
786787
let required_blob_ids = block.required_blob_ids();
787788
let maybe_blobs = self
788789
.maybe_get_required_blobs(required_blob_ids, Some(&block.created_blobs()))
@@ -1027,7 +1028,7 @@ where
10271028
self.save().await?;
10281029

10291030
self.block_values
1030-
.insert(Cow::Owned(certificate.into_inner().into_inner()));
1031+
.insert_hashed(Cow::Owned(certificate.into_inner().into_inner()));
10311032

10321033
self.register_delivery_notifier(height, &actions, notify_when_messages_are_delivered)
10331034
.await;
@@ -1670,10 +1671,12 @@ where
16701671
match manager.create_vote(proposal, block, key_pair, local_time, blobs)? {
16711672
// Cache the value we voted on, so the client doesn't have to send it again.
16721673
Some(Either::Left(vote)) => {
1673-
self.block_values.insert(Cow::Borrowed(vote.value.inner()));
1674+
self.block_values
1675+
.insert_hashed(Cow::Borrowed(vote.value.inner()));
16741676
}
16751677
Some(Either::Right(vote)) => {
1676-
self.block_values.insert(Cow::Borrowed(vote.value.inner()));
1678+
self.block_values
1679+
.insert_hashed(Cow::Borrowed(vote.value.inner()));
16771680
}
16781681
None => (),
16791682
}

linera-core/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ pub mod test_utils;
2323
pub mod worker;
2424

2525
pub(crate) mod updater;
26-
mod value_cache;
26+
27+
#[cfg(test)]
28+
#[path = "unit_tests/value_cache_tests.rs"]
29+
mod value_cache_tests;
2730

2831
pub use local_node::LocalNodeError;
2932
pub use updater::DEFAULT_QUORUM_GRACE_PERIOD;

linera-core/src/unit_tests/value_cache_tests.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ use linera_base::{
88
data_types::{BlockHeight, Epoch},
99
hashed::Hashed,
1010
identifiers::ChainId,
11+
value_cache::ValueCache,
1112
};
1213
use linera_chain::types::Timeout;
1314

14-
use super::ValueCache;
15-
1615
/// Test cache size for unit tests.
1716
const TEST_CACHE_SIZE: usize = 10;
1817

@@ -33,7 +32,7 @@ fn test_insert_single_certificate_value() {
3332
let value = create_dummy_certificate_value(0);
3433
let hash = value.hash();
3534

36-
assert!(cache.insert(Cow::Borrowed(&value)));
35+
assert!(cache.insert_hashed(Cow::Borrowed(&value)));
3736
assert!(cache.contains(&hash));
3837
assert_eq!(cache.get(&hash), Some(value));
3938
assert_eq!(cache.len(), 1);
@@ -46,7 +45,7 @@ fn test_insert_many_certificate_values_individually() {
4645
let values = create_dummy_certificate_values(0..(TEST_CACHE_SIZE as u64)).collect::<Vec<_>>();
4746

4847
for value in &values {
49-
assert!(cache.insert(Cow::Borrowed(value)));
48+
assert!(cache.insert_hashed(Cow::Borrowed(value)));
5049
}
5150

5251
for value in &values {
@@ -82,7 +81,7 @@ fn test_reinsertion_of_values() {
8281
cache.insert_all(values.iter().map(Cow::Borrowed));
8382

8483
for value in &values {
85-
assert!(!cache.insert(Cow::Borrowed(value)));
84+
assert!(!cache.insert_hashed(Cow::Borrowed(value)));
8685
}
8786

8887
for value in &values {
@@ -105,7 +104,7 @@ fn test_eviction_occurs() {
105104
create_dummy_certificate_values(0..((TEST_CACHE_SIZE as u64) * 2)).collect::<Vec<_>>();
106105

107106
for value in &values {
108-
cache.insert(Cow::Borrowed(value));
107+
cache.insert_hashed(Cow::Borrowed(value));
109108
}
110109

111110
// Cache size should be bounded by capacity
@@ -175,7 +174,7 @@ fn test_access_affects_eviction() {
175174
.collect::<Vec<_>>();
176175

177176
for value in &extra_values {
178-
cache.insert(Cow::Borrowed(value));
177+
cache.insert_hashed(Cow::Borrowed(value));
179178
}
180179

181180
// The frequently accessed first value should still be present
@@ -195,15 +194,15 @@ fn test_promotion_of_reinsertion() {
195194
cache.insert_all(values.iter().map(Cow::Borrowed));
196195

197196
// Re-insert the first value (this should "promote" it)
198-
assert!(!cache.insert(Cow::Borrowed(&values[0])));
197+
assert!(!cache.insert_hashed(Cow::Borrowed(&values[0])));
199198

200199
// Insert additional values to trigger eviction
201200
let extra_values =
202201
create_dummy_certificate_values((TEST_CACHE_SIZE as u64)..((TEST_CACHE_SIZE as u64) + 3))
203202
.collect::<Vec<_>>();
204203

205204
for value in &extra_values {
206-
cache.insert(Cow::Borrowed(value));
205+
cache.insert_hashed(Cow::Borrowed(value));
207206
}
208207

209208
// The re-inserted first value should still be present

linera-core/src/worker.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ use crate::{
4747
data_types::{ChainInfoQuery, ChainInfoResponse, CrossChainRequest},
4848
join_set_ext::{JoinSet, JoinSetExt},
4949
notifier::Notifier,
50-
value_cache::{ParkingCache, ValueCache},
5150
CHAIN_INFO_MAX_RECEIVED_LOG_ENTRIES,
5251
};
52+
use linera_base::value_cache::{ParkingCache, ValueCache};
5353

5454
#[cfg(test)]
5555
#[path = "unit_tests/worker_tests.rs"]

linera-service/src/cli/main.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ use linera_service::{
8181
task_processor::TaskProcessor,
8282
util,
8383
};
84-
use linera_storage::{DbStorage, Storage};
84+
use linera_storage::{DbStorage, Storage, StorageCacheConfig};
8585
use linera_views::store::{KeyValueDatabase, KeyValueStore};
8686
use options::Options;
8787
use serde_json::Value;
@@ -1776,7 +1776,7 @@ impl RunnableWithStore for DatabaseToolJob<'_> {
17761776
} => {
17771777
let genesis_config: GenesisConfig = util::read_json(genesis_config_path)?;
17781778
let mut storage =
1779-
DbStorage::<D, _>::maybe_create_and_connect(&config, &namespace, None).await?;
1779+
DbStorage::<D, _>::maybe_create_and_connect(&config, &namespace, None, StorageCacheConfig::default()).await?;
17801780
genesis_config.initialize_storage(&mut storage).await?;
17811781
info!(
17821782
"Namespace {namespace} was initialized in {} ms",
@@ -1796,7 +1796,7 @@ impl RunnableWithStore for DatabaseToolJob<'_> {
17961796
}
17971797
DatabaseToolCommand::ListBlobIds => {
17981798
let storage =
1799-
DbStorage::<D, _>::maybe_create_and_connect(&config, &namespace, None).await?;
1799+
DbStorage::<D, _>::maybe_create_and_connect(&config, &namespace, None, StorageCacheConfig::default()).await?;
18001800
let blob_ids = storage.list_blob_ids().await?;
18011801
info!("Blob IDs listed in {} ms", start_time.elapsed().as_millis());
18021802
info!("The list of blob IDs is:");
@@ -1806,7 +1806,7 @@ impl RunnableWithStore for DatabaseToolJob<'_> {
18061806
}
18071807
DatabaseToolCommand::ListChainIds => {
18081808
let storage =
1809-
DbStorage::<D, _>::maybe_create_and_connect(&config, &namespace, None).await?;
1809+
DbStorage::<D, _>::maybe_create_and_connect(&config, &namespace, None, StorageCacheConfig::default()).await?;
18101810
let chain_ids = storage.list_chain_ids().await?;
18111811
info!(
18121812
"Chain IDs listed in {} ms",
@@ -1819,7 +1819,7 @@ impl RunnableWithStore for DatabaseToolJob<'_> {
18191819
}
18201820
DatabaseToolCommand::ListEventIds => {
18211821
let storage =
1822-
DbStorage::<D, _>::maybe_create_and_connect(&config, &namespace, None).await?;
1822+
DbStorage::<D, _>::maybe_create_and_connect(&config, &namespace, None, StorageCacheConfig::default()).await?;
18231823
let event_ids = storage.list_event_ids().await?;
18241824
info!(
18251825
"Event IDs listed in {} ms",

0 commit comments

Comments
 (0)