Track the value and version.
Use a development record with a distinctive value. Capture the save response, its record ID and version, then reload through the normal authorised read path. Compare the responses with JSON diff.
A toast displayed before the write commits only proves that the UI took an optimistic path. If your app uses queued saves, distinguish “waiting to save” from a durable success internally.
Await and inspect the write result.
Check affected-row counts and rejected writes. A query that updates zero rows may complete without a database error. Inspect the ownership predicate and resource ID rather than removing them.
const saved = await updateOwnedRecord({
userId, recordId, expectedVersion, changes
});
if (saved.affectedRows !== 1) {
return conflictOrNotFound();
}
return saved.record;This sketch assumes an atomic version check in the actual write, not a separate check followed by an unguarded update.
Test two overlapping edits.
Open the same record in two tabs. Save different values from the same starting version. If the older edit can silently replace a newer value, consider optimistic concurrency with a version predicate and an explicit conflict response.
Also inspect autosave ordering. A slower response from an earlier edit must not replace the newer edit in the UI. A client-side sequence check alone does not prevent server-side lost updates.
Check where the reload reads from.
If the primary record is correct, inspect caches, replica reads and derived views. Use the same account scope in the write and read. Invalidate or refresh the specific data instead of bypassing authentication or disabling every cache globally.
Reference: PostgreSQL’s transaction isolation guide. Our local failure lab includes an optimistic success with an unchanged record.