Sunday, July 29, 2018

Laravel: How to update the MySQL by Eloquent in child process?

Leave a Comment

I write a Laravel Command, and it will fork some child process. Child process will update the DB by Eloquent.

Code:

<?php  namespace App\Console\Commands;  use App\Console\BaseCommand; use App\Item; use Illuminate\Console\Command;  class Test extends Command {     /**      * The name and signature of the console command.      *      * @var string      */     protected $signature = 'test';      /**      * The console command description.      *      * @var string      */     protected $description = 'Command description';      /**      * Create a new command instance.      *      * @return void      */     public function __construct()     {         parent::__construct();     }      /**      * Execute the console command.      *      * @return mixed      */     public function handle()     {         Item::first();         $children = [];         for($i = 0; $i < 5; $i++) {             $pid = pcntl_fork();             if ($pid == -1) {                 die('pmap fork error');             } else {                 if ($pid) {                     $children[] = $pid;                 } else {                     Item::first(); exit;                 }             }         }         foreach ($children as $child) {             pcntl_waitpid($child, $status);         }     } } 

Run my code:

vagrant@homestead:~/ECAME$ php artisan test     [Illuminate\Database\QueryException]   Packets out of order. Expected 1 received 116. Packet size=6255201 (SQL: select * from `items` where `items`.`deleted_at` is null limit 1)      [Illuminate\Database\QueryException]   Packets out of order. Expected 1 received 100. Packet size=6238815 (SQL: select * from `items` where `items`.`deleted_at` is null limit 1)      [Illuminate\Database\QueryException]   Packets out of order. Expected 1 received 0. Packet size=2816 (SQL: select * from `items` where `items`.`deleted_at` is null limit 1)      [Illuminate\Database\QueryException]   Packets out of order. Expected 1 received 116. Packet size=6381412 (SQL: select * from `items` where `items`.`deleted_at` is null limit 1)      [ErrorException]   Packets out of order. Expected 1 received 100. Packet size=6238815      [ErrorException]   Packets out of order. Expected 1 received 116. Packet size=6381412      [ErrorException]   Packets out of order. Expected 1 received 116. Packet size=6255201      [ErrorException]   Packets out of order. Expected 1 received 0. Packet size=2816 

What's the reason behind that? And How to update the MySQL by Eloquent in child process?

PS:

I think the reason for that problem is, all child processes use the same MySQL connection which forked from the parent process.

If I don't call Item::first() in the parent process before call fork(), it works well. (In my real use case, I can't do that... The parent process will do a lot with MySQL before fork child process.)

Because in that case, the MySQL connection doesn't initialize in the parent process, so every child process will initialize a connection on their own.

So, if it's the case, how to initialize a new MySQL connection for each child process after forked?

2 Answers

Answers 1

Since it's all about the connection dying, you can solve this by simply reconnecting to the database.

use Illuminate\Support\Facades\DB;  [...]  public function handle() {     User::first();      $children = [];      for ($i = 0; $i < 5; $i++)      {         $pid = pcntl_fork();          if ($pid == -1)          {             die('pmap fork error');         }          else          {             if ($pid)              {                 $children[] = $pid;             }              else             {                 DB::connection()->reconnect(); // <----- add this                 User::first(); exit;             }         }     }      foreach ($children as $child)      {         pcntl_waitpid($child, $status);     } } 

I tested this in Laravel 5.6 and it works.

Answers 2

And if you define a second ddbb with equal parameters in your database.php , and you launch your Item::first based in the second connection ?

# Primary database connection             'mysql' => [                 'driver'    => 'mysql',                 'host'      => 'localhost',                 'database'  => 'myddbb',                 'username'  => 'root',                 'password'  => '',                 'charset'   => 'utf8',                 'collation' => 'utf8_unicode_ci',                 'prefix'    => '',             ],   # Secondary database connection         'mysql_forConnectChildren' => [             'driver'    => 'mysql',             'host'      => 'localhost',             'database'  => 'myddbb',             'username'  => 'root',             'password'  => '',             'charset'   => 'utf8',             'collation' => 'utf8_unicode_ci',             'prefix'    => '',         ], 

Later

 $item = \DB::connection('mysql_forConnectChildren')->select('select * from Item')->get(1); 

I not tested but i think can works

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment