iqdb-cache 1.0.0

In-process vector and result caching with LRU/LFU/ARC eviction - part of the iQDB family.
Documentation
# iqdb-cache v1.0.0 — Stable

**The cache is done.** v1.0.0 commits the public API under SemVer for the 1.x series — no breaking changes until 2.0. Nothing new ships here; this release promotes the surface designed and hardened across the 0.x line to stable, with every Definition-of-Done criterion satisfied.

## What is iqdb-cache?

An in-process caching layer between the database and an index. It wraps any `iqdb_index::IndexCore` as a `CachedIndex` — itself an `IndexCore`, so it drops in anywhere the wrapped index does — and memoizes search results, turning a repeated query into a memory read. It is opt-in and transparent: a database is correct with no cache, and wrapping one never changes *what* a search returns, only how fast a repeat returns.

## The 1.0 surface

```rust
use iqdb_cache::CachedIndex;
use iqdb_index::IndexCore;
use iqdb_types::{DistanceMetric, SearchParams};

let cached = CachedIndex::new(iqdb_cache::doc_stub::stub_index());
let params = SearchParams::new(3, DistanceMetric::Cosine);

let cold = cached.search(&[1.0, 0.0, 0.0], &params).expect("search"); // miss
let warm = cached.search(&[1.0, 0.0, 0.0], &params).expect("search"); // hit
assert_eq!(cold, warm);
```

The committed public API (frozen until 2.0):

- **`CachedIndex<I>`** — the wrapper. Tier 1: `new`. Tier 2: `with_capacity`, `with_config`. Tier 3: it *is* an `IndexCore`. Plus `capacity`, `ttl`, `policy`, `is_enabled`, `get_ref`, `into_inner`, `clear_cache`, `cache_stats`.
- **`CacheConfig`** — the builder: `new`, `capacity`, `ttl`, `no_ttl`, `policy`, `Default`.
- **`EvictionPolicy`**`Lru` (default), `Lfu`, `Fifo`, `Arc` (`#[non_exhaustive]`).
- **`CacheStats`**`hits`, `misses`, `evictions`, `len`, `capacity`; `lookups`, `hit_rate`.
- **`VERSION`**.

## The feature set

- **Transparent memoization.** Identical searches (same query and `SearchParams`) are served from a bounded LRU-by-default cache; the wrapper forwards every other `IndexCore` call unchanged.
- **Mutation-exact invalidation.** `insert` / `insert_batch` / `delete` invalidate the cache, so a search after a write is never stale.
- **Optional TTL.** A per-entry expiry (via `clock-lib`) bounds staleness from changes the wrapper cannot see; off by default, and the clock is never read without it.
- **Four eviction policies** — LRU, LFU, FIFO, ARC — selectable through one config knob, all arena-backed with amortized `O(1)` operations and zero `unsafe`.
- **Stats.** Lifetime hit / miss / eviction counters and a `hit_rate`.
- **Off by default.** Capacity `0` is a pure passthrough; a database opts in by wrapping.

## Definition of Done

Every criterion in `dev/DIRECTIVES.md` §7 is met:

1. ✅ Compiles clean on Windows and Linux, on stable and the 1.87 MSRV (the CI matrix also covers macOS).
2.`fmt`, `clippy --all-targets --all-features -D warnings`, `test --all-features`, and `cargo doc -D warnings` are clean.
3.`cargo audit` and `cargo deny check` pass.
4. ✅ No `unwrap` / `expect` / `todo!` / `dbg!` in shipping code; `#![forbid(unsafe_code)]`**zero unsafe**.
5. ✅ A Tier-1 API (`CachedIndex::new`) headlines the docs.
6. ✅ Property tests cover every core invariant (transparency, no-stale-after-mutation, capacity bound) under every policy; `loom` covers the concurrent search path.
7. ✅ Hot paths carry `criterion` benchmarks; the one regression found during the policy rework was fixed back to within ~5% of baseline.
8. ✅ Every public item is documented with a runnable example; `docs/API.md`, the README, and version metadata are current.
9. ✅ All changes are recorded in the changelog.

Plus the project-specific invariants (§8): the database is correct with the cache disabled (the default), and a stale result is never returned after a mutation — both property-tested.

## Performance

On the reference machine, a top-10 search over 10,000 vectors at dim 64 (`cargo bench`):

| Path | Time | vs. uncached |
|------|------|--------------|
| `uncached_scan` (full scan) | ~234 µs ||
| `cache_hit` (default LRU) | ~250 ns | ~935× |
| `Fifo` hit | ~250 ns | ~935× |
| `Lru` hit | ~278 ns | ~840× |
| `Arc` hit | ~387 ns | ~605× |
| `Lfu` hit | ~1.17 µs | ~200× |

A configured TTL adds ~29 ns (one monotonic clock read) to a hit; with no TTL the clock is never touched.

## Breaking changes

**None.** 1.0.0 is the 0.6.0 surface, now stable. From here, only additive, non-breaking changes until 2.0.

## Verification

```bash
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
cargo build --examples
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
RUSTFLAGS="--cfg loom" cargo test --test loom_iqdb_cache
cargo audit
cargo deny check
```

MSRV: Rust 1.87.

## Installation

```toml
[dependencies]
iqdb-cache = "1.0"
```

## Documentation

- [README]https://github.com/jamesgober/iqdb-cache/blob/main/README.md
- [API reference]https://github.com/jamesgober/iqdb-cache/blob/main/docs/API.md
- [ROADMAP]https://github.com/jamesgober/iqdb-cache/blob/main/dev/ROADMAP.md
- [Standards (REPS)]https://github.com/jamesgober/iqdb-cache/blob/main/REPS.md
- [CHANGELOG]https://github.com/jamesgober/iqdb-cache/blob/main/CHANGELOG.md

---

**Full diff:** [`v0.6.0...v1.0.0`](https://github.com/jamesgober/iqdb-cache/compare/v0.6.0...v1.0.0).
**Changelog:** [`CHANGELOG.md`](https://github.com/jamesgober/iqdb-cache/blob/main/CHANGELOG.md).