diff --git a/.changeset/prefer-uncached-hyperdrive-after-write.md b/.changeset/prefer-uncached-hyperdrive-after-write.md new file mode 100644 index 0000000000..84e47ed6e4 --- /dev/null +++ b/.changeset/prefer-uncached-hyperdrive-after-write.md @@ -0,0 +1,6 @@ +--- +"emdash": patch +"@emdash-cms/cloudflare": patch +--- + +Fixes anonymous public pages reseeding edge/object caches with stale Hyperdrive query results right after content publishes. When `cachedBinding` is set, public reads prefer the uncached Hyperdrive binding for a short window after content writes (default 60s, overridable via `preferUncachedAfterWriteMs` to match your Hyperdrive max_age). diff --git a/docs/src/content/docs/deployment/database.mdx b/docs/src/content/docs/deployment/database.mdx index 0c6ff69052..eca8e89341 100644 --- a/docs/src/content/docs/deployment/database.mdx +++ b/docs/src/content/docs/deployment/database.mdx @@ -307,11 +307,14 @@ wrangler hyperdrive create emdash-db \ ### Configuration -| Option | Type | Default | Description | -| --------------- | -------- | ------------- | -------------------------------------------------------------------- | -| `binding` | `string` | `"HYPERDRIVE"` | Primary (caching-disabled) Hyperdrive binding name | -| `cachedBinding` | `string` | — | Optional caching-enabled binding for anonymous reads (see below) | -| `max` | `number` | `5` | Max size of the in-Worker connection pool to Hyperdrive | +| Option | Type | Default | Description | +| ------------------------------ | -------- | -------------- | ---------------------------------------------------------------------------------------------------------------- | +| `binding` | `string` | `"HYPERDRIVE"` | Primary (caching-disabled) Hyperdrive binding name | +| `cachedBinding` | `string` | — | Optional caching-enabled binding for anonymous reads (see below) | +| `preferUncachedAfterWriteMs` | `number` | `60000`\* | After a content publish, prefer `binding` for this many ms on anonymous public reads (match Hyperdrive `max_age`) | +| `max` | `number` | `5` | Max size of the in-Worker connection pool to Hyperdrive | + +\*Default `60000` applies only when `cachedBinding` is set; ignored otherwise. ### Serving anonymous reads from cache @@ -343,7 +346,7 @@ database: hyperdrive({ binding: "HYPERDRIVE", cachedBinding: "HYPERDRIVE_CACHED" This is the [two-configuration pattern](https://developers.cloudflare.com/hyperdrive/configuration/query-caching/#disable-caching) Cloudflare documents for caching. EmDash decides which binding to use per request: -- **Anonymous reads of public-site paths** (`GET`/`HEAD`, no session, not under `/_emdash`) → cache-enabled `cachedBinding`. +- **Anonymous reads of public-site paths** (`GET`/`HEAD`, no session, not under `/_emdash`) → cache-enabled `cachedBinding`, **except** for a short window after a content publish (default 60s; set `preferUncachedAfterWriteMs` to your Hyperdrive `max_age`) when EmDash prefers the uncached `binding` so a rebuild cannot reseed edge/object caches from still-stale Hyperdrive results. - **Authenticated requests** (editors, authors) → uncached `binding`. - **Writes** (`POST`, `PUT`, `DELETE`, including anonymous ones) → uncached `binding`. - **Any request under `/_emdash`** (admin, setup, auth, internal APIs), even an anonymous `GET` → uncached `binding`. @@ -357,7 +360,7 @@ This is the [two-configuration pattern](https://developers.cloudflare.com/hyperd