Skip to content

D1 read replication is documented but never enabled or used #337

Closed

Description

@Brooooooklyn

Summary

The docs promise "Database reads are fast everywhere via D1's read replication" (docs/guide/index), but nothing in void enables or uses it: databases are provisioned without read_replication, and void/db never calls withSession(), so every query — including per-request auth lookups — always hits the D1 primary.

Discovered while investigating production 503s on a void-deployed app: the dispatch worker races worker.fetch() against a 10s deadline, and sequential D1 round trips to an APAC-primary database push the p99 tail past it.

Evidence

  • platform/packages/api/src/provisioning/cloudflare-api.ts — createD1Database posts only {name}; no read_replication config.
  • packages/void/src/cli/provision.ts:4465 — wrangler d1 create <name> --binding ... --update-config with no --read-replication flag; nothing writes read_replication into d1_databases[] config.
  • packages/void/src/runtime/db.ts:60 — db proxy wraps drizzle(requireRuntimeBinding('DB')) on the raw binding. D1Database.withSession() / D1DatabaseSession.getBookmark() are never exposed.
  • packages/void/src/runtime/better-auth.ts:9 — resolveDatabase passes env.DB directly to better-auth, so auth's per-request session+user lookups can't use a replica session either.
  • Repo-wide, read_replication/withSession appear only in the docs claim; no code or tests reference them.

Impact

Every D1 query in every void app pays a round trip to the database's primary region. For APAC-primary databases serving global traffic that's ~800ms+ per hop; auth middleware alone does 2+ sequential hops per request.

Suggested approach

  1. Enable at provisioning: read_replication: { mode: "auto" } on createD1Database (platform API path) and wrangler d1 create --read-replication enabled / wrangler config emission (CLI path).
  2. Session API in void/db: expose a per-request D1 session, e.g. db.withSession("first-unconstrained") or a request-scoped drizzle instance via getRuntimeRequestResource (env.ts already has the plumbing). Drizzle's d1 driver only calls prepare/batch, which D1DatabaseSession supports — needs a type widening since drizzle() is typed against D1Database.
  3. Auth path: route resolveVoidAuthState / runVoidAuthMiddleware session lookups through a D1 session — biggest win, since every request's auth hops would hit the nearest replica with sequential consistency.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions