Skip to content

PLANETSCALE 2026-02-19

Read original ↗

Faster PlanetScale Postgres connections with Cloudflare Hyperdrive

Summary

A demo-app narrative post from PlanetScale (Simeon Griggs, 2026-02-19) walking through the architectural decisions behind a real-time prediction-market demo built on PlanetScale Postgres Metal + the full Cloudflare edge stack: Workers for compute, Hyperdrive for Postgres connection pooling + query caching, Durable Objects as a single-instance coordinator, and WebSockets for fan-out of live price updates to all connected browsers. Importantly a tutorial-framed post that nevertheless makes four load-bearing architectural decisions explicit: (1) Workers not Durable Objects are the write path (DOs are single-threaded single-location, bad for writes); (2) Postgres is the source of truth, WebSockets are the low-latency notification channel"updates are immediate most of the time, and eventually correct all of the time"; (3) no smart placement is used — the Worker stays user-adjacent, not DB-adjacent, with Hyperdrive's pooled/cached connection shielding the user from the DB-origin RTT tax; (4) production hardening deliberately deferred (no WebSocket replay-on-reconnect, no queue-backed fan-out, no polling reconciliation) to keep the demo on the fastest path to a working real-time arch.

Key takeaways

  1. DO-not-writer-path decision is the first non-obvious architectural call. "If we're going to use a Durable Object to broadcast updates over WebSockets, it may be tempting to make the Durable Object the write path to the database. However, this negatively impacts performance. Durable Objects are single-threaded and hosted in a single location, making them a bad candidate for the write path. Instead the Workers will send transactions to the database via the Hyperdrive connection." The DO is the notification fan-out actor only; writes go around it. This is the canonical wiki instance of patterns/single-region-do-fanout-from-distributed-writers.

  2. Postgres is authoritative; WebSockets are fast. "One useful principle in real-time apps is to decide what is authoritative and what is just fast. In this architecture, Postgres is the source of truth and WebSockets are the low-latency notification layer. That distinction matters. WebSocket messages can be delayed or dropped due to normal network behavior, but database writes are still durable. So the contract for clients should be: 'updates are immediate most of the time, and eventually correct all of the time.'" Canonical wiki statement of concepts/authoritative-vs-fast-notification and the shape of patterns/db-authoritative-with-websocket-notify.

  3. Smart-placement trade-off is explicit both ways. "Through 'smart placement' the Worker can be moved to a single location, moving latency between the user and the Worker while greatly reducing the latency to the database. In this application I've chosen not to use smart placement and keep the Worker closer to every user." For this application the default user-adjacent placement wins because Hyperdrive's pooled/cached connection already collapses most of the edge-to-origin DB latency. Decision rule from the post: "Monitor your own application's performance and needs closely to see if keeping the connection between Worker and database is more useful than between Worker and user."

  4. Stale-quote rejection at the database layer. Because the prediction-market option prices are volatile, the application enforces correctness at the database level: "option purchases won't proceed if the purchase price as seen by the user is not a valid price at the time the transaction is written to the database." Each client request carries "expected price/version data and slippage tolerance, so the backend can reject stale quotes while successful writes immediately broadcast over WebSockets to every connected browser." Canonical wiki instance of concepts/stale-quote-rejection.

  5. Transaction → DO ping → WebSocket fan-out. The write path is: Worker receives client request (with expected price + slippage) → Worker sends transaction to Postgres via Hyperdrive connection → transaction completes → Worker "pings the Durable Object via the WebSocket connection to fan out that update to all other connected browsers." The single DO coordinates the WebSocket fan-out only — it holds no authoritative state, only the list of open WebSocket connections.

  6. Hyperdrive = two-component architecture surfaced explicitly. The post's decomposition of Hyperdrive is worth the citation: "The edge component runs globally from within every Cloudflare data center and prepares the 7 round-trip steps of creating a connection to your database within the Cloudflare global network. The connection pool is enabled physically close to your database and maintains warm connections to Postgres which are automatically given to incoming requests." The edge-side pre-negotiates the TLS/handshake sequence; the origin-side keeps warm pooled connections. Two physical tiers, not one monolithic proxy.

  7. Deferred production hardening is itemised, not hand-waved. "To keep this guide focused on principles and direction, I left out several production hardening layers which could've also been implemented. Replay on reconnect: A reconnecting client could ask for events since a known cursor. We skipped this for simplicity. Queue-backed fanout: A queue can improve durability and retries between write completion and broadcast. We skipped this to avoid extra moving parts. Cloudflare has a Queueing service, too! Polling reconciliation: Periodic polling can catch any missed updates from WebSockets. We noted it earlier, but didn't implement it in the demo." Three concrete next-steps (cursor-based replay, Cloudflare Queues for fan-out durability, polling reconciliation) that together upgrade the fast-notification channel from "immediate most of the time" to stronger delivery guarantees.

  8. Single-DO-scaling caveat explicitly flagged. "A lot of what I built for this demonstration is about global distribution. However, I'm still relying on a single Durable Object to send WebSocket updates to all users. Cloudflare's own documentation gives guidance that you can scale Durable Objects horizontally — sharded with a key — should you come close to exhausting their allocated resources." One DO is the simplest shape; sharding by key (e.g. per-market DO) is the horizontal-scaling axis when connection count or broadcast rate exceeds a single- instance budget.

  9. Multi-environment branching via PlanetScale branches + wrangler env. The post explicitly calls out that defaults-first development will bite later: "When building there's a temptation to build everything as fast as possible using the defaults. But planning now for multiple environments will help you avoid surprises later." Pattern: create a PlanetScale branch for dev, configure its connection string in a wrangler.jsonc env.development.hyperdrive block, point the local dev server at it via CLOUDFLARE_ENV=development. Production Workers use the main branch DB; local dev uses the dev branch DB.

  10. AI-authored code posture. The opening framing is itself a disclosure: "You may note an absence of 'how to' code snippets... I'm anticipating that you're building by describing what you want to an LLM, and so this blog post is describing what I did (and didn't) do." PlanetScale's agent-skills package (database-skills.com) is pitched as the correctness discipline for AI-generated database code, with Query Insights + PlanetScale MCP server closing the loop at query-tuning time.

