Skip to content

Roll back vote_grants audit row on dispatch failure#49

Merged
AdaInTheLab merged 1 commit into
mainfrom
feature/vote-rewards-rollback-on-error
Apr 30, 2026
Merged

Roll back vote_grants audit row on dispatch failure#49
AdaInTheLab merged 1 commit into
mainfrom
feature/vote-rewards-rollback-on-error

Conversation

@AdaInTheLab
Copy link
Copy Markdown
Collaborator

Summary

Bug fix. Caught while finishing #48 — too late to fold in there before merge.

When `DispatchReward` throws after the audit row was inserted, the player is permanently locked out of ever claiming that vote. Every future sweep sees `HasGrantForDate=true` and short-circuits, even though no reward was ever actually granted.

This always could have happened (e.g. SQLite glitch in `AdjustPoints`), but #48's VipGift dispatch made it actually likely — a misspelled template name in the Vote Rewards settings would silently lock out every voter on that provider, with no obvious way for an admin to recover other than directly editing the SQLite database.

Fix

Narrow `try/catch` around the `DispatchReward` call only. On exception:

  1. Delete the audit row via the new `IVoteGrantRepository.DeleteByKey`
  2. Log a clear warning explaining the rollback
  3. Return a `ClaimOutcome.Error` with the underlying message

The outer `try/catch` (network errors during `GetClaimStatus` / `MarkClaimed`) is unaffected — those failure modes don't leave an orphaned row, so they don't need rollback.

Files

File What
`Data/Repositories/VoteGrantRepository.cs` `DeleteByKey(provider, steamId, voteDate)`
`Features/VoteRewardsFeature.cs` Narrow try/catch around `DispatchReward` with rollback

Test plan

  • Configure 7daystodie-servers.com with reward type = VIP Gift, template name = a typo (e.g. "vote-bundlex")
  • Vote at the listing site
  • Sweep runs, sees the vote, attempts grant, dispatch throws because no template by that name
  • Server log shows the rollback warning
  • No vote_grants row left over (`SELECT * FROM vote_grants` returns zero rows for that vote)
  • Fix the template name in settings
  • Next sweep (or `/vote`) succeeds — no longer short-circuited
  • Player gets the gift, audit row appears

🤖 Generated with Claude Code

Bug introduced (in spirit) by #46 and made very visible by #48: when
DispatchReward throws AFTER the audit/idempotency row was inserted, the
player is permanently locked out of ever claiming that vote. Future
sweeps see HasGrantForDate=true and short-circuit before retrying, even
though no reward was ever actually granted.

This always could have happened (e.g. SQLite glitch on AdjustPoints),
but #48's VipGift dispatch made it likely — a misspelled template name
in the Vote Rewards settings would silently lock out every voter on
that provider.

Fix: narrow try/catch around the DispatchReward call. On exception,
delete the audit row via the new IVoteGrantRepository.DeleteByKey so
the next sweep can retry once the admin fixes the misconfiguration.
The outer try/catch (network errors during GetClaimStatus / MarkClaimed)
is unaffected — those failure modes don't leave an orphaned row, so
they don't need rollback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@AdaInTheLab AdaInTheLab merged commit 5d6ecbe into main Apr 30, 2026
2 checks passed
@AdaInTheLab AdaInTheLab deleted the feature/vote-rewards-rollback-on-error branch April 30, 2026 15:19
@AdaInTheLab AdaInTheLab mentioned this pull request Apr 30, 2026
4 tasks
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