Skip to content

fix(canal): skip expired notification when patch is no longer pending - #383

Merged
trim21 merged 1 commit into
masterfrom
fix/canal-spurious-outdated-notify
Aug 4, 2026
Merged

fix(canal): skip expired notification when patch is no longer pending#383
trim21 merged 1 commit into
masterfrom
fix/canal-spurious-outdated-notify

Conversation

@trim21

@trim21 trim21 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Problem

When a reviewer accepts a patch, the review flow calls the external wiki API while holding a FOR UPDATE row lock on the patch (subject/character/person). The API write immediately produces a binlog event. The canal (Debezium CDC) consumer can process that event before the local transaction commits, so it sees the patch as still pending and judges it outdated.

The patch row lock makes canal's UPDATE ... WHERE state = 0 block until the accept transaction commits; after commit state is no longer pending, so the update affects zero rows. But the Reject/Accept queries were generated as :exec and ignored RowsAffected, so canal still sent an "expired" notification — the user received both an expired and an accepted notification for the same patch.

Fix

  • Change RejectSubjectPatch / RejectCharacterPatch / RejectPersonPatch and AcceptSubjectPatch / AcceptCharacterPatch / AcceptPersonPatch from :exec to :execrows (return affected row count).
  • In canal.go, check the affected row count and skip the expired notification when it is 0 (the patch state was changed by another flow, e.g. accepted).
  • The review flow keeps its original transaction structure (FOR UPDATE + API call inside the transaction), so there is no intermediate "processing" state that could be left behind after a crash — row locks are released automatically on rollback.

Verification

  • go build ./..., go vet ./..., golangci-lint-v2 run ./..., go test ./... all pass.
  • Note: dal/query.sql.go was updated manually to match sqlc's :execrows output (no sqlc/DB available in this environment); run task gen in an environment with a database to confirm there is no diff.

When a patch is accepted, the review flow calls the wiki API while holding
a FOR UPDATE row lock on the patch. The resulting binlog event can be
processed by the canal consumer before the local transaction commits, so
canal sees the patch as still pending and judges it outdated. Its UPDATE
then waits on the row lock and, after the accept commits, matches zero rows
because state is no longer pending.

Reject/Accept queries were :exec and ignored RowsAffected, so canal still
sent an outdated notification even though the update was a no-op, producing
both expired and accepted notifications for the same patch.

Change Reject/Accept queries to :execrows and have canal check the affected
row count, only notifying when the patch state was actually changed.
@mergify

mergify Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@trim21
trim21 merged commit c57b00b into master Aug 4, 2026
7 checks passed
@trim21
trim21 deleted the fix/canal-spurious-outdated-notify branch August 4, 2026 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant