Skip to content

Commit 48e73db

Browse files
authored
feat: trait-only sharded ttl controls, per_shard_initial_capacity, ExpiringLruCache::retain, docs and spec fixes (#286)
- remove the inherent `ttl`/`set_ttl`/`unset_ttl`/`refresh_on_hit`/`set_refresh_on_hit` on `ShardedTtlCacheBase`/`ShardedLruTtlCacheBase`; the runtime TTL controls live only on `ConcurrentCacheTtl`, matching the redis/redb stores and the single-owner `CacheTtl` - add `per_shard_initial_capacity` to `ShardedUnboundCacheBuilder`, `ShardedTtlCacheBuilder`, and `ShardedExpiringCacheBuilder` (per-shard preallocation hint, counterpart of the single-owner `initial_capacity`) - add `ExpiringLruCache::retain`, matching `LruTtlCache::retain`; document the shared retain contract (LRU iteration, expired entries removed regardless of the predicate, survivor recency preserved) on all three LRU stores - document that `set_max_size` shrinks evict strictly by LRU recency, ignoring TTL/expiry state (rustdoc, CHANGELOG, migration guide) - migration guide: cover the `disk_directory` -> `disk_dir` builder rename and the sharded TTL-control trait move; add both to the VERIFY error index - register the `resilience` example in Cargo.toml with required-features; fix its header run command to the minimal feature set - CHANGELOG: `RedbCacheBuildError` keeps a derived `Debug` (drop it from the manual-impl claim) - lib.rs: use the compiling `dyn ConcurrentCached<K, V, Error = E>` form in the `cache_get_or_set_with` doc - tests: cover all inert `sync_writes_buckets` modes in the ui test; add concurrent-resize stress tests for `ShardedLruTtlCache` and `ShardedExpiringLruCache`; pin the new builder hint and retain behavior - specs: mark design 0022/0026 implemented; record the sharded trait matrix, runtime resize, and buckets guard; correct the redb builder and trait enumerations
1 parent 70f4413 commit 48e73db

27 files changed

Lines changed: 507 additions & 134 deletions

CHANGELOG.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
### Breaking Changes
66

77
- `#[cached]` rejects an explicit `sync_writes_buckets` when `sync_writes` is not `"by_key"` with a pointed compile error. The value was previously accepted and silently ignored (buckets only exist on the `by_key` path); `#[once]` already rejected the same inert combination.
8-
- `RedbCacheBuilder::disk_directory` renamed to `disk_dir`, matching the `disk_dir` attribute on `#[concurrent_cached]`. Breaking only vs the 3.0 rcs (the method did not exist in 2.x under either name).
8+
- `RedbCacheBuilder::disk_directory` renamed to `disk_dir`, matching the `disk_dir` attribute on `#[concurrent_cached]`. The 2.x builder method was `DiskCacheBuilder::disk_directory`; see the [migration guide](docs/migrations/2.0-to-3.0.md#69-disk_directory-builder-method-renamed-to-disk_dir).
9+
- The inherent `ttl` / `set_ttl` / `unset_ttl` / `refresh_on_hit` / `set_refresh_on_hit` methods on `ShardedTtlCacheBase` / `ShardedLruTtlCacheBase` are removed; the runtime TTL controls live only on `ConcurrentCacheTtl` (same names and signatures, still `&self`). This makes the concurrent family uniform: `RedisCache`, `AsyncRedisCache`, and `RedbCache` already exposed them only through the trait, and the single-owner stores went through the same change for `CacheTtl` in rc.1. Builder setters are unaffected. Import the trait (or `cached::prelude::*`) at call sites; see the [migration guide](docs/migrations/2.0-to-3.0.md#70-sharded-ttl-stores-inherent-ttlset_ttlunset_ttlrefresh_on_hitset_refresh_on_hit-removed-use-concurrentcachettl).
910

1011
### Added
1112

12-
- `ShardedLruCacheBase`, `ShardedLruTtlCacheBase`, and `ShardedExpiringLruCacheBase` (and their default-hasher aliases) now support runtime capacity resizing via `set_max_size(&self, usize) -> Option<usize>` and `try_set_max_size(&self, usize) -> Result<Option<usize>, SetMaxSizeError>`. Shrinking evicts LRU-excess entries per shard, fires `on_evict`, and increments the eviction counter; the same ceiling-division-plus-16-per-shard-floor policy the builders use is applied; resize is not atomic across shards (matches the documented `set_ttl` behavior).
13+
- `ShardedLruCacheBase`, `ShardedLruTtlCacheBase`, and `ShardedExpiringLruCacheBase` (and their default-hasher aliases) now support runtime capacity resizing via `set_max_size(&self, usize) -> Option<usize>` and `try_set_max_size(&self, usize) -> Result<Option<usize>, SetMaxSizeError>`. Shrinking evicts LRU-excess entries per shard strictly by recency (expired-but-recent entries survive a shrink; only `evict()` sweeps by TTL/expiry), fires `on_evict`, and increments the eviction counter; the same ceiling-division-plus-16-per-shard-floor policy the builders use is applied; resize is not atomic across shards (matches the documented `set_ttl` behavior).
14+
- `per_shard_initial_capacity` on `ShardedUnboundCacheBuilder`, `ShardedTtlCacheBuilder`, and `ShardedExpiringCacheBuilder`: a per-shard preallocation hint, the sharded counterpart of the single-owner builders' `initial_capacity` (total preallocation is `shards x per_shard_initial_capacity`).
15+
- `ExpiringLruCache::retain(keep)`: removes entries that are expired or fail the predicate, firing `on_evict` and counting evictions, matching `LruTtlCache::retain`. The `retain` docs on all three LRU stores now spell out the shared contract: iteration is over the LRU entries, expired entries are removed without consulting the predicate (expiry-aware stores only), and survivor recency order is unchanged.
1316
- New runnable example `examples/resilience.rs` covering `sync_writes = "by_key"`, `result_fallback`, and `force_refresh`.
1417

1518
### Changed
@@ -46,7 +49,7 @@
4649
- `RedbCache` default-directory resolution self-heals a pre-existing cache directory with legacy permissions. A directory created by an earlier `cached` version was created with the process umask (0775 under the umask-002 user-private-group default of Debian/Ubuntu) and permanently failed the security validation with "I/O error preparing the disk cache directory"; the app-derived candidate is now tightened to 0700 and re-validated (the chmod only succeeds for the owner, so an attacker-owned or symlinked directory still falls through to the next candidate instead of aborting).
4750
- `ShardedExpiringLruCache::cache_set` evaluates the displaced entry's `is_expired()` exactly once, under the shard write lock. It previously evaluated twice (once inside the lock for the eviction counter, once outside for `on_evict` and the return value), so a value crossing the expiry threshold between the two calls fired `on_evict` without counting the eviction. The other sharded expiring stores already evaluated once.
4851
- `RedisCacheBuildError::MissingConnectionString` redacts the env-var value carried by `std::env::VarError::NotUnicode`. The raw value is the connection string itself (credentials included) and was printed by both `Display` and `Debug`.
49-
- `RedisCacheBuildError` uses a manual `Debug` impl, matching `RedisCacheError` / `RedbCacheError` / `RedbCacheBuildError`.
52+
- `RedisCacheBuildError` uses a manual `Debug` impl, matching `RedisCacheError` / `RedbCacheError` (`RedbCacheBuildError` keeps its derived `Debug`; it carries no redactable value).
5053
- `make examples` actually runs the registered examples again: the per-example targets are `.PHONY`, and make skips implicit-rule search for phony targets, so the `examples/basic/%` / `examples/redis/%` pattern rules silently expanded to nothing and only the two explicitly-ruled examples (`wasm`, `redis-async-async-std`) ran. The rules are now static pattern rules, `expires_per_key` and `struct_method` are registered, and the expansion guard checks every registered example expands to its own run command.
5154

5255
### Documentation

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ required-features = ["time_stores", "proc_macro"]
215215
name = "kitchen_sink"
216216
required-features = ["time_stores", "proc_macro"]
217217

218+
[[example]]
219+
name = "resilience"
220+
required-features = ["time_stores", "proc_macro"]
221+
218222
[[example]]
219223
name = "disk"
220224
required-features = ["redb_store", "proc_macro"]

docs/migrations/2.0-to-3.0.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,9 @@ in a hot path), recreate the cache with the same builder settings instead of cal
10351035
- `ConcurrentCloneCached::cache_peek_with_expiry_status` (and the `CloneCached` equivalent) — a read that returns the cached value along with its expiry status without renewing the TTL, updating LRU recency, or incrementing hit/miss counters. This is now a *required* method (see breaking change #16); the built-in sharded and in-memory expiry stores implement it with a genuine side-effect-free read.
10361036
- `in_impl = true` macro attribute — cache a method inside an `impl` block (the cache static is emitted in the method body); a `self` receiver is allowed and excluded from the key. The `_prime_cache` companion is not generated for `in_impl` methods.
10371037
- `LruCache::set_max_size` / `try_set_max_size`, `LruTtlCache::set_max_size` / `try_set_max_size`, `ExpiringLruCache::set_max_size` / `try_set_max_size` — resize a live LRU-backed cache (evicts LRU entries when shrinking). All three `try_set_max_size` methods return `Result<Option<usize>, cached::SetMaxSizeError>` where the inner value is the previous bound (same shape as `set_max_size`; `ZeroSize` on a zero argument).
1038-
- `ShardedLruCache::set_max_size` / `try_set_max_size`, `ShardedLruTtlCache::set_max_size` / `try_set_max_size`, `ShardedExpiringLruCache::set_max_size` / `try_set_max_size` (and the `*Base` generic forms) — runtime capacity resize on the sharded LRU stores. Shrinking evicts LRU-excess entries per shard, fires `on_evict`, and increments the eviction counter; the same ceiling-division-plus-16-per-shard-floor policy the builders use is applied. Resize is not atomic across shards (matches the documented `set_ttl` behavior). Return type matches the non-sharded `set_max_size` / `try_set_max_size`.
1038+
- `ShardedLruCache::set_max_size` / `try_set_max_size`, `ShardedLruTtlCache::set_max_size` / `try_set_max_size`, `ShardedExpiringLruCache::set_max_size` / `try_set_max_size` (and the `*Base` generic forms) — runtime capacity resize on the sharded LRU stores. Shrinking evicts LRU-excess entries per shard strictly by recency (TTL/expiry state is ignored; call `evict()` first to sweep expired entries preferentially), fires `on_evict`, and increments the eviction counter; the same ceiling-division-plus-16-per-shard-floor policy the builders use is applied. Resize is not atomic across shards (matches the documented `set_ttl` behavior). Return type matches the non-sharded `set_max_size` / `try_set_max_size`.
1039+
- `per_shard_initial_capacity(n)` on `ShardedUnboundCacheBuilder`, `ShardedTtlCacheBuilder`, and `ShardedExpiringCacheBuilder` — per-shard preallocation hint, the sharded counterpart of the single-owner builders' `initial_capacity` (total preallocation is `shards * per_shard_initial_capacity`). The LRU-bounded sharded builders size their shards from `max_size` / `per_shard_max_size` and do not take a separate hint.
1040+
- `ExpiringLruCache::retain(keep)` — removes entries that are expired or fail the predicate, firing `on_evict` and counting evictions; matches `LruTtlCache::retain`. `LruCache::retain` (no expiry dimension) removes solely on the predicate.
10391041
- `RedisCache` / `AsyncRedisCache` now implement `cache_clear` / `async_cache_clear` (namespace-scoped `SCAN` + `DEL`); `cache_reset` / `async_cache_reset` delegate to them (redis tracks no in-memory metrics).
10401042
- Reference arguments (`&T`, `Option<&T>`) now form the default macro key without a `convert`.
10411043
- `Expires::expires_at(&self) -> Option<cached::time::Instant>` -- default trait method returning the value's expiry instant when tracked (`None` by default / when unknown). Advisory/observability only; `is_expired()` remains the authoritative liveness check. Existing `impl Expires` blocks (which provide only `is_expired`) get the default for free. The instant type is the crate's `time::Instant` (`web_time` on wasm, `std` elsewhere), not `std::time::Instant`; a custom override must use that type.
@@ -1465,6 +1467,45 @@ fn compute(x: u64) -> u64 { expensive(x) }
14651467
fn compute(x: u64) -> u64 { expensive(x) }
14661468
```
14671469

1470+
### 69. `disk_directory(...)` builder method renamed to `disk_dir(...)`
1471+
1472+
The disk-store builder's directory setter is renamed, matching the macro attribute
1473+
`disk_dir = "..."`. This is on top of the `DiskCacheBuilder` -> `RedbCacheBuilder` type rename
1474+
(#23): renaming the type alone still fails to compile if the old method name is used.
1475+
1476+
Detection: `grep -rn 'disk_directory('`
1477+
1478+
Action:
1479+
```rust
1480+
// Before
1481+
DiskCacheBuilder::new("cache_name").disk_directory("/var/cache/my-app")
1482+
// After
1483+
RedbCache::builder("cache_name").disk_dir("/var/cache/my-app")
1484+
```
1485+
1486+
### 70. Sharded TTL stores: inherent `ttl`/`set_ttl`/`unset_ttl`/`refresh_on_hit`/`set_refresh_on_hit` removed; use `ConcurrentCacheTtl`
1487+
1488+
The runtime TTL controls on the sharded TTL-capable stores (`ShardedTtlCache`,
1489+
`ShardedLruTtlCache` and their `*Base` forms) are no longer inherent methods; they live only on
1490+
the [`ConcurrentCacheTtl`] trait (same names, same signatures, still `&self`). This makes the
1491+
concurrent family uniform: `RedisCache`, `AsyncRedisCache`, and `RedbCache` already exposed
1492+
these controls only through `ConcurrentCacheTtl`. The single-owner counterpart is #20
1493+
(`TtlCache` / `LruTtlCache` via `CacheTtl`). Builder setters (`.ttl(...)`,
1494+
`.refresh_on_hit(...)`) are unaffected.
1495+
1496+
Detection: `no method named set_ttl` (or `ttl`/`unset_ttl`/`refresh_on_hit`/`set_refresh_on_hit`)
1497+
`found for struct ShardedTtlCacheBase`/`ShardedLruTtlCacheBase` -- the compiler suggests the
1498+
trait import.
1499+
1500+
Action:
1501+
```rust
1502+
// Before -- inherent methods, no import needed
1503+
cache.set_ttl(Duration::from_secs(30));
1504+
// After -- same call, trait must be in scope
1505+
use cached::ConcurrentCacheTtl; // or `use cached::prelude::*;`
1506+
cache.set_ttl(Duration::from_secs(30));
1507+
```
1508+
14681509
## Required Cargo.toml change
14691510

14701511
```toml
@@ -1535,4 +1576,6 @@ has a breaking change this major, see #51).
15351576
- a `match` arm on `RedisCacheBuildError::Build(BuildError::MissingRequired(..))` for `"ttl"` is now unreachable → remove the arm; set `.ttl(Duration)` explicitly if you need expiry (#65).
15361577
- a `#[concurrent_cached(disk = true, cache_prefix_block = ...)]` annotation that previously compiled → remove `cache_prefix_block`; disk stores key by redb table name, set via `name = "..."` (#66).
15371578
- `` `sync_writes_buckets` only applies to `sync_writes = "by_key"` `` → add `sync_writes = "by_key"` to opt into per-key locking, or remove `sync_writes_buckets` to keep no-synchronization behavior (#68).
1579+
- `no method named disk_directory` on a `RedbCacheBuilder` → renamed to `disk_dir` (#69).
1580+
- `no method named set_ttl`/`ttl`/`unset_ttl`/`refresh_on_hit`/`set_refresh_on_hit` on a `ShardedTtlCache`/`ShardedLruTtlCache` value → `use cached::ConcurrentCacheTtl;` or the prelude (#70).
15381581
- `cargo test` — no behavior changes beyond the above; disk caches will be empty on first run after upgrade (format change). The default `#[cached]` write behavior is unchanged from 2.x (no synchronization); `sync_writes = "by_key"` is an opt-in (#39).

examples/resilience.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Three 3.0 resilience attributes on `#[cached]` functions - in-memory, no externa
1414
a cache hit (expression false) and a forced recompute (expression true).
1515
1616
Run:
17-
cargo run --example resilience --all-features
17+
cargo run --example resilience --features "proc_macro,time_stores"
1818
*/
1919

2020
use cached::macros::cached;

specs/design/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ the item stays here for history.
4040
| [0019](0019-ahash-default-feature.md) | Drop `ahash` from default features | Needs research |
4141
| [0020](0020-argument-error-unification.md) | Unify single-variant argument errors | Needs research |
4242
| [0021](0021-redb-refresh-on-hit-cost.md) | Amortize redb refresh-on-hit write txns | Needs research |
43-
| [0022](0022-serialize-cached-set-ref-return.md) | `cache_set_ref` returning previous value | Needs research |
43+
| [0022](0022-serialize-cached-set-ref-return.md) | `cache_set_ref` returning previous value | Implemented |
4444
| [0023](0023-peek-read-trait-merge.md) | Merge `CachedPeek`/`CachedRead`; trait fragmentation | Needs research |
4545
| [0024](0024-generated-companion-naming.md) | Rename/namespace generated companion fns | Needs research |
4646
| [0025](0025-redb-disk-path-introspection.md) | redb resolved-path introspection + temp fallback | Needs research |
47-
| [0026](0026-serde-feature.md) | Explicit `serde` feature for custom serialize stores | Needs research |
47+
| [0026](0026-serde-feature.md) | Explicit `serde` feature for custom serialize stores | Implemented |
4848
| [0027](0027-sync-writes-default-revert.md) | `sync_writes` default flip and revert | Implemented |
4949
| [0028](0028-per-entry-expiry-and-set-ttl-zero.md) | Per-entry expiry model and `set_ttl(0)` semantics | Implemented |
5050
| [0029](0029-self-healing-deserialization-default.md) | Self-healing deserialization default | Implemented |

specs/macro-cached.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ removed). `size = N` is a hard rename error directing to `max_size = N`, per
2020

2121
## CACHED-3
2222

23-
Write-synchronization attributes: `sync_writes` (`false` default = no synchronization,
24-
`"by_key"` bucketed locks, `true`/`"default"` whole-cache lock), `sync_writes_buckets` (default
25-
64), `sync_lock` (`"rwlock"` default or `"mutex"`), `unsync_reads` (shared read lock for hits;
23+
Write-synchronization attributes: `sync_writes` (`false`/`"disabled"` default = no
24+
synchronization, `"by_key"` bucketed locks, `true`/`"default"` whole-cache lock),
25+
`sync_writes_buckets` (default 64; a compile error unless `sync_writes = "by_key"`),
26+
`sync_lock` (`"rwlock"` default or `"mutex"`), `unsync_reads` (shared read lock for hits;
2627
`CachedRead` stores only). The `false` default and the earlier revert are recorded in
2728
[design/0027-sync-writes-default-revert.md](design/0027-sync-writes-default-revert.md); the
2829
per-key lock buckets use a seeded hasher per

specs/store-lru.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ Bounded by `max_size`: inserting beyond capacity evicts the least-recently-used
1010

1111
## LRU-2
1212

13-
Constructors: infallible `LruCache::new(max_size)`, or `LruCache::builder().max_size(n)` for a
14-
custom hasher. `max_size` is the setter (renamed from `.size()` in 2.0). Building with a
15-
zero/invalid size is a `BuildError`. See [builders.md](builders.md).
13+
Constructors: `LruCache::new(max_size)` (returns the cache directly; panics on zero), or
14+
`LruCache::builder().max_size(n)` for a custom hasher. `max_size` is the setter (renamed from
15+
`.size()` in 2.0). Building with a zero/invalid size is a `BuildError`. See
16+
[builders.md](builders.md).
1617

1718
## LRU-3
1819

@@ -23,3 +24,7 @@ See [metrics.md](metrics.md).
2324

2425
Implements `Cached`, `CachedPeek`, and `CachedIter`. Size/iter/evict semantics follow
2526
[design/0002-size-iter-evict-semantics.md](design/0002-size-iter-evict-semantics.md).
27+
Inherent `retain(keep)` removes entries failing the predicate (firing `on_evict` and counting
28+
evictions); the expiry-aware LRU stores (`LruTtlCache`, `ExpiringLruCache`) share the contract
29+
but also remove expired entries regardless of the predicate. `set_max_size` /
30+
`try_set_max_size` resize a live cache.

specs/store-redb.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# Disk (redb) backend
22

33
`RedbCache` is a disk-backed concurrent store using `redb`, gated behind `redb_store`. It is
4-
self-synchronizing over a shared `&self` and builder-only (required path field).
4+
self-synchronizing over a shared `&self` and builder-only: `builder(name)` takes the required
5+
cache name positionally; the on-disk directory (`disk_dir`) is optional with a system-cache-dir
6+
fallback.
57

68
## REDB-1
79

@@ -24,7 +26,10 @@ Errors are `RedbCacheError` (build: `RedbCacheBuildError`) with named variants,
2426
## REDB-4
2527

2628
Implements the concurrent trait family (`ConcurrentCacheBase`, `ConcurrentCached`,
27-
`ConcurrentCacheTtl`). `disk_path()` ships (returns the resolved database path after build).
29+
`ConcurrentCachedAsync`, `ConcurrentCacheTtl`, `SerializeCached`, `SerializeCachedAsync`).
30+
`ConcurrentCacheEvict` is intentionally not implemented; expired-entry sweeping is the fallible
31+
inherent `remove_expired_entries()`. `disk_path()` ships (returns the resolved database path
32+
after build).
2833
Builder-side resolved-path introspection and a configurable temp-dir fallback remain open
2934
([design/0025-redb-disk-path-introspection.md](design/0025-redb-disk-path-introspection.md)). See
3035
[traits-concurrent.md](traits-concurrent.md).

specs/store-sharded.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@ alias form the exported surface.
2121

2222
## SHARD-3
2323

24-
Sharded stores implement the concurrent trait family (`ConcurrentCacheBase`,
25-
`ConcurrentCached`, and `ConcurrentCacheTtl` on TTL variants). Metrics are exposed through the
26-
trait per [design/0012-concurrent-metrics-trait.md](design/0012-concurrent-metrics-trait.md).
24+
Sharded stores implement the concurrent trait family: `ConcurrentCacheBase`,
25+
`ConcurrentCached`, and `ConcurrentCachedAsync` on all six variants; `ConcurrentCacheTtl` on the
26+
TTL variants; `ConcurrentCacheEvict` and `ConcurrentCloneCached` on the four expiry-capable
27+
variants (TTL and expiring). The runtime TTL controls (`ttl`/`set_ttl`/`unset_ttl`/
28+
`refresh_on_hit`/`set_refresh_on_hit`) exist only on `ConcurrentCacheTtl`, not as inherent
29+
methods. Metrics are exposed through the trait per
30+
[design/0012-concurrent-metrics-trait.md](design/0012-concurrent-metrics-trait.md).
2731
See [traits-concurrent.md](traits-concurrent.md).
2832

2933
## SHARD-4
@@ -34,3 +38,13 @@ directions: a read-optimized sharded LRU
3438
([design/0010-read-optimized-sharded-lru.md](design/0010-read-optimized-sharded-lru.md)) and
3539
collapsing the `*Base` alias into a defaulted type param
3640
([design/0015-sharded-base-alias-collapse.md](design/0015-sharded-base-alias-collapse.md)).
41+
42+
## SHARD-5
43+
44+
The LRU-bounded variants (`ShardedLruCache`, `ShardedLruTtlCache`, `ShardedExpiringLruCache`)
45+
support runtime capacity resizing via `set_max_size(&self)` / `try_set_max_size(&self)`, using
46+
the builders' ceiling-division-plus-16-per-shard-floor policy. Shrinks evict per shard strictly
47+
by LRU recency (TTL/expiry state is ignored); resize is not atomic across shards. The unbounded
48+
variants' builders (`ShardedUnboundCacheBuilder`, `ShardedTtlCacheBuilder`,
49+
`ShardedExpiringCacheBuilder`) take a `per_shard_initial_capacity` preallocation hint, the
50+
sharded counterpart of the single-owner builders' `initial_capacity`.

0 commit comments

Comments
 (0)