1use std::sync::{Arc, Mutex};
7
8use serde::{Deserialize, Serialize};
9
10#[cfg(with_testing)]
11use crate::memory::MemoryDatabase;
12#[cfg(with_testing)]
13use crate::store::TestKeyValueDatabase;
14use crate::{
15 batch::{Batch, WriteOperation},
16 lru_prefix_cache::{LruPrefixCache, StorageCacheConfig},
17 store::{KeyValueDatabase, ReadableKeyValueStore, WithError, WritableKeyValueStore},
18};
19
20#[cfg(with_metrics)]
21pub(crate) mod metrics {
22 use linera_base::prometheus_util::register_int_counter_vec;
23 use prometheus::IntCounterVec;
24
25 linera_base::declare_metrics! {
26 pub static READ_VALUE_CACHE_MISS_COUNT: IntCounterVec =
28 register_int_counter_vec(
29 "num_read_value_cache_miss",
30 "Number of read value cache misses",
31 &[],
32 );
33
34 pub static READ_VALUE_CACHE_HIT_COUNT: IntCounterVec =
36 register_int_counter_vec(
37 "num_read_value_cache_hits",
38 "Number of read value cache hits",
39 &[],
40 );
41
42 pub static CONTAINS_KEY_CACHE_MISS_COUNT: IntCounterVec =
44 register_int_counter_vec(
45 "num_contains_key_cache_miss",
46 "Number of contains key cache misses",
47 &[],
48 );
49
50 pub static CONTAINS_KEY_CACHE_HIT_COUNT: IntCounterVec =
52 register_int_counter_vec(
53 "num_contains_key_cache_hit",
54 "Number of contains key cache hits",
55 &[],
56 );
57
58 pub static FIND_KEYS_BY_PREFIX_CACHE_MISS_COUNT: IntCounterVec =
60 register_int_counter_vec(
61 "num_find_keys_by_prefix_cache_miss",
62 "Number of find keys by prefix cache misses",
63 &[],
64 );
65
66 pub static FIND_KEYS_BY_PREFIX_CACHE_HIT_COUNT: IntCounterVec =
68 register_int_counter_vec(
69 "num_find_keys_by_prefix_cache_hit",
70 "Number of find keys by prefix cache hits",
71 &[],
72 );
73
74 pub static FIND_KEY_VALUES_BY_PREFIX_CACHE_MISS_COUNT: IntCounterVec =
76 register_int_counter_vec(
77 "num_find_key_values_by_prefix_cache_miss",
78 "Number of find key values by prefix cache misses",
79 &[],
80 );
81
82 pub static FIND_KEY_VALUES_BY_PREFIX_CACHE_HIT_COUNT: IntCounterVec =
84 register_int_counter_vec(
85 "num_find_key_values_by_prefix_cache_hit",
86 "Number of find key values by prefix cache hits",
87 &[],
88 );
89 }
90}
91
92pub const DEFAULT_STORAGE_CACHE_CONFIG: StorageCacheConfig = StorageCacheConfig {
96 max_cache_size: 10000000,
97 max_value_entry_size: 1000000,
98 max_find_keys_entry_size: 1000000,
99 max_find_key_values_entry_size: 1000000,
100 max_cache_entries: 1000,
101 max_cache_value_size: 10000000,
102 max_cache_find_keys_size: 10000000,
103 max_cache_find_key_values_size: 10000000,
104};
105
106#[derive(Clone)]
108pub struct LruCachingDatabase<D> {
109 database: D,
111 config: StorageCacheConfig,
113}
114
115#[derive(Clone)]
117pub struct LruCachingStore<S> {
118 store: S,
120 cache: Option<Arc<Mutex<LruPrefixCache>>>,
122}
123
124impl<D> WithError for LruCachingDatabase<D>
125where
126 D: WithError,
127{
128 type Error = D::Error;
129}
130
131impl<S> WithError for LruCachingStore<S>
132where
133 S: WithError,
134{
135 type Error = S::Error;
136}
137
138impl<K> ReadableKeyValueStore for LruCachingStore<K>
139where
140 K: ReadableKeyValueStore,
141{
142 const MAX_KEY_SIZE: usize = K::MAX_KEY_SIZE;
144
145 fn root_key(&self) -> Result<Vec<u8>, Self::Error> {
146 self.store.root_key()
147 }
148
149 async fn read_value_bytes(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
150 let Some(cache) = &self.cache else {
151 return self.store.read_value_bytes(key).await;
152 };
153 {
155 let mut cache = cache.lock().unwrap();
156 if let Some(value) = cache.query_read_value(key) {
157 #[cfg(with_metrics)]
158 metrics::READ_VALUE_CACHE_HIT_COUNT
159 .with_label_values(&[])
160 .inc();
161 return Ok(value);
162 }
163 }
164 #[cfg(with_metrics)]
165 metrics::READ_VALUE_CACHE_MISS_COUNT
166 .with_label_values(&[])
167 .inc();
168 let value = self.store.read_value_bytes(key).await?;
169 let mut cache = cache.lock().unwrap();
170 cache.insert_read_value(key, &value);
171 Ok(value)
172 }
173
174 async fn contains_key(&self, key: &[u8]) -> Result<bool, Self::Error> {
175 let Some(cache) = &self.cache else {
176 return self.store.contains_key(key).await;
177 };
178 {
179 let mut cache = cache.lock().unwrap();
180 if let Some(value) = cache.query_contains_key(key) {
181 #[cfg(with_metrics)]
182 metrics::CONTAINS_KEY_CACHE_HIT_COUNT
183 .with_label_values(&[])
184 .inc();
185 return Ok(value);
186 }
187 }
188 #[cfg(with_metrics)]
189 metrics::CONTAINS_KEY_CACHE_MISS_COUNT
190 .with_label_values(&[])
191 .inc();
192 let result = self.store.contains_key(key).await?;
193 let mut cache = cache.lock().unwrap();
194 cache.insert_contains_key(key, result);
195 Ok(result)
196 }
197
198 async fn contains_keys(&self, keys: &[Vec<u8>]) -> Result<Vec<bool>, Self::Error> {
199 let Some(cache) = &self.cache else {
200 return self.store.contains_keys(keys).await;
201 };
202 let size = keys.len();
203 let mut results = vec![false; size];
204 let mut indices = Vec::new();
205 let mut key_requests = Vec::new();
206 {
207 let mut cache = cache.lock().unwrap();
208 for i in 0..size {
209 if let Some(value) = cache.query_contains_key(&keys[i]) {
210 #[cfg(with_metrics)]
211 metrics::CONTAINS_KEY_CACHE_HIT_COUNT
212 .with_label_values(&[])
213 .inc();
214 results[i] = value;
215 } else {
216 #[cfg(with_metrics)]
217 metrics::CONTAINS_KEY_CACHE_MISS_COUNT
218 .with_label_values(&[])
219 .inc();
220 indices.push(i);
221 key_requests.push(keys[i].clone());
222 }
223 }
224 }
225 if !key_requests.is_empty() {
226 let key_results = self.store.contains_keys(&key_requests).await?;
227 let mut cache = cache.lock().unwrap();
228 for ((index, result), key) in indices.into_iter().zip(key_results).zip(key_requests) {
229 results[index] = result;
230 cache.insert_contains_key(&key, result);
231 }
232 }
233 Ok(results)
234 }
235
236 async fn read_multi_values_bytes(
237 &self,
238 keys: &[Vec<u8>],
239 ) -> Result<Vec<Option<Vec<u8>>>, Self::Error> {
240 let Some(cache) = &self.cache else {
241 return self.store.read_multi_values_bytes(keys).await;
242 };
243
244 let mut result = Vec::with_capacity(keys.len());
245 let mut cache_miss_indices = Vec::new();
246 let mut miss_keys = Vec::new();
247 {
248 let mut cache = cache.lock().unwrap();
249 for (i, key) in keys.iter().enumerate() {
250 if let Some(value) = cache.query_read_value(key) {
251 #[cfg(with_metrics)]
252 metrics::READ_VALUE_CACHE_HIT_COUNT
253 .with_label_values(&[])
254 .inc();
255 result.push(value);
256 } else {
257 #[cfg(with_metrics)]
258 metrics::READ_VALUE_CACHE_MISS_COUNT
259 .with_label_values(&[])
260 .inc();
261 result.push(None);
262 cache_miss_indices.push(i);
263 miss_keys.push(key.clone());
264 }
265 }
266 }
267 if !miss_keys.is_empty() {
268 let values = self.store.read_multi_values_bytes(&miss_keys).await?;
269 let mut cache = cache.lock().unwrap();
270 for (i, (key, value)) in cache_miss_indices
271 .into_iter()
272 .zip(miss_keys.into_iter().zip(values))
273 {
274 cache.insert_read_value(&key, &value);
275 result[i] = value;
276 }
277 }
278 Ok(result)
279 }
280
281 async fn find_keys_by_prefix(&self, key_prefix: &[u8]) -> Result<Vec<Vec<u8>>, Self::Error> {
282 let Some(cache) = self.get_exclusive_cache() else {
283 return self.store.find_keys_by_prefix(key_prefix).await;
284 };
285 {
286 let mut cache = cache.lock().unwrap();
287 if let Some(value) = cache.query_find_keys(key_prefix) {
288 #[cfg(with_metrics)]
289 metrics::FIND_KEYS_BY_PREFIX_CACHE_HIT_COUNT
290 .with_label_values(&[])
291 .inc();
292 return Ok(value);
293 }
294 }
295 #[cfg(with_metrics)]
296 metrics::FIND_KEYS_BY_PREFIX_CACHE_MISS_COUNT
297 .with_label_values(&[])
298 .inc();
299 let keys = self.store.find_keys_by_prefix(key_prefix).await?;
300 let mut cache = cache.lock().unwrap();
301 cache.insert_find_keys(key_prefix.to_vec(), &keys);
302 Ok(keys)
303 }
304
305 async fn find_key_values_by_prefix(
306 &self,
307 key_prefix: &[u8],
308 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, Self::Error> {
309 let Some(cache) = self.get_exclusive_cache() else {
310 return self.store.find_key_values_by_prefix(key_prefix).await;
311 };
312 {
313 let mut cache = cache.lock().unwrap();
314 if let Some(value) = cache.query_find_key_values(key_prefix) {
315 #[cfg(with_metrics)]
316 metrics::FIND_KEY_VALUES_BY_PREFIX_CACHE_HIT_COUNT
317 .with_label_values(&[])
318 .inc();
319 return Ok(value);
320 }
321 }
322 #[cfg(with_metrics)]
323 metrics::FIND_KEY_VALUES_BY_PREFIX_CACHE_MISS_COUNT
324 .with_label_values(&[])
325 .inc();
326 let key_values = self.store.find_key_values_by_prefix(key_prefix).await?;
327 let mut cache = cache.lock().unwrap();
328 cache.insert_find_key_values(key_prefix.to_vec(), &key_values);
329 Ok(key_values)
330 }
331}
332
333impl<K> WritableKeyValueStore for LruCachingStore<K>
334where
335 K: WritableKeyValueStore,
336{
337 const MAX_VALUE_SIZE: usize = K::MAX_VALUE_SIZE;
339
340 async fn write_batch(&self, batch: Batch) -> Result<(), Self::Error> {
341 self.store.write_batch(batch.clone()).await?;
342 if let Some(cache) = &self.cache {
343 let mut cache = cache.lock().unwrap();
344 for operation in &batch.operations {
345 match operation {
346 WriteOperation::Put { key, value } => {
347 cache.put_key_value(key, value);
348 }
349 WriteOperation::Delete { key } => {
350 cache.delete_key(key);
351 }
352 WriteOperation::DeletePrefix { key_prefix } => {
353 cache.delete_prefix(key_prefix);
354 }
355 }
356 }
357 }
358 Ok(())
359 }
360
361 async fn clear_journal(&self) -> Result<(), Self::Error> {
362 self.store.clear_journal().await
363 }
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct LruCachingConfig<C> {
369 pub inner_config: C,
371 pub storage_cache_config: StorageCacheConfig,
373}
374
375impl<D> KeyValueDatabase for LruCachingDatabase<D>
376where
377 D: KeyValueDatabase,
378{
379 type Config = LruCachingConfig<D::Config>;
380
381 type Store = LruCachingStore<D::Store>;
382
383 fn get_name() -> String {
384 format!("lru caching {}", D::get_name())
385 }
386
387 async fn connect(config: &Self::Config, namespace: &str) -> Result<Self, Self::Error> {
388 let database = D::connect(&config.inner_config, namespace).await?;
389 Ok(LruCachingDatabase {
390 database,
391 config: config.storage_cache_config.clone(),
392 })
393 }
394
395 fn open_shared(&self, root_key: &[u8]) -> Result<Self::Store, Self::Error> {
396 let store = self.database.open_shared(root_key)?;
397 Ok(LruCachingStore { store, cache: None })
399 }
400
401 fn open_exclusive(&self, root_key: &[u8]) -> Result<Self::Store, Self::Error> {
402 let store = self.database.open_exclusive(root_key)?;
403 let store = LruCachingStore::new(
404 store,
405 self.config.clone(),
406 true,
407 );
408 Ok(store)
409 }
410
411 async fn list_all(config: &Self::Config) -> Result<Vec<String>, Self::Error> {
412 D::list_all(&config.inner_config).await
413 }
414
415 async fn list_root_keys(&self) -> Result<Vec<Vec<u8>>, Self::Error> {
416 self.database.list_root_keys().await
417 }
418
419 async fn delete_all(config: &Self::Config) -> Result<(), Self::Error> {
420 D::delete_all(&config.inner_config).await
421 }
422
423 async fn exists(config: &Self::Config, namespace: &str) -> Result<bool, Self::Error> {
424 D::exists(&config.inner_config, namespace).await
425 }
426
427 async fn create(config: &Self::Config, namespace: &str) -> Result<(), Self::Error> {
428 D::create(&config.inner_config, namespace).await
429 }
430
431 async fn delete(config: &Self::Config, namespace: &str) -> Result<(), Self::Error> {
432 D::delete(&config.inner_config, namespace).await
433 }
434}
435
436impl<S> LruCachingStore<S> {
437 fn new(store: S, config: StorageCacheConfig, has_exclusive_access: bool) -> Self {
439 let cache = {
440 if config.max_cache_entries == 0 {
441 None
442 } else {
443 Some(Arc::new(Mutex::new(LruPrefixCache::new(
444 config,
445 has_exclusive_access,
446 ))))
447 }
448 };
449 Self { store, cache }
450 }
451
452 fn get_exclusive_cache(&self) -> Option<&Arc<Mutex<LruPrefixCache>>> {
454 let Some(cache) = &self.cache else {
455 return None;
456 };
457 let has_exclusive_access = {
458 let cache = cache.lock().unwrap();
459 cache.has_exclusive_access()
460 };
461 if has_exclusive_access {
462 Some(cache)
463 } else {
464 None
465 }
466 }
467}
468
469#[cfg(with_testing)]
471pub type LruCachingMemoryDatabase = LruCachingDatabase<MemoryDatabase>;
472
473#[cfg(with_testing)]
474impl<D> TestKeyValueDatabase for LruCachingDatabase<D>
475where
476 D: TestKeyValueDatabase,
477{
478 async fn new_test_config() -> Result<LruCachingConfig<D::Config>, D::Error> {
479 let inner_config = D::new_test_config().await?;
480 let storage_cache_config = DEFAULT_STORAGE_CACHE_CONFIG;
481 Ok(LruCachingConfig {
482 inner_config,
483 storage_cache_config,
484 })
485 }
486}
487
488#[cfg(with_testing)]
489impl<D: crate::backends::DatabaseBackup> crate::backends::DatabaseBackup for LruCachingDatabase<D> {
490 fn backup_to(&self, dir: &std::path::Path) -> anyhow::Result<()> {
491 self.database.backup_to(dir)
492 }
493}