cache-mod 1.0.0

High-performance in-process caching with multiple eviction policies (LRU, LFU, TinyLFU, TTL, size-bounded). Async-safe, lock-minimized internals. Typed key-value API. No dependency on any external store.
Documentation
# cache-mod v0.9.0 — Hardening + audit

**Audit milestone.** No new features. Skipped 0.8.0 — the `SizedCache` sharding redesign hasn't surfaced as a real-world bottleneck, and forcing it through artificially would have been busywork. This release locks down the 0.7.0 public surface with explicit `#[must_use]` contracts, an expanded test surface, a documented set of crate-level guarantees, and a `cargo deny` configuration that gates the dependency story before 1.0.

## What is cache-mod?

High-performance in-process caching for Rust with five eviction policies all behind one trait. Async-safe (`&self` everywhere, `Send + Sync` cache instances), lock-minimized internals (single-mutex for small caches, sharded for larger), typed key-value API. No dependency on any external store.

## What landed in the audit

### `#[must_use]` on every value-returning trait method

`Cache::get`, `insert`, `remove`, `contains_key`, `len`, `is_empty`, and `capacity` now carry `#[must_use]` annotations. Silently dropping these returns is almost always a bug:

- Dropping `get`'s result means you bothered to look up the value and threw it away.
- Dropping `insert`'s result means you missed the new-vs-replace signal (or, on `TinyLfuCache`, the admit-vs-reject signal).
- Dropping `contains_key` / `len` / `is_empty` / `capacity` is just dead code.

Callers that *want* to drop a return now have to be explicit: `let _ = cache.insert(k, v);`. The existing test suite already uses this pattern, so the change is source-compatible with sensible call-sites.

### Crate-level "Guarantees" section

