Our analitic system which works with huge DB. Some of queries can take few hours. We create DB query and after 60 seconds if it is not done we kill it and add this query as Job to Queue.
Now we migrate our system to Laravel and here we have a problem with killing a query. After killing query in DB it creates another one and only after second kill it gives a Exception MySQL server has gone away
I investigated Laravel stack and found a function where it happens vendor/laravel/framework/src/Illuminate/Database/Connection.php:
protected function handleQueryException(QueryException $e, $query, $bindings, Closure $callback)
{
if ($this->transactions >= 1) {
throw $e;
}
return $this->tryAgainIfCausedByLostConnection(
$e, $query, $bindings, $callback
);
}
protected function tryAgainIfCausedByLostConnection(QueryException $e, $query, $bindings, Closure $callback)
{
if ($this->causedByLostConnection($e->getPrevious())) {
$this->reconnect();
return $this->runQueryCallback($query, $bindings, $callback);
}
throw $e;
}
There is no any params how we can avoid it, only using transactions, but it is select query and doesn't have a sense.
Any idea how we can get Exception from first attemp?
In general we save connection ID:
session(['process_id' => DB::selectOne('SELECT CONNECTION_ID() AS id')->id]);
session()->save();
And kill it with statement:
DB::statement("KILL $id");
but later we have another connection and need to kill it again...
I added connection override in AppServiceProvider:
/**
* Replace method to avoid reconnect after killing query
* @return void
*/
private function overrideConnection(): void
{
\Illuminate\Database\Connection::resolverFor('mysql', static function ($pdo, $database, $tablePrefix, $config) {
return new \App\Overrides\Connection($pdo, $database, $tablePrefix, $config);
});
}
And created new Connection class which check config option and avoids reconnecting:
class Connection extends MySqlConnection
{
/**
* Handle a query exception.
*
* @param QueryException $e
* @param string $query
* @param array $bindings
* @param \Closure $callback
* @return mixed
*
* @throws QueryException
*/
protected function handleQueryException(QueryException $e, $query, $bindings, \Closure $callback)
{
if ($this->transactions >= 1 || false === $this->getConfig('reconnect')) {
throw $e;
}
return $this->tryAgainIfCausedByLostConnection(
$e, $query, $bindings, $callback
);
}
}