Repository navigation
Conversation
There was a problem hiding this comment.
🟢 Approval recommended
Both affected dispatch paths defer unique-lock acquisition until after commit, with focused regression coverage and no unresolved findings.
No open issues 🎉
What changed in this PR
This PR addresses #1066 by deferring CheckUserLevel dispatch until commit creation or approval has completed, preventing database-backed unique-lock conflicts from aborting those PostgreSQL transactions.
Changes:
- Move both level-check dispatch paths into
DB::afterCommit()callbacks. - Add regression tests verifying that lock queries execute outside the transaction saving the commit or approval.
| File | Description |
|---|---|
tests/Feature/Controllers/CommitControllerTest.php |
Verify lock acquisition occurs after the commit-saving transaction. |
tests/Feature/Controllers/CommitApprovalControllerTest.php |
Verify lock acquisition occurs after the approval-saving transaction. |
app/Actions/UpdateCommitStatuses.php |
Dispatch authors’ level checks after status changes are committed. |
app/Actions/CreateCommit.php |
Dispatch the level check after commit creation is committed. |
Review effort: Balanced
|
I tested this locally (macOS, PHP 8.5, Postgres 18) by merging it onto the current
After changing those two lines to A rebase onto |
Closes #1066.
CheckUserLevelis a unique job, and Laravel takes its lock when the job is dispatched, even with->afterCommit().CreateCommitandUpdateCommitStatusesdispatched it inside their transaction. With the database cache store, a lock still held by an earlier check makesDatabaseLockfail its insert and fall back to an update. On Postgres the failed insert has already aborted the transaction, so the update fails with25P02and everything rolls back:Both now dispatch the job from
DB::afterCommit(), asCreateCommitStaralready does, so the lock is taken outside the transaction, where a failed insert can't abort anything.Tests: the suite runs every test inside a
RefreshDatabasetransaction, so it can't replay "a lock already exists" without aborting the test's own transaction. The new tests check the property that matters instead: with the database cache store, every query oncache_locksruns outside the transaction that saves the commit or the approval. They're inCommitControllerTestandCommitApprovalControllerTest, and both fail without the change.I also replayed the issue on a local Postgres with the database cache and no worker, outside any test transaction. Before the change, the second approved commit failed with
SQLSTATE[25P02]: In failed sql transaction. After it, both commits were saved. The full suite passes (4,053 tests, 100% coverage), along with type coverage, PHPStan, Pint and Rector.