Consistency and patterns

Choose KV patterns that fit

Use KV for cacheable configuration and preferences while avoiding correctness-critical locks, quotas, and hot counters.

8 minute lesson

~~~

Good KV patterns share one property: a brief stale read does not violate a hard rule. That single test sorts almost every design decision.

Public settings, feature configuration, derived responses, and user preferences fit. If a preference change lands 30 seconds late in another region, nothing breaks and nobody notices.

A strict rate limit, an inventory decrement, a uniqueness check, or a distributed lock does not fit KV alone. Each of those has a hard rule — never over the limit, never below zero, never two of the same — and stale reads plus non-atomic writes break hard rules.

A worked example: rate limiting

I built rate limiting on KV for a real product, and it works because I chose a soft rule. The design counts requests in fixed time windows:

const window = Math.floor(Date.now() / 1000 / windowSeconds)
const kvKey = `rl:roast:${callerId}:${window}`

const count = parseInt(await env.CACHE.get(kvKey) ?? '0', 10)
if (count >= limit) return { allowed: false }

await env.CACHE.put(kvKey, String(count + 1), {
  expirationTtl: Math.max(60, windowSeconds),
})

The increment is not atomic. Two concurrent requests can both read 0 and both write 1, so a limit of five sometimes admits six. For “stop a bot from hammering a free endpoint,” that is acceptable — this is abuse protection, not a billing-grade quota. The same code applied to a paid API quota would be a bug.

The hybrid pattern

When part of the job needs correctness, split it. Use a Durable Object or a D1 transaction for the authoritative operation, then optionally publish a read-friendly result to KV:

// Durable Object decides, atomically
const decision = await stub.consumeQuota(userId)
// KV serves the cheap, global "current plan" read
const plan = await env.CONFIG.get(`plan:${userId}`, 'json')

The strong store owns the rule. KV owns the fan-out. You get atomicity where it matters and cheap global reads everywhere else, instead of forcing one store to do both jobs badly.

Now review the rate-limiting design above and mark each part as approximate or strict: the counter, the window rollover, the TTL cleanup, and the block decision. Then name which parts would need stronger coordination if the limit became contractual.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →