Skip to content

feat: Memcached + File backends (Node-only subpath exports) (LAB-430)#76

Merged
27Bslash6 merged 3 commits into
mainfrom
lab-430-ts-memcached-file-backends
Jul 24, 2026
Merged

feat: Memcached + File backends (Node-only subpath exports) (LAB-430)#76
27Bslash6 merged 3 commits into
mainfrom
lab-430-ts-memcached-file-backends

Conversation

@27Bslash6

Copy link
Copy Markdown
Contributor

Summary

Implements the two missing backends that bring the (Node-runtime) TypeScript backend menu to parity with Python's — backend-agnostic is a core CacheKit principle, and until now a Node user of cachekit-ts had a smaller menu than a Python user for no principled reason.

FileBackend (backends/file.ts) — implements Backend + TTLBackend

  • On-disk compatible with cachekit-py's File backend: filenames are blake2b(key, digestSize=16) hex (32 chars, flat layout) and every file carries the same 14-byte header (magic CK, version, flags, u64 BE expiry; 0 = never expire) — py and ts processes can share a cache directory. Pinned by python-generated golden vectors in the unit tests (filename hashes for 3 keys incl. unicode, plus full py-struct-packed file images parsed by ts, plus ts-written headers verified field-by-field).
  • Atomic writes (temp + fsync + rename), expired/corrupt entries unlinked on read, O_NOFOLLOW on every open, orphaned-temp-file sweep on init, 10-year TTL ceiling, maxValueBytes guard (100 MiB default, matching py).
  • getTTL/refreshTTL read/rewrite the on-disk expiry header in place (payload untouched → cross-SDK readers stay compatible). refreshTTL throws on ttl <= 0 per the ts-wide TTLBackend contract (deliberate divergence from py's refresh_ttl(0) = permanent — zero-semantics stay uniform across ts backends).
  • Documented deltas from py: no LRU size eviction (LAB-430 scoped parity to directory layout + TTL semantics; cap growth with TTLs) and no flock (Node is single-threaded per process; cross-process safety comes from atomic rename).

MemcachedBackend (backends/memcached.ts) — implements Backend

  • Built on memjs (binary protocol, multi-server) — not a hand-rolled protocol.
  • Capability surface matches py exactly: base backend + directly-callable refreshTTL (via touch, trivially free), deliberately not TTLBackend — the memcached protocol cannot read a key's remaining TTL, so getTTL cannot exist and capability checks correctly exclude it.
  • 30-day TTL clamp (larger values are read as unix timestamps by the protocol), client-side 1 MiB item-size guard that fails loudly, GET-based exists, client-side keyPrefix exposed for the interop fail-closed guard.

Dependency boundaries (acceptance criterion)

  • Both backends live behind subpath exports (@cachekit-io/cachekit/backends/file, …/backends/memcached) and are not re-exported from the root index — verified that dist/index.js's module graph has no reference to either module or memjs.
  • memjs is an optional peerDependency (dev-dep for tests), loaded lazily via dynamic import on first use with a clear ConfigurationError if missing. Config interfaces are exported type-only from the root.

Tests

  • 59 unit tests: File against the real filesystem (tmpdir) including the py golden vectors; Memcached against a documented protocol-faithful memjs mock (the in-CI strategy — no memcached server needed for pnpm test).
  • 9 integration tests against real memcached 1.6 via Testcontainers (same docker-availability skip pattern as the Redis suite) — green locally.
  • Full suite: 615 passed / 1 pre-existing skip. Lint, format, type-check, build all green. Verified end-to-end: createCache({ backend: file({...}) }) round-trips through the full stack (L1 + MessagePack + reliability) on the built dist, both ESM and CJS subpaths load.

Docs (matrix + README updated in sibling PRs)

  • Package README: new Backends section (menu table + Memcached/File usage and semantics).
  • protocol/sdk-feature-matrix.md and docs.cachekit.io updated in sibling LAB-430 PRs (ts Memcached/File cells → ✅ with module evidence; stale LAB-427/LAB-446 capability cells refreshed).

Closes LAB-430.

…-430)

Two new backends bring the (Node-runtime) TypeScript backend menu to
parity with Python's, per the backend-agnostic core principle:

- backends/file.ts — FileBackend implements Backend + TTLBackend.
  On-disk compatible with cachekit-py's File backend: blake2b(key,
  digestSize=16) hex filenames and the same 14-byte header (magic,
  version, flags, u64 BE expiry), pinned by python-generated golden
  vectors in the tests, so py and ts can share a cache directory.
  Atomic write-to-temp/fsync/rename, expired+corrupt entries unlinked
  on read, O_NOFOLLOW everywhere. Deliberate deltas from py (documented
  in the class doc): no LRU size eviction, no flock.

