From ad499bd39c90f367ecd7a849974d268645a3cc9e Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Thu, 23 Jul 2026 23:42:28 +1000 Subject: [PATCH 1/2] docs(matrix): flip rs reliability rows for LAB-518; refresh stale LAB-426 lock/TTL cells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Circuit breaker, retry (new row), graceful degradation (new row), and cache stampede prevention flipped for Rust — shipped by cachekit-rs#43 (LAB-518). - rs distributed-locking and TTL cells were stale: LAB-426 already landed Redis + Workers LockableBackend/TtlInspectable impls (code-verified: redis.rs:165, workers.rs:262/322). Refreshed the cells and the backend-abstraction coverage note to match. - Rust SDK summary gains the reliability feature flag + tier bullet. Co-authored-by: multica-agent --- sdk-feature-matrix.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/sdk-feature-matrix.md b/sdk-feature-matrix.md index 320208c..748e22e 100644 --- a/sdk-feature-matrix.md +++ b/sdk-feature-matrix.md @@ -6,7 +6,7 @@ **Feature parity and compliance status across all CacheKit SDK implementations.** -*Last updated: 2026-07-23 — LAB-431 Workers feasibility spike: ts Cloudflare Workers footnote records GO verdict via wasm32 cachekit-core (~64 KB gz measured; WebCrypto as fallback), implementation tracked as LAB-595* +*Last updated: 2026-07-23 — LAB-518 rs reliability tier: circuit breaker, retry, graceful degradation, and stampede single-flight rows flipped to ✅ for Rust; rs distributed-locking/TTL cells refreshed for the already-landed LAB-426 Redis/Workers capability parity* @@ -112,7 +112,7 @@ The contract a storage backend must satisfy per SDK (bytes in / bytes out; seria > [!NOTE] > **Lock API shape divergence:** Python's `acquire_lock` is an async context manager yielding `bool` — the lock token stays internal and release is automatic. Rust and TypeScript return the raw `lock_id` capability token from `acquire_lock`/`acquireLock` and require an explicit `release_lock(key, lock_id)` — a direct mirror of the SaaS lock endpoint. All three pass the **bare cache key** (backends own any `:lock` namespace derivation). Porting a lockable backend across SDKs must bridge this shape difference. > -> **Coverage, not shape, is the parity gap:** all three SDKs use the same required-base + optional-capability pattern, but Redis optional-capability coverage varies by SDK: Python has the broadest coverage (both locking and TTL inspection), Rust's Redis backend implements TTL inspection only (no locking), and TypeScript's Redis backend implements neither. Rust's Workers backend also lacks both despite speaking the same SaaS API (gap tickets under LAB-102). +> **Coverage, not shape, is the parity gap:** all three SDKs use the same required-base + optional-capability pattern, but optional-capability coverage varies by SDK: Python and Rust now cover both locking and TTL inspection on all their backends (Rust closed its Redis-locking and Workers gaps in LAB-426), while TypeScript's Redis backend implements neither (gap tickets under LAB-102). --- @@ -120,12 +120,14 @@ The contract a storage backend must satisfy per SDK (bytes in / bytes out; seria | Feature | Python | Rust | TypeScript | PHP | | :--- | :---: | :---: | :---: | :---: | -| Circuit breaker | ✅ | ❌ | ✅ | ❌ | +| Circuit breaker | ✅ | ✅ `reliability` feature — on in `production`/`encrypted`/`io` presets, off in `minimal` (LAB-518) | ✅ | ❌ | +| Retry (exponential backoff + jitter) | ✅ | ✅ on transient/timeout classification (`is_retryable`), inside the breaker (LAB-518) | ✅ | ❌ | +| Graceful degradation | ✅ fail-open on `BackendError` | ✅ `#[cachekit]` runs the function uncached on backend failure; `secure` fails closed (LAB-518) | ✅ | ❌ | | Backpressure | ✅ | ❌ | ⚠️ Concurrent refresh limits | ❌ | -| Distributed locking | ✅ Redis + SaaS backends | ✅ SaaS backend only (`LockableBackend`; Redis/Workers lack impls) | ✅ SaaS backend only | ❌ | +| Distributed locking | ✅ Redis + SaaS backends | ✅ SaaS + Redis + Workers (`LockableBackend`, LAB-426) | ✅ SaaS backend only | ❌ | | L1/L2 dual-layer cache | ✅ | ✅ moka (native) / `l1` feature | ✅ | ❌ | -| Cache stampede prevention | ✅ | ❌ | ✅ Version tokens + background L1 refresh | ❌ | -| TTL management | ✅ Redis + SaaS | ✅ Redis + SaaS (`TtlInspectable`; Workers ❌) | ✅ SaaS only (`TTLBackend`) | ❌ | +| Cache stampede prevention | ✅ | ✅ cold-miss single-flight: per-key in-process lock + distributed fill lock on lock-capable backends (LAB-518) | ✅ Version tokens + background L1 refresh | ❌ | +| TTL management | ✅ Redis + SaaS | ✅ Redis + SaaS + Workers (`TtlInspectable`, LAB-426) | ✅ SaaS only (`TTLBackend`) | ❌ | | Stale-while-revalidate (server stale-grace) | 🚧 LAB-381 | ❌ | ❌ | ❌ | > **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). @@ -200,9 +202,10 @@ its spec: Rust SDK (cachekit-rs) - Published on crates.io as `cachekit-rs` v0.3.0 + `cachekit-macros` v0.3.0 -- Feature flags: `redis`, `cachekitio`, `encryption`, `l1`, `macros`, `workers` +- Feature flags: `redis`, `cachekitio`, `encryption`, `l1`, `macros`, `workers`, `reliability` - Backends: `RedisBackend` (fred), `CachekitIO` (reqwest), `WorkersCachekitIO` (CF Workers fetch) - L1 cache via moka (native only, `l1` feature) +- Reliability tier (`reliability` feature, native): retry with backoff + jitter on the `is_retryable` classification, circuit breaker (rolling window), macro-level graceful degradation (fail-open; `secure` fail-closed), cold-miss single-flight with distributed fill locks - `#[cachekit]` proc-macro for decorator-style caching - `SecureCache` for zero-knowledge encrypted caching - SSRF protection, credential redaction, `Zeroizing` key material From 79d4e580c61b1eb1503c042837d289dd933d0700 Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Fri, 24 Jul 2026 15:14:07 +1000 Subject: [PATCH 2/2] docs(matrix): scope rs fail-open to outage-class errors (LAB-518 panel follow-up) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Permanent/auth backend errors now propagate on the plain #[cachekit] path — a wrong API key fails loudly instead of silently running uncached forever. Cell wording updated to match the shipped behaviour. Co-authored-by: multica-agent --- sdk-feature-matrix.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk-feature-matrix.md b/sdk-feature-matrix.md index 748e22e..af361fd 100644 --- a/sdk-feature-matrix.md +++ b/sdk-feature-matrix.md @@ -122,7 +122,7 @@ The contract a storage backend must satisfy per SDK (bytes in / bytes out; seria | :--- | :---: | :---: | :---: | :---: | | Circuit breaker | ✅ | ✅ `reliability` feature — on in `production`/`encrypted`/`io` presets, off in `minimal` (LAB-518) | ✅ | ❌ | | Retry (exponential backoff + jitter) | ✅ | ✅ on transient/timeout classification (`is_retryable`), inside the breaker (LAB-518) | ✅ | ❌ | -| Graceful degradation | ✅ fail-open on `BackendError` | ✅ `#[cachekit]` runs the function uncached on backend failure; `secure` fails closed (LAB-518) | ✅ | ❌ | +| Graceful degradation | ✅ fail-open on `BackendError` | ✅ `#[cachekit]` runs the function uncached on outage-class failures (transient/timeout/circuit-open); permanent/auth errors propagate; `secure` fails closed on everything (LAB-518) | ✅ | ❌ | | Backpressure | ✅ | ❌ | ⚠️ Concurrent refresh limits | ❌ | | Distributed locking | ✅ Redis + SaaS backends | ✅ SaaS + Redis + Workers (`LockableBackend`, LAB-426) | ✅ SaaS backend only | ❌ | | L1/L2 dual-layer cache | ✅ | ✅ moka (native) / `l1` feature | ✅ | ❌ |