diff --git a/Cargo.lock b/Cargo.lock index b701d89056..60a7be1db6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1914,6 +1914,7 @@ dependencies = [ "bstr", "chrono", "chrono-english", + "clru", "config", "criterion", "digest", diff --git a/Cargo.toml b/Cargo.toml index 41957ec284..a7e065c24f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ chrono = { version = "0.4.38", default-features = false, features = [ "clock", ] } chrono-english = { version = "0.1.7" } +clru = "0.6.2" config = { version = "0.13.4", default-features = false, features = ["toml"] } criterion = "0.5.1" crossterm = { version = "0.27", default-features = false } diff --git a/lib/Cargo.toml b/lib/Cargo.toml index ed9a8edb64..e45e45f881 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -39,6 +39,7 @@ blake2 = { workspace = true } bstr = { workspace = true } chrono = { workspace = true } chrono-english = { workspace = true } +clru = { workspace = true } config = { workspace = true } digest = { workspace = true } either = { workspace = true } diff --git a/lib/src/store.rs b/lib/src/store.rs index 3952a985ac..e648f2f3eb 100644 --- a/lib/src/store.rs +++ b/lib/src/store.rs @@ -15,14 +15,14 @@ #![allow(missing_docs)] use std::any::Any; -use std::collections::HashMap; use std::fmt::Debug; use std::fmt::Formatter; use std::io::Read; use std::sync::Arc; -use std::sync::RwLock; +use std::sync::Mutex; use std::time::SystemTime; +use clru::CLruCache; use futures::stream::BoxStream; use pollster::FutureExt; @@ -49,13 +49,18 @@ use crate::signing::Signer; use crate::tree::Tree; use crate::tree_builder::TreeBuilder; +// There are more tree objects than commits, and trees are often shared across +// commits. +const COMMIT_CACHE_CAPACITY: usize = 100; +const TREE_CACHE_CAPACITY: usize = 1000; + /// Wraps the low-level backend and makes it return more convenient types. Also /// adds caching. pub struct Store { backend: Box, signer: Signer, - commit_cache: RwLock>>, - tree_cache: RwLock>>, + commit_cache: Mutex>>, + tree_cache: Mutex>>, } impl Debug for Store { @@ -71,8 +76,8 @@ impl Store { Arc::new(Store { backend, signer, - commit_cache: Default::default(), - tree_cache: Default::default(), + commit_cache: Mutex::new(CLruCache::new(COMMIT_CACHE_CAPACITY.try_into().unwrap())), + tree_cache: Mutex::new(CLruCache::new(TREE_CACHE_CAPACITY.try_into().unwrap())), }) } @@ -136,15 +141,15 @@ impl Store { async fn get_backend_commit(&self, id: &CommitId) -> BackendResult> { { - let read_locked_cached = self.commit_cache.read().unwrap(); - if let Some(data) = read_locked_cached.get(id).cloned() { + let mut locked_cache = self.commit_cache.lock().unwrap(); + if let Some(data) = locked_cache.get(id).cloned() { return Ok(data); } } let commit = self.backend.read_commit(id).await?; let data = Arc::new(commit); - let mut write_locked_cache = self.commit_cache.write().unwrap(); - write_locked_cache.insert(id.clone(), data.clone()); + let mut locked_cache = self.commit_cache.lock().unwrap(); + locked_cache.put(id.clone(), data.clone()); Ok(data) } @@ -158,8 +163,8 @@ impl Store { let (commit_id, commit) = self.backend.write_commit(commit, sign_with)?; let data = Arc::new(commit); { - let mut write_locked_cache = self.commit_cache.write().unwrap(); - write_locked_cache.insert(commit_id.clone(), data.clone()); + let mut locked_cache = self.commit_cache.lock().unwrap(); + locked_cache.put(commit_id.clone(), data.clone()); } Ok(Commit::new(self.clone(), commit_id, data)) @@ -185,15 +190,15 @@ impl Store { ) -> BackendResult> { let key = (dir.to_owned(), id.clone()); { - let read_locked_cache = self.tree_cache.read().unwrap(); - if let Some(data) = read_locked_cache.get(&key).cloned() { + let mut locked_cache = self.tree_cache.lock().unwrap(); + if let Some(data) = locked_cache.get(&key).cloned() { return Ok(data); } } let data = self.backend.read_tree(dir, id).await?; let data = Arc::new(data); - let mut write_locked_cache = self.tree_cache.write().unwrap(); - write_locked_cache.insert(key, data.clone()); + let mut locked_cache = self.tree_cache.lock().unwrap(); + locked_cache.put(key, data.clone()); Ok(data) } @@ -218,8 +223,8 @@ impl Store { let tree_id = self.backend.write_tree(path, &tree)?; let data = Arc::new(tree); { - let mut write_locked_cache = self.tree_cache.write().unwrap(); - write_locked_cache.insert((path.to_owned(), tree_id.clone()), data.clone()); + let mut locked_cache = self.tree_cache.lock().unwrap(); + locked_cache.put((path.to_owned(), tree_id.clone()), data.clone()); } Ok(Tree::new(self.clone(), path.to_owned(), tree_id, data))