Skip to main content
Adzbyte Solutions
Web Development

Reject Stale Admin Saves with Record Versions

Adrian Saycon
Adrian Saycon
November 6, 20265 min read
Reject Stale Admin Saves with Record Versions

The court-management work in Dink Slot needed an answer to a very ordinary problem: an owner can open the same record in two tabs. Both forms can be valid, both can have a valid nonce, and one can still contain yesterday’s view of the record.

The owner catalog change added version checks to edits and lifecycle actions. That includes publishing, moving records to draft, archiving and restoring them. This is development work for an owner-only scheduling beta. It isn’t evidence of public customer bookings or a production incident.

Send the version the form actually loaded

Each editable record has a version. The form sends the version it loaded along with the requested change. The server compares that value with the current record before accepting the write. If the record has changed, the old form doesn’t get to overwrite it just because its submission arrived later.

For example, two tabs load a court at version four. One saves a new name and advances the record to five. The other tries to save its old amenities against version four. The second save is rejected. The owner needs to reload and review the current values before submitting another change.

In this implementation, a missing or invalid version gets a 428 response, while a stale version gets 412. Those cases stay separate from a normal field-validation error. Telling someone to fix a court name won’t help when the form itself is out of date.

Keep the comparison inside the write transaction

Checking a version in a controller and updating later would leave another opportunity for the record to change. The storage command takes the relevant locks and checks the version as part of the transaction that performs the write.

The catalog code locks the branch first. For a court command, it then reads the court with a locking read. The final update also includes the expected version in its condition and must change exactly one record. A successful edit or lifecycle command increments the version once.

Court data spans a resource record and court details. Updating the details without updating the resource version would leave a misleading result. Both writes use the same transaction, and failures roll them back together. This matters even when each individual query looks straightforward.

Apply the rule to archive and publish too

It’s easy to think of concurrency as a text-editing problem. The catalog work also applies it to state changes. An old Publish or Archive form can be just as misleading as an old name field, especially when the record has moved into a different state since the page loaded.

The implementation treats those actions as explicit commands. An archived record must be restored before editing, restoration produces a draft, and court branch assignments can’t be changed through a details form. Archive retains the record and its identity rather than deleting it.

Parent rules matter as well. An archived branch prevents several child-court operations. Checking only the court’s version wouldn’t establish that its parent still allows the requested action. The version check sits alongside these rules; it doesn’t replace them.

Give a stale save a useful recovery path

The original owner-form response says the record changed in another window and asks the owner to reload and review the latest values. That’s a smaller promise than automatically merging two edits, but it’s a clear one. The server won’t quietly pick a winner and pretend both forms succeeded.

For richer editors, I’d keep the person’s unsaved values available while fetching the latest record. Show what changed and let them decide which values to submit. That’s follow-on interface advice, not a claim that this catalog commit included a full merge screen.

The same idea can be expressed through HTTP preconditions in an API. MDN’s If-Match reference describes conditional requests that can prevent lost updates. The catalog milestone used a version in native owner forms, so it’s important not to describe those forms as an implemented REST editing API.

Test two real submissions, not just a comparison helper

The added browser test sends two simultaneous saves using the same version. It expects one successful redirect and one 412 response, then checks that the version advanced exactly once. That tests the request path and storage behavior together.

Other added checks cover missing and invalid versions, stale court edits and stale branch lifecycle actions. A database-side test forces a resource update to fail and verifies that the court details and version remain unchanged. Those assertions cover the request path and transaction outcome rather than only testing whether two numbers differ.

Keep fixtures small enough that the expected outcome is obvious. Two competing edits to one court say more about this rule than a large test that creates dozens of unrelated records.

Don’t confuse a version with a command receipt

A repeated save using an old version fails after the first save succeeds. That’s useful protection against applying an outdated change twice. It doesn’t tell a disconnected browser which result belongs to an earlier request, and it doesn’t provide a durable command-recovery record.

The catalog documentation makes that distinction explicit. Versioning answers whether this change was based on the current record. Recoverable commands answer whether a particular operation already committed. A form may need both as the workflow grows.

For this owner editor, the useful starting rule is simple: the server checks the record the person actually reviewed, and an older form gets a clear chance to recover rather than silently replacing newer work.

Adrian Saycon

Written by

Adrian Saycon

A developer with a passion for emerging technologies, Adrian Saycon focuses on transforming the latest tech trends into great, functional products.

Discussion (0)

Your email is used only for comment moderation and is never published.

No comments yet. Be the first to share your thoughts.