Concurrency & conflicts
Two requests that read the same document and write it back shouldn't silently clobber each other. Forze uses optimistic concurrency keyed on a revision number, with a history-based merge that lets non-overlapping edits through and rejects genuine collisions.
The revision¶
Every Document carries a rev — an integer
that starts at 1, is frozen on the model, and is bumped by the store on each
write. You read at a revision, then write back the one you read:
order = await ctx.document.query(order_spec).get(order_id)
await ctx.document.command(order_spec).update(
order.id, order.rev, OrderUpdate(status="paid"),
)
The rev you pass is the revision you expect to be current.
What happens on a concurrent write¶
The store compares your expected rev to the stored one:
- Match — apply the change and bump
rev. - Differ — load the history snapshot at your revision and run a three-way
merge. Edits that touch disjoint fields merge through (last-writer-wins per
field); edits that touch the same fields raise
exc.conflict(historical_consistency_violation). - A future or unknown revision (or a missing history snapshot) raises
exc.precondition(revision_mismatch) — re-read the document and retry.
So a stale write doesn't blindly overwrite: it either merges cleanly or surfaces a conflict for the caller to resolve.
History powers the merge
The three-way merge needs document history enabled. Without it, any
revision mismatch is a precondition error rather than a merge attempt.
Related write helpers¶
Document.validate_historical_consistency(old, patch)is the check behind the merge —Truewhen the concurrent change and yours touch disjoint fields.command.touch(pk)bumpslast_update_atandrevwithout other changes.