Bounded Rollouts and Guarded Writes

Lesson 3 of 4

Two Writers, One Row

Estimated time: 10 minutes

Two writers, one row

Two runs read the same record, each changes a different field, and each writes the whole record back. The second write replaces the first run's change with the value it happened to read. Nothing errors, and the change is simply missing later.

Serialising the record you read and sending it back is convenient and it reverts every field you did not mean to touch. That is why the bug looks like a field that keeps resetting rather than like a failure.

The guard is a version carried with the record and included in the write condition. The write applies only if the stored version still matches what was read.

def save(store, record, expected_version):
    current = store.get(record["id"])
    if current["version"] != expected_version:
        return {"ok": False, "reason": "version_moved"}
    record["version"] = expected_version + 1
    store.put(record)
    return {"ok": True}

The store now enforces the check rather than your logic. A rejected write is information, not a problem.