-
Notifications
You must be signed in to change notification settings - Fork 11.6k
Description
Laravel Version
11.44.2
PHP Version
8.2.27
Database Driver & Version
No response
Description
When using Bus::chain(...)->catch(...), the catch callback will be transferred to a Bus::batch inside of the chain. This will result in the same callback to be executed twice, once for the batch and then for the chain. I do not understand why this should happen. The chain and the batch both have providable catch callbacks, so why would a batch receive all of the chain callbacks on top?
Just for the sake of hopefully understanding it better:
Bus::chain([
Bus::batch([
new MyJob(),
])->catch(static fn() => logger()->info('batch callback')),
])->catch(static fn() => logger()->info('chain callback'));When the Bus::batch is dispatched onto the queue, it will keep its catch callback outputting "batch callback", but it will also receive the catch callback outputting "chain callback". So if MyJob fails, the batch will output both "batch callback" AND "chain callback". Then the error will bubble up, which will cause the chain to run its catch callback, too, which will output "chain callback" again. Therefore: The chain callback has been called twice.
Another thing.
On the sync queue (like in PHPUnit tests), the catch callback of Bus::batch is run in a transaction, which will even reverse any database changes done in the catch callback. That means I have to add checks inside of the catch callback since it's called twice, but I cannot test those check because my phpunit runs through the sync queue, which will revert any database changes in the first callback.
For reference:
- Batches are run inside of transaction, which will revert the changes on sync queue: vendor/laravel/framework/src/Illuminate/Bus/Batch.php:187
- Chain catch callbacks are added to batch, which will make the callback execute twice: vendor/laravel/framework/src/Illuminate/Bus/ChainedBatch.php:100
Steps To Reproduce
Bus::chain(
Bus::batch([....]),
Bus::batch([....]),
Bus::batch([....]),
)->catch(static fn() => logger()->info('hi'))->dispatch();- Create a job chain like the above and make sure the job inside the batch fails.
- Configure to use the sync queue, i.e. through calling the code within a phpunit test.
- Run the code, check the logs.
For reference:
- Batches are run inside of transaction, which will revert the changes on sync queue: vendor/laravel/framework/src/Illuminate/Bus/Batch.php:187
- Chain catch callbacks are added to batch, which will make the callback execute twice: vendor/laravel/framework/src/Illuminate/Bus/ChainedBatch.php:100