-
Notifications
You must be signed in to change notification settings - Fork 2
Cacher WorkersKV
Cache engine backed by a Cloudflare Workers KV namespace.
Runtime note:
WorkersKVCacherneeds a KV namespace binding, which only a Worker (or Miniflare) provides. The class imports on every runtime, and there is no REST fallback for Deno, Bun or Node. UseREDISorMEMCACHEDthere instead.
WorkersKVCacher stores entries in the KV namespace bound to your Worker. It
is shared by every isolate and data centre, needs no server of its own, and
serves reads from Cloudflare's edge cache.
KV is eventually consistent. A write or delete is visible at once in the data centre that made it, and can take 60 seconds or more to reach the others. That suits read-through caches, such as norm's read cache, and data that changes rarely. It does not suit state that must be revoked everywhere at once.
| Feature | Supported |
|---|---|
| No external dependencies | ✅ |
| Shared across processes | ✅ |
| TLS / SSL support | n/a |
| Sliding (window) expiry | ❌ |
| Per-entry custom TTL | ✅* |
| Namespace isolation | ✅ |
* 0 (no expiry) or at least 60 seconds.
Deno:
deno add @tundralibs/cacherBun:
bunx jsr add @tundralibs/cacherNode.js:
npx jsr add @tundralibs/cacherBind a KV namespace to the Worker in wrangler.jsonc:
| Option | Type | Default | Description |
|---|---|---|---|
binding |
WorkersKVNamespace |
required | The KV namespace from the Worker's env
|
defaultExpiry |
number |
300 |
Default TTL in seconds: 0 (no expiry) or 60–2592000 |
WorkersKVNamespace is the part of Cloudflare's KVNamespace the engine
calls: get, put and delete. The binding on env satisfies it, so cacher
needs no dependency on @cloudflare/workers-types.
Throws CacherEngineError:
-
CONFIG_MISSINGwithout abinding. -
CONFIG_INVALIDifbindinglacksget,putordelete, ordefaultExpiryis between 1 and 59.
All methods are inherited from AbstractEngine. See
Cacher-Engines. set() throws
OPERATION_INVALID_PARAMS for window: true or an expiry between 1 and 59,
before anything is written.
import { Cacher, type WorkersKVNamespace } from '@tundralibs/cacher';
type Env = { CACHE: WorkersKVNamespace };
export default {
async fetch(_req: Request, env: Env): Promise<Response> {
const cache = Cacher.create('WORKERS_KV', 'pages', {
binding: env.CACHE,
defaultExpiry: 600,
});
const hit = await cache.get<{ title: string }>('home');
if (hit) return Response.json(hit);
const page = { title: 'Home' };
await cache.set('home', page);
return Response.json(page);
},
};import { WorkersKVCacher } from '@tundralibs/cacher/engines';
import type { WorkersKVNamespace } from '@tundralibs/cacher';
declare const env: { CACHE: WorkersKVNamespace };
const cache = new WorkersKVCacher('sessions', { binding: env.CACHE });
await cache.set('session:abc', { userId: 42 }, { expiry: 3600 });-
Consistency.
delete()andclear()follow the same propagation as writes. A reader in another data centre can see the old entry for up to 60 seconds. -
Expiry. KV rejects an
expirationTtlbelow 60 seconds, so the engine rejectsexpirybetween 1 and 59 rather than rounding it. - No window mode. KV cannot extend a TTL without rewriting the value, so a sliding expiry would turn every read into a write.
-
One write per second per key. KV rate-limits a key written more often
than that. The failure surfaces as
OPERATION_FAILED, with KV's error ascause. -
clear(). KV cannot delete a namespace's keys through a binding, soclear()writes a new random version to{name}:__ns_version__. Data keys are{name}:v{version}:{key}, and the old ones become unreachable. They stay stored until their own TTL expires, so an entry written withexpiry: 0stays until you delete it by other means, for example withwrangler kv key delete. Each instance re-reads the version at most once per second. -
Keys. KV caps a key at 512 bytes. A
{key}that would push the stored key past that is replaced by its SHA-256 digest, and a{name}longer than 300 bytes is too. -
has()reads the value, since KV has no existence check.
{ "kv_namespaces": [{ "binding": "CACHE", "id": "<namespace-id>" }] }