guide

Contention

A permission model can't stop two people booking the same 3pm slot. Preconditions can, and each one is an argument on a call you already make.

A 409 conflict is the mechanism working. Catch it and tell the user the slot, stock, or edit was taken, rather than retrying it away.

Uniqueness with keyed creates

Derive the key from the thing that must be unique. The second writer gets a 409; your own retry returns your record back (existing: true); deleting the record frees the key.

await bookings.create({ who: email }, { key: "slot:2026-07-15T15:00" })

Limited stock: N units anyone can buy

Claim units with keyed creates, and on conflict try the next unit. When every unit is taken, the item is sold out. This is race-proof under every safety rule, because writes on other users' records are never allowed.

await orders.create({ item: 42 }, { key: "unit:item42:1" })  // then :2 … :N

Counters: the server does the math

Atomic ops run against current state on the server. Breaching the floor or ceiling → 409, record untouched.

await products.update(id, { stock: { decrement: 1, floor: 0 } })

An object in a patch is an atomic op ONLY when its keys are exactly increment|decrement (+ optional floor|ceiling), all numbers. Anything else is stored as plain data.

Shared editing with versions

Pass back the version you read. A stale save gets a 409 instead of silently clobbering someone's edit, so re-read, reapply, and retry.

await pages.update(id, { body }, { ifVersion: page.version })

Compound: one-per-user AND limited

A booking that must be unique and capped per user wants two keyed creates: claim the resource first, then the per-user cap. If the second 409s, delete the first, which frees its key:

const slot = await bookings.create({ when }, { key: "slot:" + when })
try {
  await quotas.create({ ref: slot.id }, { key: "user-day:" + user.userId + ":" + day })
} catch (e) {
  if (e.code === "conflict") { await bookings.delete(slot.id) }  // give the slot back
  throw e
}

Never find-then-create, never compute counters client-side. Both race, and both fail only when real users collide, which is exactly when it matters. Keys are 1–120 chars of letters, numbers, and : _ . @ / -.