advanced 3 min answer Multiple choice

A live-auction product needs every bid on one lot ordered correctly and every watcher to see the same current price within 100 ms. Lots run for 90 seconds and draw up to 2000 watchers each; 400 lots can be live at once. The edge platform offers objects where exactly one instance per id exists worldwide and processes messages in order on a single thread. Which placement do you choose?

cloudflaredurable objectsserializationedge consistencycontention
Pick one
Show the full answer Hide the answer

The deciding property

Not latency, and not consistency in the abstract. The deciding property is the contention unit. Bids compete with other bids on the same lot and with nothing else; two bids on different lots have no ordering relationship at all.

That makes the lot the natural unit of both consistency and scale, and the primitive in the question hands you exactly that. Cloudflare's documentation for Durable Objects states that there is only ever one instance of a Durable Object with a given id running anywhere in the world, with all Workers making subrequests for that id routed to the same instance, and that the object processes messages in order and on a single thread, which is what allows coordination across messages. Cloudflare's general-availability post names its own Waiting Room as a user, keeping a strongly consistent view of how many users are waiting for a given site globally.

Why one object per lot

Single-threaded in-order execution per id is a serialization point you did not have to build. The "order the bids" problem becomes "the order the object received them". No lock, no compare-and-set loop, no retry storm. The object holds the current price in memory, validates a bid against it, writes to its own strongly consistent storage, and broadcasts to its watchers - all inside one turn.

The arithmetic works: 400 live lots is 400 objects, each with at most 2000 watchers. A bid is one message on one thread, so the 100 ms budget goes almost entirely on the network hop to wherever that object lives.

Why the other options fail

  • One object for the whole service. The right mechanism at the wrong granularity, and the most common mistake with this primitive. Every bid on every lot now queues behind a single thread, so your global throughput ceiling is one object's throughput, and a hot lot starves the other 399. You have rebuilt a single-threaded server and paid edge prices for it.
  • Eventually consistent key-value with last-writer-wins. Correct for a read-mostly value nobody competes over - a regional price list, a flag set. For a bid it loses money: two bids landing at different locations converge on whichever carries the later timestamp, which is not the higher bid, and the loser is told they won. The failure surfaces as a disputed invoice.
  • Regional row with optimistic concurrency. Defensible, and correct at lower contention. Here each bid is a cross-region round trip and each conflict adds another, so with 2000 watchers racing inside a 90-second lot the retry rate climbs exactly when the 100 ms budget is tightest.

What it costs

One thread per lot is a hard ceiling, not a soft one. A celebrity lot generating more bids than one object can serialise cannot be scaled by adding instances - that is the point of the primitive - so you would shard the lot itself, which changes the product rules. The object's location is pinned to where it was created, so a London-created lot serves Sydney watchers at Sydney-to-London latency. Price broadcast fans out through edge caches; the bid write does not.

When this is the wrong answer

Until the operations on one entity are frequent enough that the round trip is the user-visible cost. A once-a-minute write to a row in one region, read through a cache, needs none of this and should get none of it: you would be adding a new runtime, a new storage API and a new failure mode to solve a problem you do not have.

The threshold is roughly this - if a single entity sees more than a few writes per second from clients spread across continents, and those writes must be ordered, a per-entity serialization point earns its cost. Below that, a regional database with optimistic concurrency is simpler, portable, and already understood by everyone on the team.