Cache and consistency

Use query caching only where stale is safe

Identify eligible reads and understand that writes do not automatically invalidate Hyperdrive query cache entries.

8 minute lesson

~~~

Hyperdrive can cache eligible non-mutating query results for a configured time. It parses each query, decides whether it reads or writes, and for reads it may store the response. Matching reads can then return near the Worker without reaching the origin database at all.

The defaults: cached results live for a max_age of 60 seconds, plus a 15-second stale_while_revalidate window where Hyperdrive serves the old result while refreshing in the background. You can raise max_age to one hour, or tune it down.

Here is the property that decides everything: the cache does not automatically invalidate when your application writes. An UPDATE goes straight to the origin, but a cached SELECT of the same row keeps returning the old value until its age expires. That makes caching wrong for any read that must immediately show the new value.

Two configurations, one database

The pattern for mixed workloads is a second, cache-disabled configuration against the same database:

npx wrangler hyperdrive create app-db-fresh \
  --connection-string="$DATABASE_URL" --caching-disabled

Bind both and route each read to the right one:

{
  "hyperdrive": [
    { "binding": "HYPERDRIVE", "id": "57b7076f58be42419276f058a8968187" },
    { "binding": "HYPERDRIVE_FRESH", "id": "a1c9330bb2ae4d62a1a0d1b7b1b8f3c2" }
  ]
}
// popular, staleness-tolerant reads
const catalog = postgres(env.HYPERDRIVE.connectionString)

// auth, permissions, reads right after a write
const fresh = postgres(env.HYPERDRIVE_FRESH.connectionString)

The cache-disabled path still gets connection pooling and fast connection setup, so you lose nothing except the cache — which is exactly what you want for fresh reads.

One quirk worth knowing: Hyperdrive detects uncacheable functions like NOW() or RANDOM() by text matching, even inside SQL comments. A stray -- NOW() comment silently makes a query uncacheable.

Classify before you configure

Take three reads and classify each as cached or fresh: a public catalog, an account balance after a transfer, and a dashboard summary.

The catalog caches happily — a product description sixty seconds old harms nobody. The account balance after a transfer must be fresh; a user who just moved money and still sees the old number will file a support ticket, or worse, retry the transfer. The dashboard summary is a judgment call: pure reporting tolerates a minute of lag, but if an action button reads it, treat it as fresh. When in doubt, start uncached and add caching where load appears.

Lesson completed

Take this course offline

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

Get the download library →