|
| 1 | +# Weight store |
| 2 | + |
| 3 | +This store module is implemented in `cachekit::store::weight` and provides a weight-aware store that enforces both an entry-count limit and a total weight limit (typically “bytes”). For an overview of all store types, see `docs/stores/README.md`. |
| 4 | + |
| 5 | +## Architecture |
| 6 | +- Stores `Arc<V>` values in a `HashMap<K, WeightEntry<V>>`. |
| 7 | +- Tracks a running `total_weight` to enforce a weight capacity. |
| 8 | +- Uses a caller-provided weight function `F: Fn(&V) -> usize`. |
| 9 | + |
| 10 | +## Capacity Semantics |
| 11 | +- Two limits are enforced: entry count (`capacity()`) and total weight (`capacity_weight()`). |
| 12 | +- `try_insert` returns `Err(StoreFull)` when inserting a new key would exceed the entry limit, or when inserting/updating would exceed the weight limit. |
| 13 | +- Updates recompute weight and may fail if the updated value is “too large” for the configured weight capacity. |
| 14 | +- The store does not evict on its own; eviction is driven by the policy layer (which should call `remove` and then `record_eviction` to keep eviction metrics accurate). |
| 15 | + |
| 16 | +## Key Components |
| 17 | +- `WeightStore<K, V, F>`: single-threaded weight-aware store. |
| 18 | +- `ConcurrentWeightStore<K, V, F>`: `RwLock`-protected store for multi-threaded use. |
| 19 | + |
| 20 | +## Core Operations |
| 21 | +- `try_insert`: insert/update by key while enforcing entry and weight limits. |
| 22 | +- `get`: fetch by key (updates hit/miss counters). |
| 23 | +- `remove`: delete by key and adjust `total_weight`. |
| 24 | +- `total_weight`, `capacity_weight`, `clear`. |
| 25 | + |
| 26 | +## Performance Trade-offs |
| 27 | +- Inserts/updates compute weight; the weight function is on the hot path. |
| 28 | +- Reads are cheap (weight is stored per entry, not recomputed on get). |
| 29 | +- Keeps “size accounting” separate from the eviction policy (policy decides what to evict). |
| 30 | + |
| 31 | +## When to Use |
| 32 | +- Values vary widely in size and you want size-based capacity. |
| 33 | +- You want observability into how “full” the store is in bytes/weight. |
| 34 | + |
| 35 | +## Example Usage |
| 36 | +```rust |
| 37 | +use std::sync::Arc; |
| 38 | + |
| 39 | +use cachekit::store::traits::StoreMut; |
| 40 | +use cachekit::store::weight::WeightStore; |
| 41 | + |
| 42 | +let mut store = WeightStore::with_capacity(10, 64, |v: &String| v.len()); |
| 43 | +store.try_insert("k1", Arc::new("value".to_string())).unwrap(); |
| 44 | +assert!(store.total_weight() <= store.capacity_weight()); |
| 45 | +``` |
| 46 | + |
| 47 | +## Example: Concurrent usage |
| 48 | +```rust |
| 49 | +use std::sync::Arc; |
| 50 | + |
| 51 | +use cachekit::store::traits::ConcurrentStore; |
| 52 | +use cachekit::store::weight::ConcurrentWeightStore; |
| 53 | + |
| 54 | +let store = ConcurrentWeightStore::with_capacity(10, 64, |v: &String| v.len()); |
| 55 | +store.try_insert("k1", Arc::new("value".to_string())).unwrap(); |
| 56 | +assert!(store.total_weight() <= store.capacity_weight()); |
| 57 | +``` |
| 58 | + |
| 59 | +## Type Constraints |
| 60 | +- `K: Eq + Hash` for key lookup. |
| 61 | +- `F: Fn(&V) -> usize` to compute weight. |
| 62 | + |
| 63 | +## Thread Safety |
| 64 | +- `WeightStore` is single-threaded. |
| 65 | +- `ConcurrentWeightStore` is `Send + Sync` via `RwLock`. |
| 66 | + |
| 67 | +## Implementation Notes |
| 68 | +- Updates recompute weight and adjust `total_weight` (can fail with `StoreFull`). |
| 69 | +- Entry capacity and weight capacity are both enforced. |
| 70 | +- In `ConcurrentWeightStore`, `get` takes a write lock because it updates metrics; this can increase contention in read-heavy workloads. |
| 71 | +- Weight is an accounting mechanism, not a guarantee of actual memory usage; the accuracy depends on your chosen weight function. |
| 72 | + |
| 73 | +## Weight Function Guidelines |
| 74 | +- Keep it fast (it runs on every insert/update). |
| 75 | +- Keep it deterministic and stable for a given value (avoid time/randomness/global state). |
| 76 | +- Prefer “monotonic with size” (larger values should not report smaller weights). |
| 77 | +- For `ConcurrentWeightStore`, the weight function must be `Send + Sync` (capture-only thread-safe state). |
0 commit comments