How to use retryHandler #19429
-
Sometimes, connections to MySQL servers aren't the way you want it to be:
Since this can happen almost anywhere, I thought I'd see what Yii2 allows me to configure. Stumbled upon the My initial thoughts were this: return [
'components' => [
'db' => [
'class' => \common\components\RetryingDBConnection::class,
'dsn' => 'mysql:host=mysql;port=3306;dbname=mydb',
'username' => 'root',
'password' => 'super-secure-password',
'charset' => 'utf8',
'commandMap' => [
'mysql' => [
'class' => \yii\db\Command::class,
'retryHandler' => static function (\yii\db\Exception $e, $attempt) {
if (str_contains($e->getMessage(), 'MySQL server has gone away')) {
Yii::$app->getDb()->close();
Yii::$app->getDb()->open();
return true; // retry
}
if (str_contains($e->getMessage(), 'try restarting transaction')) {
return true; // retry
}
return false; // do not retry, but throw error
}
]
],
],
],
]; And even though this configures the How does one use this |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 7 replies
-
Deadlock is not server "gone away", is "busy", PDO tells you what to do: try restarting transaction. Also, you can enable |
Beta Was this translation helpful? Give feedback.
-
I ended up creating my own <?php
namespace common\components;
use yii\db\Command;
/**
* Retries commands if they fail with MySQL gone away or retrying transaction.
*/
class RetryingCommand extends Command
{
public function init()
{
parent::init();
$this->setRetryHandler(static function (\yii\db\Exception $e, $attempt) {
if (str_contains($e->getMessage(), 'MySQL server has gone away')) {
// The MySQL connection got dropped / disconnected, so let's reconnect
\Yii::$app->getDb()->close();
\Yii::$app->getDb()->open();
return true; // retry the SQL command
}
if (str_contains($e->getMessage(), 'try restarting transaction')) {
// Deadlock issues with Galera cluster - two commands updating the same data in parallel
return true; // retry the SQL command
}
return false; // do not retry, but throw error
});
}
} And then specifying it in the config: <?php
return [
'components' => [
'db' => [
'class' => \yii\db\Connection::class,
'dsn' => 'mysql:host=mysql;port=3306;dbname=db1',
'username' => 'root',
'password' => 'super-duper-secure-password',
'charset' => 'utf8',
'attributes' => [
PDO::ATTR_PERSISTENT => true,
],
'commandClass' => \common\components\RetryingCommand::class,
],
] ,
]; |
Beta Was this translation helpful? Give feedback.
This is almost right but doesn't quite work because the prepared query already has the database connection inside of it (so it will fail forever because the old connection is gone), so after the DB reconnects, we actually have to re-create the query using the new database connection.
Here's a working version I used to stop our scripts from crashing when the VPN disconnects: