Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions sdk-feature-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

**Feature parity and compliance status across all CacheKit SDK implementations.**

*Last updated: 2026-07-25 — LAB-430 shipped TypeScript Node-only Memcached and File backends; the protocol-owned File format and vectors now define fail-closed flag negotiation. LAB-446: Python File backend gains full TTL inspection/refresh; Memcached gains `refresh_ttl` (touch) only (see [TTL management note](#reliability-features)). LAB-595 shipped: ts Cloudflare Workers flipped ❌ → ✅ via the `@cachekit-io/cachekit/workers` entrypoint on a wasm32 cachekit-core build (~55 KB gz measured); footnote ¹ records the phase-1 surface and semantics deltas. LAB-519: ts cold-miss single-flight (in-process, always on) + LockableBackend wired into `wrap()`'s miss path (opt-in); ts backpressure decision recorded; ts Redis lock/TTL capability cells refreshed for LAB-427. LAB-272 code-verified protocol-adherence audit (2026-07-22): interop/v1 merged in Python ([cachekit-py#220](https://github.com/cachekit-io/cachekit-py/pull/220)), TypeScript ([cachekit-ts#71](https://github.com/cachekit-io/cachekit-ts/pull/71)), and Rust ([cachekit-rs#33](https://github.com/cachekit-io/cachekit-rs/pull/33)); test-vector CI coverage corrected*
*Last updated: 2026-07-28 — LAB-729: rs backpressure flipped ❌ → ✅ (semaphore + bounded queue in the rs reliability stack; decision footnote records why the LAB-519 ts rationale doesn't transfer to tokio). LAB-430 shipped TypeScript Node-only Memcached and File backends; the protocol-owned File format and vectors now define fail-closed flag negotiation. LAB-446: Python File backend gains full TTL inspection/refresh; Memcached gains `refresh_ttl` (touch) only (see [TTL management note](#reliability-features)). LAB-595 shipped: ts Cloudflare Workers flipped ❌ → ✅ via the `@cachekit-io/cachekit/workers` entrypoint on a wasm32 cachekit-core build (~55 KB gz measured); footnote ¹ records the phase-1 surface and semantics deltas. LAB-519: ts cold-miss single-flight (in-process, always on) + LockableBackend wired into `wrap()`'s miss path (opt-in); ts backpressure decision recorded; ts Redis lock/TTL capability cells refreshed for LAB-427. LAB-272 code-verified protocol-adherence audit (2026-07-22): interop/v1 merged in Python ([cachekit-py#220](https://github.com/cachekit-io/cachekit-py/pull/220)), TypeScript ([cachekit-ts#71](https://github.com/cachekit-io/cachekit-ts/pull/71)), and Rust ([cachekit-rs#33](https://github.com/cachekit-io/cachekit-rs/pull/33)); test-vector CI coverage corrected*

</div>

Expand Down Expand Up @@ -126,7 +126,7 @@ The contract a storage backend must satisfy per SDK (bytes in / bytes out; seria
| Feature | Python | Rust | TypeScript | PHP |
| :--- | :---: | :---: | :---: | :---: |
| Circuit breaker | ✅ | ❌ | ✅ | ❌ |
| Backpressure | ✅ | | ⚠️ Refresh cap only — deliberate, decision recorded (LAB-519) | ❌ |
| Backpressure | ✅ | ✅ Semaphore + bounded queue, decision recorded (LAB-729) | ⚠️ Refresh cap only — deliberate, decision recorded (LAB-519) | ❌ |
| Distributed locking | ✅ Redis + SaaS backends | ✅ Redis + SaaS (`LockableBackend`; LAB-426. Workers lacks an impl) | ✅ Redis + SaaS backends; wired into `wrap()` cold miss (opt-in `stampede.distributedLock`, LAB-519) | ❌ |
| L1/L2 dual-layer cache | ✅ | ✅ moka (native) / `l1` feature | ✅ | ❌ |
| Cache stampede prevention | ✅ | ❌ | ✅ Cold-miss single-flight + SWR version tokens (LAB-519) | ❌ |
Expand All @@ -136,6 +136,8 @@ The contract a storage backend must satisfy per SDK (bytes in / bytes out; seria
> **Lock id transport (CWE-532):** the unlock call carries the lock capability token in the `X-CacheKit-Lock-Id` request header, never the `?lock_id=` query string (which leaks via access/proxy logs and OTel `http.url` spans). **Migration complete in all three SDKs** (verified 2026-07-20, LAB-273): Python (#131, closed), Rust (#24, closed), TypeScript ships the header (ts#63 remains open only for an unrelated NAPI-rebuild item). SaaS dual-reads both during the rollout window. See [spec/saas-api.md](spec/saas-api.md#delete-v1cachekeylock).
>
> **TypeScript backpressure decision (LAB-519):** general admission control beyond L1's `maxConcurrentRefreshes` was evaluated and declined. On Node's single-threaded event loop concurrent misses don't compete for threads, cold-miss single-flight collapses the per-key herd (the amplification vector metered-misses punishes), and distinct-key miss floods are bounded by backend timeouts plus the circuit breaker — a global miss semaphore would add queueing latency and a tuning knob without a failure mode it prevents. Revisit only with evidence of backend connection exhaustion. Full rationale on `StampedeConfig` in cachekit-ts.
>
> **Rust backpressure decision (LAB-729):** built — parity with Python's `reliability/load_control.py`, not net-new. The LAB-519 event-loop rationale does not transfer: Node's single event loop bounds nothing here either, but ts declined on herd-shape grounds specific to its stack, while in cachekit-rs a tokio runtime (multi-threaded or single-threaded `unsync` alike) will happily hold unbounded concurrent backend ops in flight — `fred`'s single multiplexed Redis connection buffers unbounded in-flight commands (caller memory), and the `reqwest` SaaS client grows per-host connections without cap (socket/FD exhaustion) — exactly the failure modes backpressure sheds. Shape: `tokio::sync::Semaphore` as the outermost layer of the `ReliableBackend` stack (`backpressure(breaker(retry(op)))`) — one permit per logical operation held across the whole retry sequence, so retry amplification is bounded and shed calls never skew breaker state. Over-limit calls wait in a bounded queue (max 1 000) up to 100 ms, then fail with the typed, non-retryable `BackendErrorKind::Backpressure` — never an unbounded silent queue. Defaults mirror Python (100 concurrent / 1 000 queued / 100 ms); on in `production`/`encrypted`/`io` presets, off in `minimal`. Divergence from Python: rejections are one typed non-retryable kind rather than Transient/Timeout — immediately retrying a shed call would re-amplify the overload being shed.

> [!NOTE]
> **TTL management is per-backend.** "TTL inspection/refresh" means the `TTLInspectableBackend`
Expand Down
Loading