Coordination — A Meeting Point for Many Clients
Two things in Hoplink need everyone to agree. First, when two people in different cities try to claim the short code launch at the same moment, exactly one must win. Second, the owner wants a live dashboard that shows clicks the instant they happen, on every open browser tab. Both are coordination problems, and both have the same answer.
The rule
Send everyone who must agree to the same object; let it decide in order.
Because a Durable Object handles one event at a time, it can act as a referee.
Claiming a code. The race from the KV lesson — both people read null, both write — disappears when both requests go to an object that owns the claim:
export class CodeRegistry extends DurableObject {
async claim(owner) {
const current = await this.ctx.storage.get("owner");
if (current) return { ok: false, owner: current };
await this.ctx.storage.put("owner", owner);
return { ok: true };
}
}
// in the Worker: one registry object per code
const registry = env.REGISTRY.get(env.REGISTRY.idFromName("launch"));
const result = await registry.claim("priya");Both requests reach the same object; the first to arrive takes the code, and the second is told who owns it. No locks, no retry loops — the order in which the object handles requests is the decision.
Live updates. A Durable Object can hold many WebSocket connections at once and push a message to all of them. Each browser tab opens a WebSocket that the Worker forwards to the link's object; when a click arrives, the object broadcasts it:
export class LinkLive extends DurableObject {
async fetch(request) {
const [client, server] = Object.values(new WebSocketPair());
this.ctx.acceptWebSocket(server); // hibernation-friendly accept
return new Response(null, { status: 101, webSocket: client });
}
async recordClick(country) {
for (const ws of this.ctx.getWebSockets()) {
ws.send(JSON.stringify({ type: "click", country }));
}
}
}Accepting with ctx.acceptWebSocket lets the object hibernate while the sockets are quiet: connections stay open, but you are not billed for an idle object sitting in memory.
The shortcut
Ask "Who needs to see the same truth at the same time?" Everyone in that group gets routed to one object named after the thing they share — the code being claimed, the link being watched, the room being joined.
Who shares the truth?
Every tab watching link
catmust see the same stream of clicks.
Trap: sending watchers of the same link to different objects (for example, naming by visitor instead of by link). Each object then sees only part of the truth.
Drills
Answer fast — trust the rule.
Why does routing both claims for launch to one object prevent a double claim?
What name should the live-dashboard object use?
What does accepting sockets with ctx.acceptWebSocket(server) allow?
A chat feature names objects by user id, so each user in a room reaches a different object. What goes wrong?
Remember
- Send everyone who must agree to the same object; let it decide in order.
- Shortcut: "who needs the same truth at the same time?" — name the object after what they share.
- Trap: naming objects by the wrong unit and splitting the truth.
