create_ticket_api.php: dedup/escalation race can duplicate notifications or drop a real escalation #35
Closed
opened 2026-08-31 21:29:43 -04:00 by jared
·
1 comment
No Branch/Tag Specified
main
development
fix/comment-markdown-persist-18
deploy-2026.10.03-279
deploy-2026.10.03-275
deploy-2026.09.26-272
deploy-2026.09.25-268
deploy-2026.09.25-264
deploy-2026.09.24-258
deploy-2026.09.24-254
deploy-2026.09.12-242
deploy-2026.09.12-238
deploy-2026.09.12-234
deploy-2026.09.12-230
deploy-2026.09.12-226
deploy-2026.09.12-222
deploy-2026.09.11-218
deploy-2026.09.11-214
deploy-2026.09.11-210
deploy-2026.09.11-206
deploy-2026.09.11-202
deploy-2026.09.11-198
deploy-2026.09.11-194
deploy-2026.09.11-190
deploy-2026.09.11-186
deploy-2026.09.09-182
deploy-2026.09.09-178
deploy-2026.09.08-172
deploy-2026.09.01-163
deploy-2026.08.08-155
deploy-2026.08.08-151
deploy-2026.08.08-147
deploy-2026.08.08-143
deploy-2026.08.08-139
deploy-2026.07.15-130
deploy-2026.07.15-122
deploy-2026.07.11-107
deploy-2026.06.30-96
deploy-2026.06.30-92
deploy-2026.06.30-88
deploy-2026.06.30-84
deploy-2026.06.30-80
deploy-2026.06.30-76
deploy-2026.06.30-72
deploy-2026.06.30-68
deploy-2026.04.29-49
deploy-2026.04.29-41
deploy-2026.04.18-35
deploy-2026.04.16-31
deploy-2026.04.16-27
deploy-2026.04.16-23
deploy-2026.04.16-11
deploy-2026.04.14-9
Labels
Clear labels
api
bug
concurrency
config
data-integrity
dead-code
documentation
duplicate
enhancement
help wanted
invalid
needs-decision
notifications
performance
priority/docs
priority/high
priority/low
priority/medium
question
rate-limiting
reliability
security
ux
wontfix
workflow
Bearer/internal API surface
Something is not working
Race condition / concurrency bug
Configuration / deployment default
Data correctness / schema / integrity issue
Unused / dead code cleanup
README / docs accuracy
This issue or pull request already exists
New feature
Need some help
Something is wrong
Needs a maintainer decision, not clearly a bug
Matrix / in-app notification bug
Performance or resource-usage concern
Documentation-only gap
High-severity / high-impact issue
Low-severity / cosmetic issue
Medium-severity issue
More information is needed
Rate limiting behavior
Reliability / error-handling gap
Security or access-control impact
User-facing UX/functional bug
This won't be fixed
Ticket status workflow engine
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: LotusGuild/tinker_tickets#35
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Severity: Medium
create_ticket_api.php(~lines 253-347 and ~457-470): the dedup lookup (SELECT ... WHERE hash = ?) and the subsequent priority-escalationUPDATErun with no transaction or row lock.Two related failure modes:
hwmonDaemon raised priority...comment and firing its own Matrix notification for what is really one escalation event.{success:false, error:'Duplicate ticket'}and the request is dropped entirely — it never falls back to applying the priority-escalation/description-refresh logic the normal (non-race) duplicate path performs, so a genuinely-worsening alert that loses the race is silently discarded instead of escalating the existing ticket.Fix: Wrap the dedup-check + escalate/reopen/insert sequence in a transaction with a row lock (
SELECT ... FOR UPDATE) on the matched ticket (or a unique constraint +INSERT ... ON DUPLICATE KEYpattern) so concurrent requests for the same hash serialize correctly instead of racing.Fixed in commit
3778a59(merged to main in3ab33d8).Two concurrent hwmonDaemon reports carrying the same dedup hash could both read the same pre-update ticket snapshot and each independently apply a priority escalation (losing one), or both attempt to INSERT a new ticket for a not-yet-existing hash and have the loser's request dropped with a 'Duplicate ticket' error instead of falling through to the update/escalate path.
Fix: wrapped the hash lookup through the update-or-insert in one transaction, with the lookup taking SELECT ... FOR UPDATE. For an existing row this serializes the read-modify-write. For a not-yet-existing hash, testing showed InnoDB's gap lock there is shared rather than exclusive, so two concurrent inserts can both reach the INSERT and deadlock (error 1213) instead of one cleanly blocking on the other's row — added a bounded retry (once) on that deadlock (or a lock-wait-timeout, 1205) so the retry's own SELECT finds the winner's committed row and takes the update path instead of erroring.
Verified against real MariaDB with two concurrent OS processes for both scenarios: (1) same existing active ticket — second process blocked ~1.1s on the held row lock, then correctly escalated from the first's committed priority; (2) same not-yet-existing hash — reproduced the 1213 deadlock deterministically (5/5 runs) with the pre-retry code, then confirmed the retry resolves it every time (5/5), leaving exactly one ticket row created and no dropped/erroring request.