Systems extracted

  • PlanetScale Postgres Metal — canonical customer of the architecture. Smallest Metal DB ($50/month) used for the demo; "PlanetScale Metal databases are powered by blazing-fast, locally-attached NVMe SSD drives instead of network-attached storage". Also called out: the $5/month non-Metal tier "could be an option for this demo too".
  • Cloudflare Hyperdrive — edge-global-pool + origin-co-located-warm-pool architecture described in new detail; 7-round-trip-step TLS/handshake pre-negotiation is the edge-component's job.
  • Cloudflare Workers — write path for DB transactions; user-adjacent placement by default.
  • Cloudflare Durable Objects — WebSocket fan-out coordinator, NOT the write path (load-bearing architectural decision).
  • Cloudflare WebSockets — fast notification layer between DO and connected browsers.
  • Wrangler — Cloudflare CLI hosting the wrangler.jsonc config with Hyperdrive binding + env blocks for multi-environment Postgres branches.
  • Query Insights — the post-load tuning surface; "you can access these insights from the PlanetScale dashboard or the CLI. Or better yet, with the PlanetScale MCP server, you can just ask your LLM."

Concepts extracted

  • concepts/edge-to-origin-database-latency — the hazard Hyperdrive's two levers (pooling → reduce RTT count, placement → reduce RTT cost) exist to mitigate. This post adds the counter-example: sometimes the default Workers placement (user-adjacent) is the right choice, when Hyperdrive's pooling/caching is already closing the gap and the user-to-Worker leg matters more for UX.
  • concepts/authoritative-vs-fast-notification (NEW) — "decide what is authoritative and what is just fast". DB is source of truth; WebSocket is low-latency notification; client contract is "immediate most of the time, eventually correct all of the time".
  • concepts/stale-quote-rejection (NEW) — request carries expected-price + slippage; backend rejects if write-time price is invalid. Prevents acting on a stale WebSocket broadcast.
  • concepts/connection-pool-exhaustion — the motivating hazard Hyperdrive solves; Workers' short-lived, highly parallel isolates would otherwise open a fresh Postgres connection per request.
  • concepts/single-writer-assumption — the DO's single-threaded single-location property that makes it a bad fit for the write path is the same property that makes single-writer-only systems tractable elsewhere; here it's a negative constraint for writes, a positive one for fan-out coordination.
  • concepts/network-attached-storage-latency-penalty — the reason the post name-drops NVMe SSD for Metal: latency of network-attached block storage would undermine the rest of the real-time pitch.

Patterns extracted

Operational numbers

  • $50/month — smallest PlanetScale Metal cluster used for the demo.
  • $5/month — non-Metal Postgres tier also viable for the demo.
  • 7 round-trip steps — the TLS/handshake sequence the edge-component of Hyperdrive pre-negotiates at Cloudflare POPs, before handing off to the origin-co-located warm pool.
  • "Single digit milliseconds" — forward-looking Cloudflare target for multi-query request paths once auto-placement from DB region ships (referenced but not measured here).
  • 330+ Cloudflare POPs — the global-network premise the Worker-at-user-POP argument rests on.
  • "~50ms of 95% of the world's internet-connected population" — Cloudflare global network positioning line reproduced in the post.

Caveats

  • Demo architecture, not production. Three hardening layers explicitly omitted: replay-on-reconnect, queue-backed fanout, polling reconciliation. The post names Cloudflare Queues as the natural fit for the second.
  • Single-DO-for-broadcast is a known scaling cliff. Sharding by key (e.g. per-market DO) is the named horizontal-scaling lever.
  • Smart placement decision is workload-dependent. The post takes the "keep Worker user-adjacent" choice for this demo (multi-market UX, one-or-a-few queries per user interaction, Hyperdrive absorbing per-query RTT cost) but explicitly says "your mileage may vary".
  • No WebSocket reconnection logic in the demo — so dropped messages are lost, not replayed. The client contract "eventually correct all of the time" is aspirational in the demo code; replay-on-reconnect is the work item that would deliver it.
  • Writer-observes-its-own-write faster than other clients. "The user that sends the transaction will get their feedback faster than users who are only listening. It's a small trade-off I'm considering acceptable in this demo." Non-uniformity of real-time update delivery is a known (accepted) artefact of the DO-ping-after-commit path.
  • Post does not disclose: DO CPU / memory budget under broadcast load, Hyperdrive pool size / per-tenant connection budget, Hyperdrive query-cache invalidation policy or staleness semantics, Metal cluster sizing curve, WebSocket connection count ceiling per DO.

Source

Last updated · 347 distilled / 1,201 read