Skip to content

Data

Key-value storage

Cloudflare KV, an eventually consistent key-value store we use for sessions, caches and lookups read on every request.

Workers KV is a key-value store built for reads. A write goes to central storage and then propagates outwards; a read is served from the cache at whichever location handled the request, so the second read of a popular key anywhere in the world is fast and cheap. The trade is written on the tin: it is eventually consistent, and every design decision follows from that.

How we use it

Jonosakti keeps sessions and cached responses there, so a signed-in request validates without touching the database. ClassProfile uses it for per-IP and per-user rate limiting on the authentication routes as well as for caching. @Heartbeat holds sessions and feed caches in it, and the admin dashboard has a screen for inspecting and clearing KV entries, which is an honest admission that a cache eventually needs clearing by hand. Objectify and DMARC Engine use it for the same class of value: read constantly, written rarely, and survivable if briefly stale.

What it costs you

A write can take up to about a minute to be visible everywhere. Write a value and read it back from a different location and you can get the old one, or nothing. Misses are cached too, so the write-then-immediately-read pattern is the one that catches people, and the fix is to keep the fresh value in memory for the rest of the request rather than reading it back.

There are no atomic operations. Read, modify, write loses updates under concurrency, which means counters, quotas and anything with an exact limit are wrong on KV by construction. Our rate limiting there is deliberately approximate: it stops one script hammering one location, and it will not hold a precise global limit against traffic spread across many.

The limits are worth knowing before you design around them. Keys are capped at 512 bytes, values at 25 MiB, and sustained writes to a single hot key are limited to roughly one per second, so a single counter key is both incorrect and throttled. Listing keys is paginated and slow enough that it belongs in admin tooling, not a request path.

Billing is per operation. A request that reads three KV keys is three reads, every time, and a page that fans out to a dozen small keys costs more than one D1 query returning the same data. Cold keys are the other half of that: the first read at a location that has not seen the key goes to central storage, so the fast number quoted for KV is the cached case.

When we would choose something else

Anything that needs a correct count, a lock or a strict order goes to a Durable Object, which gives you a single writer and immediate consistency at the price of one object being a bottleneck. Anything you will ever want to filter, sort or join belongs in SQLite and D1, because a key-value store cannot answer a question you did not anticipate when you chose the key. On a conventional server stack the same jobs go to Redis, which is atomic and fast but has to be running somewhere and paid for.

Where we have used it

Every build below lists this in its stack, so the claim is checkable.