- backends/memcached.ts — MemcachedBackend implements Backend via memjs
  (binary protocol, multi-server). Matches py's capability choices:
  base backend + directly-callable refreshTTL (touch), deliberately NOT
  TTLBackend because the protocol cannot read a key's remaining TTL.
  30-day TTL clamp, client-side 1 MiB item-size guard, GET-based
  exists, client-side keyPrefix exposed for the interop guard.

Dependency boundaries: both backends live behind subpath exports
(./backends/file, ./backends/memcached) and are NOT re-exported from
the root index; memjs is an optional peerDependency loaded lazily on
first use. Browser/edge bundles importing the package root never see
memjs or node:fs (verified: dist/index.js graph has no reference).

Tests: 59 unit tests (File against real fs incl. py golden vectors;
Memcached against a documented protocol-faithful memjs mock) plus a
Testcontainers integration suite run against real memcached 1.6
(9 tests, green locally). Config types exported type-only from the
root for discoverability.

Co-authored-by: multica-agent <github@multica.ai>
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e43b5f90-0a78-4ee8-b1b1-2cdcf095432c

📥 Commits

Reviewing files that changed from the base of the PR and between d70d225 and 885e3b1.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • README.md
  • packages/cachekit/README.md
  • packages/cachekit/package.json
  • packages/cachekit/src/backends/file.test.ts
  • packages/cachekit/src/backends/file.ts
  • packages/cachekit/src/backends/memcached.test.ts
  • packages/cachekit/src/backends/memcached.ts
  • packages/cachekit/src/backends/types.ts
  • packages/cachekit/src/index.ts
  • packages/cachekit/test/integration/memcached-backend.integration.test.ts
  • packages/cachekit/test/protocol/fixtures/interop-mode.json
  • packages/cachekit/test/protocol/interop-mode.protocol.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lab-430-ts-memcached-file-backends

Comment @coderabbitai help to get the list of available commands.

@27Bslash6 27Bslash6 changed the title LAB-430: Memcached + File backends (Node-only subpath exports) feat: Memcached + File backends (Node-only subpath exports) (LAB-430) Jul 23, 2026
Resolve against the LAB-595 entrypoint split: the Memcached/File config
type exports land in index.ts's Node-only section (the backends stay
behind subpath exports, unchanged), package.json keeps both new export
maps (./backends/* from this branch, ./workers from main), and
pnpm-lock.yaml is regenerated from main's lockfile plus this branch's
memjs/@types-memjs additions.
@27Bslash6

Copy link
Copy Markdown
Contributor Author

Un-dirtied per LAB-761: merged main (the LAB-595 entrypoint split) into this branch. The Memcached/File config type exports moved into index.ts's Node-only section (backends stay behind subpath exports, unchanged), package.json keeps both new export maps (./backends/* + ./workers), and the lockfile was regenerated from main's plus the memjs additions.

Verified locally: type-check, lint, format, full vitest (620 passed incl. memcached/file suites), and check:workers-bundle (no node:fs/memjs leak into the workers graph). Note for the merge conductor: after #77/#75 land, this branch may need a trivial index.ts re-merge — nothing semantic.

Co-authored-by: multica-agent <github@multica.ai>
@27Bslash6
27Bslash6 merged commit e22928d into main Jul 24, 2026
16 checks passed
@27Bslash6
27Bslash6 deleted the lab-430-ts-memcached-file-backends branch July 24, 2026 16:12
27Bslash6 pushed a commit that referenced this pull request Jul 24, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>cachekit: 0.1.4</summary>

##
[0.1.4](cachekit-v0.1.3...cachekit-v0.1.4)
(2026-07-24)


### Features

* Cloudflare Workers entrypoint on wasm32 cachekit-core (LAB-595)
([#78](#78))
([d70d225](d70d225))
* Memcached + File backends (Node-only subpath exports) (LAB-430)
([#76](#76))
([e22928d](e22928d))
</details>

<details><summary>cachekit-core-wasm: 0.1.1</summary>

##
[0.1.1](cachekit-core-wasm-v0.1.0...cachekit-core-wasm-v0.1.1)
(2026-07-24)


### Features

* Cloudflare Workers entrypoint on wasm32 cachekit-core (LAB-595)
([#78](#78))
([d70d225](d70d225))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: cachekit-release-bot[bot] <247960786+cachekit-release-bot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant