fix(actions): make validator sync atomic to stop dropped validators#48
Merged
Conversation
upsertAction replaced an action's validators by deleting on `db` (a separate connection, unawaited) and inserting on `tx` (also unawaited). The transaction committed before the inserts landed, and the out-of-tx delete could race and wipe them — leaving actions with zero validators. Such claimable actions become invisible to validators in the app, so their claims sit pending forever. A reindex on 2026-06-13/14 replayed upsertaction as updates and hit this race, stranding 187 pending claims across 8 communities (~80 users). Run the delete + inserts inside `tx`, awaited via Promise.all, and let any failure roll the action back instead of swallowing it. Re-running the indexer over the affected blocks now repopulates validators reliably. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
upsertActionreplaced an action's validators by deleting ondb(a separate connection, unawaited) and inserting ontx(also unawaited). The transaction committed before the inserts completed, and the out-of-transaction delete could race and wipe the just-inserted rows. Result: actions left with zero validators. Aclaimableaction with no validators is invisible to validators in the app, so its claims sitpendingforever.A manual reindex on 2026-06-13/14 replayed
cambiatus.cm::upsertactionas updates (action_id > 0) and tripped this race at scale.Impact (prod, read-only audit 2026-06-29)
Fix
Run the delete + inserts inside
tx, awaited viaPromise.all, with the whole chain returned so the transaction commits only after they land. Drop the per-insert swallowing.catchso any failure rolls the action back instead of persisting a half-written action. Re-running the indexer over the affected blocks now repopulates validators reliably (also makes future reindexes safe).Follow-up (separate, not in this PR)
Backfill the already-broken 262 actions: re-process the affected
upsertactionblocks with this fix deployed, then verify 0 actions with missing validators and the 187 claims become visible.Testing
npx standardclean.upsertactionupdate against a dev DB before deploy.🤖 Generated with Claude Code