diff --git a/Cargo.lock b/Cargo.lock index 8baaceb19..6d00792e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -504,6 +504,7 @@ dependencies = [ "brk_types", "color-eyre", "derive_more", + "parking_lot", "rayon", "rustc-hash", "schemars", @@ -2544,7 +2545,9 @@ dependencies = [ [[package]] name = "rawdb" -version = "0.7.1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "912a9c6f76a5f141057139d510b969b082ff74f39a72a1c27178d8e1eeec95dc" dependencies = [ "libc", "log", @@ -3436,7 +3439,9 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23" [[package]] name = "vecdb" -version = "0.7.1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f89be182f86511ee28832cc04039d818564b88c63b14093fcf1871c732c0dd" dependencies = [ "itoa", "libc", @@ -3457,7 +3462,9 @@ dependencies = [ [[package]] name = "vecdb_derive" -version = "0.7.1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42915a5ca404d941e3e6d024f794403c481e5aeb96b1867d12d1f1e972d1433f" dependencies = [ "quote", "syn", diff --git a/Cargo.toml b/Cargo.toml index 7fa9a366f..e3926241b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,8 +87,8 @@ tower-http = { version = "0.6.8", features = ["catch-panic", "compression-br", " tower-layer = "0.3" tracing = { version = "0.1", default-features = false, features = ["std"] } ureq = { version = "3.3.0", features = ["json"] } -# vecdb = { version = "0.7.1", features = ["derive", "serde_json", "pco", "schemars"] } -vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] } +vecdb = { version = "0.7.2", features = ["derive", "serde_json", "pco", "schemars"] } +# vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] } [workspace.metadata.release] shared-version = true diff --git a/crates/brk_computer/Cargo.toml b/crates/brk_computer/Cargo.toml index f511ea3a6..7907d7913 100644 --- a/crates/brk_computer/Cargo.toml +++ b/crates/brk_computer/Cargo.toml @@ -22,6 +22,7 @@ brk_store = { workspace = true } brk_traversable = { workspace = true } brk_types = { workspace = true } derive_more = { workspace = true } +parking_lot = { workspace = true } tracing = { workspace = true } rayon = { workspace = true } rustc-hash = { workspace = true } diff --git a/crates/brk_computer/src/internal/cache_budget.rs b/crates/brk_computer/src/internal/cache_budget.rs new file mode 100644 index 000000000..5c4f62464 --- /dev/null +++ b/crates/brk_computer/src/internal/cache_budget.rs @@ -0,0 +1,88 @@ +use std::sync::{ + Arc, + atomic::{AtomicU64, AtomicUsize, Ordering::Relaxed}, +}; + +use parking_lot::Mutex; +use vecdb::{CachedVec, CachedVecBudget, ReadableBoxedVec, VecIndex, VecValue}; + +const MAX_CACHED: usize = 256; +const MIN_ACCESSES: u64 = 2; + +struct LruBudget { + remaining: AtomicUsize, +} + +impl LruBudget { + fn try_decrement(&self) -> bool { + self.remaining + .fetch_update(Relaxed, Relaxed, |n| if n > 0 { Some(n - 1) } else { None }) + .is_ok() + } +} + +impl CachedVecBudget for LruBudget { + fn try_reserve(&self, access_count: u64) -> bool { + if access_count < MIN_ACCESSES { + return false; + } + if self.try_decrement() { + return true; + } + // Only evict if we're more popular than the least popular cached entry. + if evict_less_popular_than(access_count) { + self.try_decrement() + } else { + false + } + } +} + +struct CacheEntry { + access_count: Arc, + clear: Box, +} + +static BUDGET: LruBudget = LruBudget { + remaining: AtomicUsize::new(MAX_CACHED), +}; +static CACHES: Mutex> = Mutex::new(Vec::new()); + +fn evict_less_popular_than(threshold: u64) -> bool { + let caches = CACHES.lock(); + if let Some((idx, _)) = caches + .iter() + .enumerate() + .filter(|(_, e)| { + let c = e.access_count.load(Relaxed); + c >= MIN_ACCESSES && c < threshold + }) + .min_by_key(|(_, e)| e.access_count.load(Relaxed)) + { + (caches[idx].clear)(); + BUDGET.remaining.fetch_add(1, Relaxed); + true + } else { + false + } +} + +/// Wraps a boxed source in a budgeted [`CachedVec`] and registers it for eviction. +pub fn cache_wrap(source: ReadableBoxedVec) -> CachedVec { + let access_count = Arc::new(AtomicU64::new(0)); + let cached = CachedVec::new_budgeted(source, &BUDGET, access_count.clone()); + let clone = cached.clone(); + CACHES.lock().push(CacheEntry { + access_count, + clear: Box::new(move || clone.clear()), + }); + cached +} + +/// Clears all cached vecs and resets the budget. +pub fn cache_clear_all() { + for entry in CACHES.lock().iter() { + (entry.clear)(); + } + BUDGET.remaining.store(MAX_CACHED, Relaxed); +} diff --git a/crates/brk_computer/src/internal/mod.rs b/crates/brk_computer/src/internal/mod.rs index e667d1af0..e4b3fea3b 100644 --- a/crates/brk_computer/src/internal/mod.rs +++ b/crates/brk_computer/src/internal/mod.rs @@ -1,5 +1,6 @@ pub(crate) mod algo; mod amount; +mod cache_budget; mod containers; pub(crate) mod db_utils; mod per_block; @@ -9,6 +10,7 @@ mod traits; mod transform; pub(crate) use amount::*; +pub(crate) use cache_budget::*; pub(crate) use containers::*; pub(crate) use per_block::*; pub(crate) use per_tx::*; diff --git a/crates/brk_computer/src/internal/per_block/computed/resolutions.rs b/crates/brk_computer/src/internal/per_block/computed/resolutions.rs index 2073da5c8..bc733f435 100644 --- a/crates/brk_computer/src/internal/per_block/computed/resolutions.rs +++ b/crates/brk_computer/src/internal/per_block/computed/resolutions.rs @@ -8,12 +8,13 @@ use brk_types::{ use derive_more::{Deref, DerefMut}; use schemars::JsonSchema; use vecdb::{ - AggFold, LazyAggVec, ReadOnlyClone, ReadableBoxedVec, ReadableVec, VecIndex, VecValue, + AggFold, LazyAggVec, ReadOnlyClone, ReadableBoxedVec, ReadableCloneableVec, ReadableVec, + VecIndex, VecValue, }; use crate::{ indexes, - internal::{ComputedVecValue, NumericValue, PerResolution}, + internal::{ComputedVecValue, NumericValue, PerResolution, cache_wrap}, }; /// Aggregation strategy for epoch-based indices (Halving, Epoch). @@ -107,6 +108,9 @@ where version: Version, indexes: &indexes::Vecs, ) -> Self { + let cached = cache_wrap(height_source); + let height_source = cached.read_only_boxed_clone(); + let cm = &indexes.cached_mappings; macro_rules! res { diff --git a/crates/brk_computer/src/lib.rs b/crates/brk_computer/src/lib.rs index 2e2d0be8d..5c86b8b69 100644 --- a/crates/brk_computer/src/lib.rs +++ b/crates/brk_computer/src/lib.rs @@ -299,6 +299,8 @@ impl Computer { reader: &Reader, exit: &Exit, ) -> Result<()> { + internal::cache_clear_all(); + let compute_start = Instant::now(); let mut starting_indexes = timed("Computed indexes", || { diff --git a/crates/brk_server/src/lib.rs b/crates/brk_server/src/lib.rs index 45ed659b8..0838f3e62 100644 --- a/crates/brk_server/src/lib.rs +++ b/crates/brk_server/src/lib.rs @@ -57,7 +57,7 @@ impl Server { query: query.clone(), data_path, website, - cache: Arc::new(Cache::new(5_000)), + cache: Arc::new(Cache::new(1_000)), started_at: jiff::Timestamp::now(), started_instant: Instant::now(), }) diff --git a/website/styles/chart.css b/website/styles/chart.css index 01a4e6a89..a4e999847 100644 --- a/website/styles/chart.css +++ b/website/styles/chart.css @@ -243,7 +243,7 @@ button.capture { position: absolute; - bottom: 0.325rem; + bottom: 0.375rem; right: -0.75rem; z-index: 50; font-size: var(--font-size-xs);