use lru::LruCache;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, LazyLock, Mutex};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PartitionLoc {
pub data_offset: u64,
pub data_size: u32,
}
impl PartitionLoc {
#[inline]
pub fn new(data_offset: u64, data_size: u32) -> Self {
Self {
data_offset,
data_size,
}
}
#[inline]
pub fn offset_only(data_offset: u64) -> Self {
Self {
data_offset,
data_size: 0,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct GenerationIdentity {
pub device: u64,
pub inode: u64,
pub size: u64,
pub generation: u64,
pub mtime_ns: i128,
}
impl GenerationIdentity {
pub fn resolve(path: &std::path::Path, generation: u64) -> Option<Self> {
let (device, inode, size, mtime_ns) = stat_identity(path)?;
Some(Self {
device,
inode,
size,
generation,
mtime_ns,
})
}
}
#[cfg(unix)]
fn stat_identity(path: &std::path::Path) -> Option<(u64, u64, u64, i128)> {
use std::os::unix::fs::MetadataExt;
let md = std::fs::metadata(path).ok()?;
let mtime_ns = md.mtime() as i128 * 1_000_000_000 + md.mtime_nsec() as i128;
Some((md.dev(), md.ino(), md.len(), mtime_ns))
}
#[cfg(not(unix))]
fn stat_identity(path: &std::path::Path) -> Option<(u64, u64, u64, i128)> {
let md = std::fs::metadata(path).ok()?;
let mtime_ns = md
.modified()
.ok()
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
.map(|d| d.as_nanos() as i128)
.unwrap_or(0);
Some((0, 0, md.len(), mtime_ns))
}
const PER_ENTRY_OVERHEAD: usize = 96;
#[inline]
const fn entry_cost(key_len: usize) -> usize {
key_len.saturating_add(PER_ENTRY_OVERHEAD)
}
pub const DEFAULT_GLOBAL_KEY_CACHE_BYTES: usize = 64 * 1024 * 1024;
pub const DEFAULT_GLOBAL_KEY_CACHE_SHARDS: usize = 128;
#[derive(Clone, Copy, Debug)]
struct Entry {
loc: PartitionLoc,
seq: u64,
}
struct Shard {
map: HashMap<GenerationIdentity, LruCache<Box<[u8]>, Entry>>,
current_bytes: usize,
seq: u64,
}
impl Shard {
fn new() -> Self {
Self {
map: HashMap::new(),
current_bytes: 0,
seq: 0,
}
}
#[inline]
fn next_seq(&mut self) -> u64 {
self.seq = self.seq.wrapping_add(1);
self.seq
}
fn total_len(&self) -> usize {
self.map.values().map(LruCache::len).sum()
}
fn get(&mut self, identity: &GenerationIdentity, key: &[u8]) -> Option<PartitionLoc> {
let seq = self.next_seq();
let inner = self.map.get_mut(identity)?;
let entry = inner.get_mut(key)?;
entry.seq = seq;
Some(entry.loc)
}
fn evict_one(&mut self) -> bool {
let mut victim: Option<GenerationIdentity> = None;
let mut min_seq = u64::MAX;
for (id, inner) in self.map.iter() {
if let Some((_, entry)) = inner.peek_lru() {
if victim.is_none() || entry.seq < min_seq {
min_seq = entry.seq;
victim = Some(*id);
}
}
}
let Some(id) = victim else {
return false;
};
let Some(inner) = self.map.get_mut(&id) else {
return false;
};
match inner.pop_lru() {
Some((k, _)) => {
self.current_bytes = self.current_bytes.saturating_sub(entry_cost(k.len()));
if inner.is_empty() {
self.map.remove(&id);
}
true
}
None => false,
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct GlobalKeyCacheSnapshot {
pub hits: u64,
pub misses: u64,
pub evictions: u64,
pub invalidations: u64,
pub resident_bytes: usize,
pub capacity_bytes: usize,
}
pub struct GlobalKeyOffsetCache {
shards: Box<[Mutex<Shard>]>,
per_shard_bytes: usize,
mask: usize,
disabled: bool,
hits: AtomicU64,
misses: AtomicU64,
evictions: AtomicU64,
invalidations: AtomicU64,
}
impl std::fmt::Debug for GlobalKeyOffsetCache {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GlobalKeyOffsetCache")
.field("shards", &self.shards.len())
.field("per_shard_bytes", &self.per_shard_bytes)
.field("disabled", &self.disabled)
.field("len", &self.len())
.field("resident_bytes", &self.resident_bytes())
.field("hits", &self.hits.load(Ordering::Relaxed))
.field("misses", &self.misses.load(Ordering::Relaxed))
.field("evictions", &self.evictions.load(Ordering::Relaxed))
.field("invalidations", &self.invalidations.load(Ordering::Relaxed))
.finish()
}
}
static GLOBAL: LazyLock<Arc<GlobalKeyOffsetCache>> = LazyLock::new(|| {
Arc::new(GlobalKeyOffsetCache::with_budget_bytes(
DEFAULT_GLOBAL_KEY_CACHE_BYTES,
))
});
impl GlobalKeyOffsetCache {
pub fn global() -> Arc<GlobalKeyOffsetCache> {
Arc::clone(&GLOBAL)
}
pub fn with_budget_bytes(total_budget_bytes: usize) -> Self {
Self::with_budget_and_shards(total_budget_bytes, DEFAULT_GLOBAL_KEY_CACHE_SHARDS)
}
pub fn with_budget_and_shards(total_budget_bytes: usize, shard_count: usize) -> Self {
let shard_count = shard_count.max(1).next_power_of_two();
let per_shard_bytes = (total_budget_bytes / shard_count).max(entry_cost(0));
let mut shards = Vec::with_capacity(shard_count);
for _ in 0..shard_count {
shards.push(Mutex::new(Shard::new()));
}
Self {
shards: shards.into_boxed_slice(),
per_shard_bytes,
mask: shard_count - 1,
disabled: false,
hits: AtomicU64::new(0),
misses: AtomicU64::new(0),
evictions: AtomicU64::new(0),
invalidations: AtomicU64::new(0),
}
}
pub fn disabled() -> Self {
Self {
shards: vec![Mutex::new(Shard::new())].into_boxed_slice(),
per_shard_bytes: 0,
mask: 0,
disabled: true,
hits: AtomicU64::new(0),
misses: AtomicU64::new(0),
evictions: AtomicU64::new(0),
invalidations: AtomicU64::new(0),
}
}
#[inline]
fn lock(m: &Mutex<Shard>) -> std::sync::MutexGuard<'_, Shard> {
m.lock().unwrap_or_else(|e| e.into_inner())
}
#[inline]
fn shard_for(&self, identity: &GenerationIdentity, key: &[u8]) -> &Mutex<Shard> {
let mut h = std::collections::hash_map::DefaultHasher::new();
identity.hash(&mut h);
key.hash(&mut h);
let idx = (h.finish() as usize) & self.mask;
&self.shards[idx]
}
pub fn get(&self, identity: GenerationIdentity, key: &[u8]) -> Option<PartitionLoc> {
if self.disabled {
return None;
}
let mut guard = Self::lock(self.shard_for(&identity, key));
let found = guard.get(&identity, key);
drop(guard);
match found {
Some(loc) => {
self.hits.fetch_add(1, Ordering::Relaxed);
Some(loc)
}
None => {
self.misses.fetch_add(1, Ordering::Relaxed);
None
}
}
}
pub fn insert(&self, identity: GenerationIdentity, key: &[u8], loc: PartitionLoc) {
if self.disabled {
return;
}
let cost = entry_cost(key.len());
let mut guard = Self::lock(self.shard_for(&identity, key));
let seq = guard.next_seq();
let inner = guard
.map
.entry(identity)
.or_insert_with(LruCache::unbounded);
let replaced = inner.put(key.into(), Entry { loc, seq }).is_some();
if replaced {
guard.current_bytes = guard.current_bytes.saturating_sub(cost);
}
guard.current_bytes = guard.current_bytes.saturating_add(cost);
let mut evicted_here: u64 = 0;
while guard.current_bytes > self.per_shard_bytes && guard.total_len() > 1 {
if !guard.evict_one() {
break;
}
evicted_here += 1;
}
drop(guard);
if evicted_here > 0 {
self.evictions.fetch_add(evicted_here, Ordering::Relaxed);
}
}
pub fn invalidate(&self, identity: GenerationIdentity) -> u64 {
if self.disabled {
return 0;
}
let mut dropped: u64 = 0;
for shard in self.shards.iter() {
let mut guard = Self::lock(shard);
if let Some(inner) = guard.map.remove(&identity) {
let mut reclaimed = 0usize;
let mut n: u64 = 0;
for (k, _) in inner.iter() {
reclaimed = reclaimed.saturating_add(entry_cost(k.len()));
n += 1;
}
guard.current_bytes = guard.current_bytes.saturating_sub(reclaimed);
dropped += n;
}
}
if dropped > 0 {
self.invalidations.fetch_add(dropped, Ordering::Relaxed);
}
dropped
}
pub fn invalidate_all(&self) -> u64 {
if self.disabled {
return 0;
}
let mut dropped: u64 = 0;
for shard in self.shards.iter() {
let mut guard = Self::lock(shard);
dropped = dropped.saturating_add(guard.total_len() as u64);
guard.map.clear();
guard.current_bytes = 0;
}
if dropped > 0 {
self.invalidations.fetch_add(dropped, Ordering::Relaxed);
}
dropped
}
pub fn len(&self) -> usize {
self.shards.iter().map(|m| Self::lock(m).total_len()).sum()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn resident_bytes(&self) -> usize {
self.shards
.iter()
.map(|m| Self::lock(m).current_bytes)
.sum()
}
pub fn budget_bytes(&self) -> usize {
self.per_shard_bytes.saturating_mul(self.shards.len())
}
pub fn hit_count(&self) -> u64 {
self.hits.load(Ordering::Relaxed)
}
pub fn miss_count(&self) -> u64 {
self.misses.load(Ordering::Relaxed)
}
pub fn eviction_count(&self) -> u64 {
self.evictions.load(Ordering::Relaxed)
}
pub fn invalidation_count(&self) -> u64 {
self.invalidations.load(Ordering::Relaxed)
}
pub(crate) fn snapshot(&self) -> GlobalKeyCacheSnapshot {
GlobalKeyCacheSnapshot {
hits: self.hit_count(),
misses: self.miss_count(),
evictions: self.eviction_count(),
invalidations: self.invalidation_count(),
resident_bytes: self.resident_bytes(),
capacity_bytes: self.budget_bytes(),
}
}
}
impl Default for GlobalKeyOffsetCache {
fn default() -> Self {
Self::with_budget_bytes(DEFAULT_GLOBAL_KEY_CACHE_BYTES)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn ident(dev: u64, ino: u64, size: u64, gen: u64) -> GenerationIdentity {
GenerationIdentity {
device: dev,
inode: ino,
size,
generation: gen,
mtime_ns: 0,
}
}
fn budget_for(n: usize, key_len: usize) -> usize {
entry_cost(key_len) * n
}
#[test]
fn eviction_order_lru_single_shard() {
let cache = GlobalKeyOffsetCache::with_budget_and_shards(budget_for(2, 5), 1);
let g = ident(1, 1, 100, 1);
cache.insert(g, b"key-A", PartitionLoc::new(10, 100));
cache.insert(g, b"key-B", PartitionLoc::new(20, 200));
assert_eq!(cache.get(g, b"key-A"), Some(PartitionLoc::new(10, 100)));
cache.insert(g, b"key-C", PartitionLoc::new(30, 300));
assert_eq!(cache.get(g, b"key-A"), Some(PartitionLoc::new(10, 100)));
assert_eq!(cache.get(g, b"key-C"), Some(PartitionLoc::new(30, 300)));
assert_eq!(cache.get(g, b"key-B"), None, "LRU entry must be evicted");
assert!(cache.resident_bytes() <= cache.budget_bytes());
}
#[test]
fn reinsert_same_key_does_not_grow_resident_bytes() {
let cache = GlobalKeyOffsetCache::with_budget_and_shards(DEFAULT_GLOBAL_KEY_CACHE_BYTES, 1);
let g = ident(1, 1, 100, 1);
let key = b"hot-partition-key";
cache.insert(g, key, PartitionLoc::new(10, 100));
let bytes_after_first = cache.resident_bytes();
assert_eq!(cache.len(), 1, "one entry after the first insert");
cache.insert(g, key, PartitionLoc::new(999, 42));
assert_eq!(
cache.len(),
1,
"reinsert updates in place — no duplicate entry"
);
assert_eq!(
cache.resident_bytes(),
bytes_after_first,
"reinserting the same key must not double-count resident bytes"
);
assert_eq!(cache.get(g, key), Some(PartitionLoc::new(999, 42)));
}
#[test]
fn aggregate_bounded_across_many_generations() {
let key_len = 6;
let cache = GlobalKeyOffsetCache::with_budget_and_shards(budget_for(4, key_len), 1);
for gen in 0..200u64 {
let g = ident(1, gen, 100, gen);
let key = format!("key-{:02}", gen % 100);
assert_eq!(key.len(), key_len);
cache.insert(g, key.as_bytes(), PartitionLoc::new(gen, gen as u32));
assert!(
cache.resident_bytes() <= cache.budget_bytes(),
"resident {} exceeded budget {} after generation {}",
cache.resident_bytes(),
cache.budget_bytes(),
gen
);
}
assert!(cache.len() <= 4);
}
#[test]
fn large_keys_evict_sooner_than_small_keys() {
let budget = budget_for(4, 4);
let cache = GlobalKeyOffsetCache::with_budget_and_shards(budget, 1);
let g = ident(1, 1, 100, 1);
let big = vec![b'x'; budget / 2];
let mut big1 = big.clone();
big1[0] = b'a';
let mut big2 = big.clone();
big2[0] = b'b';
cache.insert(g, &big1, PartitionLoc::new(1, 1));
cache.insert(g, &big2, PartitionLoc::new(2, 2));
assert!(cache.resident_bytes() <= cache.budget_bytes());
assert_eq!(cache.get(g, &big1), None, "LRU large key must be evicted");
assert_eq!(cache.get(g, &big2), Some(PartitionLoc::new(2, 2)));
}
#[test]
fn same_key_two_generations_no_alias() {
let cache = GlobalKeyOffsetCache::with_budget_and_shards(DEFAULT_GLOBAL_KEY_CACHE_BYTES, 4);
let g1 = ident(1, 10, 100, 1);
let g2 = ident(1, 20, 200, 2);
let g3 = ident(1, 30, 300, 3);
let key = b"shared-partition-key";
cache.insert(g1, key, PartitionLoc::new(111, 11));
cache.insert(g2, key, PartitionLoc::new(222, 22));
assert_eq!(cache.get(g1, key), Some(PartitionLoc::new(111, 11)));
assert_eq!(cache.get(g2, key), Some(PartitionLoc::new(222, 22)));
assert_eq!(cache.get(g3, key), None, "never-inserted generation misses");
}
#[test]
fn mismatched_identity_is_a_miss() {
let cache = GlobalKeyOffsetCache::with_budget_and_shards(DEFAULT_GLOBAL_KEY_CACHE_BYTES, 4);
let g1 = ident(1, 10, 100, 1);
let g2 = ident(1, 10, 999, 1);
cache.insert(g1, b"k", PartitionLoc::new(5, 5));
assert_eq!(cache.get(g2, b"k"), None, "size mismatch fails closed");
assert_eq!(cache.get(g1, b"k"), Some(PartitionLoc::new(5, 5)));
}
#[test]
fn invalidate_drops_generation_and_counts_distinctly() {
let cache = GlobalKeyOffsetCache::with_budget_and_shards(DEFAULT_GLOBAL_KEY_CACHE_BYTES, 4);
let g1 = ident(1, 10, 100, 1);
let g2 = ident(1, 20, 200, 2);
cache.insert(g1, b"a", PartitionLoc::new(1, 1));
cache.insert(g1, b"b", PartitionLoc::new(2, 2));
cache.insert(g2, b"c", PartitionLoc::new(3, 3));
let dropped = cache.invalidate(g1);
assert_eq!(dropped, 2, "both g1 entries dropped");
assert_eq!(cache.invalidation_count(), 2);
assert_eq!(cache.eviction_count(), 0, "invalidation is not an eviction");
assert_eq!(cache.get(g1, b"a"), None);
assert_eq!(cache.get(g1, b"b"), None);
assert_eq!(cache.get(g2, b"c"), Some(PartitionLoc::new(3, 3)));
}
#[test]
fn invalidate_does_not_touch_other_identities() {
let cache = GlobalKeyOffsetCache::with_budget_and_shards(DEFAULT_GLOBAL_KEY_CACHE_BYTES, 1);
let g1 = ident(1, 10, 100, 1);
let g2 = ident(1, 20, 200, 2);
cache.insert(g1, b"a1", PartitionLoc::new(1, 1));
cache.insert(g1, b"a2", PartitionLoc::new(2, 2));
cache.insert(g2, b"b1", PartitionLoc::new(3, 3));
cache.insert(g2, b"b2", PartitionLoc::new(4, 4));
let bytes_before = cache.resident_bytes();
let g2_bytes = entry_cost(2) * 2;
let dropped = cache.invalidate(g1);
assert_eq!(dropped, 2, "only g1's two entries dropped");
assert_eq!(cache.get(g2, b"b1"), Some(PartitionLoc::new(3, 3)));
assert_eq!(cache.get(g2, b"b2"), Some(PartitionLoc::new(4, 4)));
assert_eq!(cache.len(), 2, "g2's two entries remain");
assert_eq!(
cache.resident_bytes(),
g2_bytes,
"only g1's bytes reclaimed"
);
assert!(bytes_before > g2_bytes);
assert_eq!(cache.eviction_count(), 0, "invalidation is not eviction");
assert_eq!(cache.get(g1, b"a1"), None);
assert_eq!(cache.get(g1, b"a2"), None);
}
#[test]
fn get_probes_by_borrowed_slice() {
let cache = GlobalKeyOffsetCache::with_budget_and_shards(DEFAULT_GLOBAL_KEY_CACHE_BYTES, 4);
let g = ident(1, 1, 100, 1);
cache.insert(g, b"KEY", PartitionLoc::new(42, 7));
let buf = b"prefixKEYsuffix";
assert_eq!(
cache.get(g, &buf[6..9]),
Some(PartitionLoc::new(42, 7)),
"lookup by a borrowed subslice hits — no owned key needed"
);
}
#[test]
fn eviction_crosses_identity_by_global_recency() {
let cache = GlobalKeyOffsetCache::with_budget_and_shards(budget_for(2, 5), 1);
let g1 = ident(1, 10, 100, 1);
let g2 = ident(1, 20, 200, 2);
cache.insert(g1, b"key-A", PartitionLoc::new(1, 1));
cache.insert(g2, b"key-B", PartitionLoc::new(2, 2));
assert_eq!(cache.get(g1, b"key-A"), Some(PartitionLoc::new(1, 1)));
cache.insert(g2, b"key-C", PartitionLoc::new(3, 3));
assert_eq!(
cache.get(g1, b"key-A"),
Some(PartitionLoc::new(1, 1)),
"recently-used entry survives across the identity boundary"
);
assert_eq!(
cache.get(g2, b"key-B"),
None,
"global LRU evicted regardless of which identity owns it"
);
assert_eq!(cache.get(g2, b"key-C"), Some(PartitionLoc::new(3, 3)));
assert!(cache.resident_bytes() <= cache.budget_bytes());
}
#[test]
fn rebind_keeps_entries_valid() {
let cache = GlobalKeyOffsetCache::with_budget_and_shards(DEFAULT_GLOBAL_KEY_CACHE_BYTES, 4);
let g = ident(7, 77, 4096, 12);
cache.insert(g, b"partition", PartitionLoc::new(64, 512));
assert_eq!(cache.get(g, b"partition"), Some(PartitionLoc::new(64, 512)));
}
#[test]
fn disabled_cache_is_a_genuine_no_op() {
let cache = GlobalKeyOffsetCache::disabled();
let g = ident(1, 1, 1, 1);
assert_eq!(cache.budget_bytes(), 0);
cache.insert(g, b"k", PartitionLoc::new(1, 2));
assert_eq!(cache.get(g, b"k"), None);
assert_eq!(cache.invalidate(g), 0);
assert_eq!(cache.len(), 0);
assert!(cache.is_empty());
assert_eq!(cache.resident_bytes(), 0);
assert_eq!(cache.hit_count(), 0);
assert_eq!(cache.miss_count(), 0);
assert_eq!(cache.eviction_count(), 0);
assert_eq!(cache.invalidation_count(), 0);
}
#[test]
fn counters_reflect_real_activity() {
let cache = GlobalKeyOffsetCache::with_budget_and_shards(budget_for(2, 5), 1);
let g = ident(1, 1, 100, 1);
cache.insert(g, b"key-A", PartitionLoc::new(10, 100));
cache.insert(g, b"key-B", PartitionLoc::new(20, 200));
assert_eq!(cache.eviction_count(), 0);
cache.insert(g, b"key-C", PartitionLoc::new(30, 300)); assert_eq!(cache.eviction_count(), 1);
assert!(cache.get(g, b"key-C").is_some()); assert!(cache.get(g, b"absent").is_none()); let inval = cache.invalidate(g);
assert!(inval >= 1);
let snap = cache.snapshot();
assert_eq!(snap.hits, 1);
assert_eq!(snap.misses, 1);
assert_eq!(snap.evictions, 1);
assert_eq!(snap.invalidations, inval);
assert_eq!(snap.capacity_bytes, cache.budget_bytes());
}
#[test]
fn poisoned_lock_recovers() {
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::thread;
let cache = Arc::new(GlobalKeyOffsetCache::with_budget_and_shards(
DEFAULT_GLOBAL_KEY_CACHE_BYTES,
1,
));
let g = ident(1, 1, 100, 1);
cache.insert(g, b"k", PartitionLoc::new(7, 8));
let poison_cache = Arc::clone(&cache);
let _ = thread::spawn(move || {
let _guard = GlobalKeyOffsetCache::lock(&poison_cache.shards[0]);
panic!("intentional poison");
})
.join();
let res = catch_unwind(AssertUnwindSafe(|| {
let hit = cache.get(g, b"k");
cache.insert(g, b"k2", PartitionLoc::new(9, 10));
hit
}));
assert_eq!(res.ok(), Some(Some(PartitionLoc::new(7, 8))));
}
#[test]
fn concurrency_soundness() {
use std::thread;
let cache = Arc::new(GlobalKeyOffsetCache::with_budget_bytes(64 * 1024));
let n_keys = 512u64;
let n_gens = 4u64;
let mut handles = Vec::new();
for t in 0..8u64 {
let cache = Arc::clone(&cache);
handles.push(thread::spawn(move || {
for round in 0..3000u64 {
let gi = (t.wrapping_add(round)) % n_gens;
let g = ident(1, gi, 100 + gi, gi);
let idx = (t.wrapping_mul(31).wrapping_add(round)) % n_keys;
let key = format!("k-{gi}-{idx}");
let expect = idx.wrapping_mul(1000).wrapping_add(gi);
match cache.get(g, key.as_bytes()) {
Some(loc) => assert_eq!(loc.data_offset, expect),
None => cache.insert(g, key.as_bytes(), PartitionLoc::new(expect, 0)),
}
}
}));
}
for h in handles {
h.join().expect("worker must not panic");
}
assert!(cache.resident_bytes() <= cache.budget_bytes());
}
#[test]
fn shard_count_rounds_to_power_of_two() {
let cache =
GlobalKeyOffsetCache::with_budget_and_shards(DEFAULT_GLOBAL_KEY_CACHE_BYTES, 100);
assert_eq!(cache.shards.len(), 128);
assert_eq!(cache.mask, 127);
}
#[test]
fn global_singleton_is_shared() {
let a = GlobalKeyOffsetCache::global();
let b = GlobalKeyOffsetCache::global();
assert!(
Arc::ptr_eq(&a, &b),
"global() returns the one shared instance"
);
assert!(a.budget_bytes() > 0);
}
}