`src/lib.rs` now leads with an explicit guarantees block (visible at the top of [docs.rs/cache-mod](https://docs.rs/cache-mod)):

- **No `unsafe`.** Zero `unsafe` blocks in the crate.
- **No `panic!`, no `unwrap`, no `expect`** on shipping paths. `clippy::unwrap_used` and `clippy::expect_used` are denied at the crate level.
- **No background threads.** Every cache type uses lazy bookkeeping — eviction, expiry, and sketch aging all run on the call-site thread.
- **No required runtime.** No async runtime dependency. `&self` everywhere lets you share instances across `.await` points if you do use one.
- **`Send + Sync`** whenever `K, V` are.

These were all true in earlier releases but never documented in one place. Saying them out loud makes them part of the contract.

### Expanded test surface (74 → 91)

**9 new unit tests** in `src/sharding.rs`:

- `shard_count_tiny_caches_use_one_shard` — confirms the single-shard floor for capacities under 32.
- `shard_count_scales_with_capacity` — exercises the heuristic at boundary capacities (32, 48, 64, 127, 128, 256, 1 000 000).
- `shard_count_is_always_power_of_two` — the routing mask depends on this invariant.
- `per_shard_capacity_distributes_evenly_when_divisible` — straightforward arithmetic check.
- `per_shard_capacity_floors_when_not_divisible` — documents the floor-division behaviour with a concrete example (capacity 17 with 16 shards yields 16 effective).
- `per_shard_capacity_never_returns_zero` — pathological-input safety.
- `from_factory_creates_requested_number_of_shards` — sanity-check the constructor.
- `shard_for_routes_deterministically` — the same key always lands in the same shard.
- `shard_for_distributes_keys_across_shards` — statistical check that 1024 keys hit at least 8 of 16 shards.

**8 new property tests** in `tests/properties.rs`:

- 4 **concurrent-safety properties** — for each of `LruCache`, `LfuCache`, `TtlCache`, `TinyLfuCache`, spawn 4 threads × 256 random ops each against a single shared instance, then assert the capacity invariant holds after every thread joins. Exercises the sharded code path (capacity 64 → 4 shards) under realistic interleaving.
- 4 **cross-cache symmetry properties**`remove(k)` followed by `contains_key(&k)` returns `false`, on every cache type. Trivial but worth proving against arbitrary inputs.

Total test count: **9 unit + 47 integration + 17 property + 18 doctests = 91 tests**, up from 74. Property test runtime stayed under 1 second on the local machine.

### `cargo deny` configuration

A new `deny.toml` lands at the repo root. Run with `cargo deny check all`:

- **Licenses** — allow-list of permissive licenses only (MIT, Apache-2.0, BSD-2/3, ISC, Unicode-3.0, Zlib, Apache-2.0 WITH LLVM-exception). Anything outside fails the check.
- **Advisories** — yanked crates fail. RUSTSEC advisories ride on `cargo audit`'s normal feed.
- **Bans** — wildcard dependencies fail. Duplicate versions warn.
- **Sources** — only `crates.io-index` is allowed; unknown registries and unknown git remotes fail.

This locks in the dependency story before the 1.0 freeze. The library itself has zero runtime dependencies, so the surface to clear is just dev-deps (criterion, proptest), both well-maintained.

### Documentation polish

- `Cache::insert` rustdoc now explicitly mentions the `TinyLfuCache` admit-vs-reject case in the return-value description.
- `Cache::len` rustdoc clarifies that the sharded version sums per-shard lengths under brief locks — **not** an atomic snapshot across shards. Matters for code racing a `len()` against concurrent writers.
- `Cache::clear` rustdoc explicitly notes that auxiliary state (LFU priority index, TinyLFU sketch, monotonic clocks) is reset.
- `Cache::contains_key` rustdoc surfaces the `TtlCache`-specific nuance: an expired-but-not-yet-cleaned entry is removed during the check.
- Stale "rewrites land in 0.6.0" line in crate-level docs cleaned up — 0.6.0 (arena-backed) and 0.7.0 (sharded) have both shipped.

## Breaking changes

**None at the source-compatible level.** Every public symbol has identical signatures to 0.7.0. The `#[must_use]` additions are warnings, not errors — existing call-sites that drop return values will see a new warning but compile cleanly.

The crate remains pre-1.0; minor versions may still break in the future. Pin exact versions.

## Verification

Local run on Windows x86_64, Rust stable:

```bash
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo clippy --all-targets --no-default-features -- -D warnings
cargo test --all-features
cargo doc --no-deps --all-features            # RUSTDOCFLAGS="-D warnings"
cargo build --benches --release
```

All green. Test totals:

- Unit tests: **9 passed** (sharding helpers).
- Integration tests: **47 passed** (unchanged from 0.7.0).
- Property tests: **17 passed** (up from 9 — added concurrent-safety and cross-cache symmetry properties).
- Doctests: **18 passed**.

Optional `cargo deny check all` will pass after the 0.9.0 commit lands and `deny.toml` is picked up.

## What's next

- **0.9.x** — audit-fix releases if anything surfaces between now and the freeze. Patch-only; no minor bumps.
- **1.0.0** — API freeze. SemVer-strict from that point on. The 0.9.0 public surface (`Cache` trait, `CacheError`, the five cache types, their constructors, every method, the crate-level guarantees) is the candidate for the 1.0 contract.

## Installation

```toml
[dependencies]
cache-mod = "0.9"
```

MSRV: Rust 1.75. Edition 2021. `default-features = ["std"]`.

## Documentation

- [README]https://github.com/jamesgober/cache-mod/blob/main/README.md
- [API reference (full)]https://github.com/jamesgober/cache-mod/blob/main/docs/API.md
- [CHANGELOG]https://github.com/jamesgober/cache-mod/blob/main/CHANGELOG.md
- [REPS standards]https://github.com/jamesgober/cache-mod/blob/main/REPS.md
- [docs.rs/cache-mod/0.9.0]https://docs.rs/cache-mod/0.9.0

---

**Full diff:** [`v0.7.0...v0.9.0`](https://github.com/jamesgober/cache-mod/compare/v0.7.0...v0.9.0).
**Changelog:** [`CHANGELOG.md`](https://github.com/jamesgober/cache-mod/blob/main/CHANGELOG.md#090---2026-05-21).