Skip to content

Fix crash and lost-update window when sync deletion candidates change concurrently - #256

Merged
norkans7 merged 2 commits into
mainfrom
sync-delete-race
Jul 27, 2026
Merged

Fix crash and lost-update window when sync deletion candidates change concurrently#256
norkans7 merged 2 commits into
mainfrom
sync-delete-race

Conversation

@norkans7

@norkans7 norkans7 commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

In sync_local_to_set, the set of local objects to deactivate is computed before any per-object locks are taken, so a concurrent sync can change an object before the deletion loop reaches it. This caused two problems:

  • if a candidate was concurrently hard-deleted, delete_local's save(update_fields=...) affected 0 rows and raised a DatabaseError, crashing the sync
  • if a candidate was concurrently deactivated, it was redundantly deactivated again and double-counted as deleted

The deletion loop now re-checks each candidate under its lock with a cheap exists() probe: candidates that no longer exist or are already inactive are skipped and counted as ignored, so outcome counts still sum to the number of objects considered.

Known limitation: an object re-affirmed by a concurrent sync after the deletion set is computed can still be deactivated, since the remote identity set is frozen before the loop. Closing that fully would require tracking a last-confirmed timestamp on synced objects. The re-check also assumes it sees committed state (READ COMMITTED/autocommit), so callers must not wrap syncs in an outer transaction that outlives the lock.

norkans7 added 2 commits July 27, 2026 13:34
The deletion queryset is evaluated before any locks are taken, so a
concurrent sync could change or remove an object before the loop
reaches it. Now the object is re-fetched inside its lock and only
deleted if it is still active and still absent from the remote set.
… as ignored

The re-check under the lock now uses a single exists() query instead of
re-fetching the instance with its related objects, and candidates skipped
because they were concurrently deactivated or hard-deleted are counted as
ignored so outcome counts still sum to the number of objects considered.
Also documents the isolation assumptions the re-check relies on.
@norkans7 norkans7 changed the title Re-check object state under lock before deletion in sync_local_to_set Fix crash and lost-update window when sync deletion candidates change concurrently Jul 27, 2026
@norkans7
norkans7 merged commit 1896ac0 into main Jul 27, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant