[8.x] Run observer callbacks after database transactions have committed #436
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR sets
public $afterCommit = true
on Scout'sModelObserver
class, so that thesaved
,deleted
,forceDeleted
, andrestored
model event callbacks, if they are triggered inside a database transaction, are not executed until that transaction has committed.Background
This fixes an existing issue where incorrect data is synced to a search index if a model event Scout listens for, such as
saved
, is fired inside a database transaction.If Scout is not set up to queue search index updates, this issue affects all code that fires the above model events inside database transactions, because Scout's observer method is executed immediately—before the transaction has committed or been rolled back—syncing stale or invalid data.
If Scout is set up to use the queue this issue will appear intermittently, when the queue workers pick up and execute the syncing job before the transaction has committed or rolled back. In my experience this happens almost all the time.
See #152 and laravel/nova-issues#1906.
It wasn't really possible to address this at all up until now, but transaction-aware code execution was just added to Laravel core (see laravel/framework#35373 and laravel/framework#35434). In Laravel 8.17.0 and later, it's trivial to delay any listeners and observers until open database transactions have committed.
Backwards compatibility
Theoretically this could change some behaviour in existing Scout installations, but I would argue that that behaviour is a bug and should be fixed. I can't imagine a situation where an application would be relying on stale or invalid data being persisted to a search index, so I doubt this change would break anything.
Tests
Since model observers are basically just larger listener classes, in my opinion this PR is covered by Laravel's tests for delaying listener execution until transactions have committed: https://github.com/laravel/framework/blob/8.x/tests/Integration/Events/ListenerTest.php.