The following tutorial column ofLaravelwill share with you the record about the Redis queue exception caused by Laravel Observer. I hope it will be helpful to everyone!
After creating a new model, use the Observer model event Created to push into the asynchronous SMS sending queue
App\ Http\Controllers\UsersController
public function store(User $user) { \DB::beginTransaction(); try{ $input = request()->validated(); $user->fill($input); $user->save(); //do something...... //其他数据表操作 \DB::commit(); } catch ($e \Exception) { \DB::rollBack(); } }
App\Observers\UserObserver
class UserObserver{ public function created (User $user) { dispatch(new SmsQueue($user)); }}
Business The department reported that occasionally users could not receive SMS notifications, so I checked the logs and found that there were occasional error exceptions: No query results for model [App\Models\User]. It means that the corresponding model cannot be found
I can’t type it Yes, I made the queue call after creating the model... Then I carefully checked the business code and guessed that it should be affected by the transaction.
Verify the conjecture:
public function store(User $user) { \DB::beginTransaction(); try{ $input = request()->validated(); $user->fill($input); $user->save(); //do something...... //其他数据表操作 sleep(3); //三秒之后再提交事务 \DB::commit(); } catch ($e \Exception) { \DB::rollBack(); } }
Sure enough, after waiting for three seconds, the submission queue exception is 100% triggered.
$user->save() If this method successfully creates data, the scheduler will be triggered as well. Execute model events one by one.
Push the model to the queue in the event, and the queue process consumes data in the queue continuously.
In most cases, if the do something processing speed is normal, the queue process will run as usual.
If there is an occasional delay during the do something phase, the transaction has not yet been committed but the queue has already started consuming the new model; therefore, the above error is caused.
Then when I searched the Github Issues record, I found that this issue had been raised in an Issue in 2015, and in Laravel 8.X, support for transaction model events was finally added. Supported; learnku.com/docs/laravel/8.x/eloqu..., I don’t seem to find relevant instructions in the community documentation~
Because my version is 6.x, I can’t use this new feature[ Crying]~~
This involves the transaction isolation level of MySQL. The default isolation level of the InnoDB engine is REPEATABLE READ. The differences between each level can be found in the official documentation.
Switching the isolation level to READ UNCOMMITTED can solve this problem, but in order to prevent bigger problems, I advise you not to use this method~
View the source code and learn that after the transaction is completed, the corresponding event will be called, so you only need to add event monitoring.
New classApp\Handlers\TransactionHandler
class TransactionHandler{ public array $handlers; public function __construct() { $this->handlers = []; } public function add(\Closure $handler) { $this->handlers[] = $handler; } public function run() { foreach ($this->handlers as $handler) { $handler(); } $this->handlers = []; }}
Create auxiliary Functionapp/helpers.php
if (! function_exists('after_transaction')) { /* * 事务结束之后再进行操作 * */ function after_transaction(Closure $job) { app()->singletonIf(\App\Handlers\TransactionHandler::class, function (){ return new \App\Handlers\TransactionHandler(); }); app(\App\Handlers\TransactionHandler::class)->add($job); }}
Create listenerApp\Listeners\TransactionListener
namespace App\Listeners;use App\Handlers\TransactionHandler;class TransactionListener{ public function handle() { app(TransactionHandler::class)->run(); }}
Bind listeningApp\Providers\EventServiceProvider
namespace App\Providers;use App\Listeners\TransactionListener;use Illuminate\Database\Events\TransactionCommitted;use Illuminate\Database\Events\TransactionRolledBack;use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;;class EventServiceProvider extends ServiceProvider{ /** * The event listener mappings for the application. * * @var array */ protected $listen = [ TransactionCommitted::class => [ TransactionListener::class ], TransactionRolledBack::class => [ TransactionListener::class ] ];}
Change the calling methodApp\Observers\UserObserver
class UserObserver{ public function created (User $user) { after_transaction(function() use ($user) { dispatch(new SmsQueue($user)); }); }}
OK, an elegant solution is complete~~
Related recommendations: The latest five Laravel video tutorials
The above is the detailed content of Using Observer events in Laravel causes Redis queue exception problem. For more information, please follow other related articles on the PHP Chinese website!