Skip to content

Retry a request set whose transaction was rolled back by a caught error - #356

Merged
snake14 merged 7 commits into
6.x-devfrom
dev-20866-centralize-transaction-rollback-handling
Nov 2, 2026
Merged

snake14 merged 7 commits into
6.x-devfrom
dev-20866-centralize-transaction-rollback-handling

Conversation

@chippison

Copy link
Copy Markdown
Contributor

DEV-20866

Follow-up to matomo-org/matomo#25549, which makes the tracker connection refuse every query and the commit after an error that rolled back the transaction.

If a tracking step caught such an error on the last query of a request set, nothing noticed until the lock or commit queries, which then failed outside the retry path. The worker crashed, and the unlock in the error handler failed too, so the lock stayed held until it expired.

  • After each tracked request the handler checks whether the transaction was rolled back, so the set goes down the existing retry path.
  • When processing fails, the processor rolls back before it releases the lock.
  • The retry check uses core's TransactionErrors when it exists.

Both checks are skipped on Matomo versions without the core change. The new HandlerTest case needs the core PR, and is skipped without it.

Review

  • [✔] I have understood, reviewed, and tested all AI outputs before use
  • [✔] All AI instructions respect security, IP, and privacy rules

A tracking step can catch an error after which the database already rolled back the transaction; the set now goes down the retry path, and a failed run rolls back before releasing its lock.
@chippison
chippison force-pushed the dev-20866-centralize-transaction-rollback-handling branch from ed34465 to 0079917 Compare October 30, 2026 00:07
PHPStan runs against released Matomo, which has no TransactionErrors class or TRANSACTION_ABORTED_MESSAGE yet.
@chippison
chippison requested a review from a team November 2, 2026 00:36

@snake14 snake14 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix works: a deadlock swallowed on the last request of a batch is now retried, the lock is released, and every request is stored once, with both PDO and MYSQLI. A few things to check:

Medium

The rollback added to Processor::process()'s catch block has no test, so removing or reordering it before unlock() would leave the suite green. A ProcessorTest case that makes a backend query fail on an aborted transaction and asserts the lock is released would cover it.

Low

  1. failIfTransactionAborted() always says "MySQL error 1213", even when the swallowed error was another one that ends the transaction, and that message goes to the warning log. A neutral message ("a caught error already rolled back this transaction") with the same code would avoid misleading triage.
  2. The docblocks of hasTransactionErrors(), getRequestSetsWithTransactionErrors() and isRetryableTransactionError() still describe the old set of errors.

Adds a ProcessorTest case that fails if the aborted transaction is not rolled back before the lock is released, and updates the docblocks to the new error set.
A request that swallowed a deadlock and was then rejected as invalid or for an unknown site left the set counted as processed.
@chippison

Copy link
Copy Markdown
Contributor Author

@snake14
Thanks for the quick review.
Fixed all 3 issues, then I also found another low issue: a request that swallowed a deadlock and was then skipped (invalid request or unknown site) didn't run the new check, so the set counted as processed. This now gets retried like other cases

Should be good to be re-reviewed now

@chippison
chippison requested a review from snake14 November 2, 2026 02:39

@snake14 snake14 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes @chippison . It looks like we missed the automated release for this week. Can you please merge in the base and bump the plugin.json/changelog version?

@snake14

snake14 commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

AI also mentioned few things to check. One is the version bump which I requested:

Medium

  1. 6.0.3 was released today, but the CHANGELOG.md entry is still under 6.0.3 and plugin.json wasn't bumped. It merges cleanly, so the fix would be listed under the shipped version. It needs a 6.0.4 section and version bump.
  2. The \Throwable catch in Handler::process() doesn't call failIfTransactionAborted() like the other two catches. When a step swallows a deadlock and then throws anything else, with the MySQL backend sharing the tracker connection, the error still escapes Processor::process() and the rest of that run's queues are skipped; on Redis the same case just carries on. Calling it at the top of that catch, after logging, would send the set down the retry path in both cases.

Low

  1. In failIfTransactionAborted(), "the code sends the set down the retry path" reads as "the program". Something like "1213 sends the set down the retry path" would be clearer.

6.0.3 was released without it.
The rollback before unlocking is now tested through a deadlock in the lock query, the path still left for it.
@chippison

chippison commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor Author

@snake14
Nice catch on the second issue!
Ive added the fix for the 3 issues found.
Ready to be re-reviewed again
Thanks

@chippison
chippison requested a review from snake14 November 2, 2026 09:22

@snake14 snake14 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works as expected. All review points have been addressed. Thank you. 👍

@snake14
snake14 merged commit 9ca34eb into 6.x-dev Nov 2, 2026
24 checks passed
@snake14
snake14 deleted the dev-20866-centralize-transaction-rollback-handling branch November 2, 2026 20:48
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.

2 participants