-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reconect to database if connect closed #12634
Comments
I think it's nice to have feature. |
|
It may be useful when you write a service or a daemon |
add please! I need it too! |
@solovjov, @anatoliyarkhipov, @pernatiy02 what do you think about the solution @Mirocow proposed? |
Constant triggering of SQL statement even such simple as |
For MySQL it could be @klimov-paul could it be avoided while achieving reconnecting? |
The only possible solution is wrapping existing public function execute()
{
try {
return $this->executeInternal();
} catch (Exception $e) {
if (/* $e indicates DB connection is closed by timeout */) {
$this->db->close();
$this->db->open();
return $this->executeInternal();
}
throw $e;
}
} |
We may add flag permanent such
|
DO 1; very nice sql query |
bad idea because sql query should be very hard |
This is terrible! Please don't do this! |
Duplicates #10168 |
why? |
As for me, better to do like this: <?php
namespace app\components\db;
use yii\db\Exception;
class Command extends \yii\db\Command
{
/**
* @var ReConnection
*/
public $db;
protected function queryInternal($method, $fetchMode = null)
{
try {
return parent::queryInternal($method, $fetchMode);
} catch (Exception $e) {
$this->cancel();
$this->db->reconnect();
$this->bindValues($this->params);
return parent::queryInternal($method, $fetchMode);
}
}
} <?php
namespace app\components\db;
use Yii;
use yii\base\Exception;
use yii\db\Connection;
use app\components\db\mysql\PDO;
class ReConnection extends Connection
{
/** @var int - attempts retry */
public $retry_attempts = 10;
/** @var int - millisecond sleep time */
public $sleep_time = 100;
/** @var string */
public $commandClass = Command::class;
/**
* Creates a command for execution.
* @param string $sql the SQL statement to be executed
* @param array $params the parameters to be bound to the SQL statement
* @return Command the DB command
*/
public function createCommand($sql = null, $params = [])
{
$class_name = $this->commandClass
$command = new $class_name([
'db' => $this,
'sql' => $sql,
]);
return $command->bindValues($params);
}
/**
* ReCreates the PDO instance.
*
* @return PDO the pdo instance
* @throws Exception - Cannot connect to db
*/
protected function createPdoInstance()
{
$count = 0;
do {
try {
return parent::createPdoInstance();
} catch (\PDOException $e) {
if (++$count > $this->retry_attempts) {
break;
}
usleep($this->sleep_time);
Yii::info("Reconnect attempt: $count ". $this->dsn, __METHOD__);
}
} while (true);
throw $e;
}
/**
* @throws \yii\base\InvalidConfigException
* @throws \yii\db\Exception
*/
public function reconnect()
{
Yii::info('Reopening DB connection: ' . $this->dsn, __METHOD__);
$this->close();
$this->open();
}
} |
so hard for me ;) |
Are you trying to check you code?
There no variable _instance in Command class
Variable pdoStatement will content old instance of PDO, not a new one. |
fixed
because this code should be in the namespace \yii\db\Command |
There no variable _instance in \yii\db\Command |
Faced this problem in my long-running queue workers. Found that the best solution is to store date of Something like that: if (time() - $this->_openedAt > $this->reconnectTimeout) {
$this->close();
} |
@Mirocow I don't know why you've reacted with thumb down on comment on my previous comment if you think it's not related - explain your opinion, please. I see the following use cases and solutions for the problem:
Why do I think auto-reconnect can not be accepted:
|
because we must execute very simle sql query in my app i did such
|
Another way is to introduce Yii::$app->trigger('idle'); This will allow us to close any kind of resources in framework core. // Any connection based component
public function init()
{
Yii::$app->on('idle', [$this, 'close']);
parent::init()
} This may be useful only in daemon scripts, but I think you should consider this solution for its flexibility. |
Daemons is a complex thing and daemons know a plenty of ways how to fuck you up :) There are much wiser ways to handle gone connection than sending SQL pings through it each time you want to execute something real. And @AnatolyRugalev have posted some of them. I think we should close the issue. @Mirocow do you have anything to say about the transactions use case I've posted? |
@SilverFire another case, we write daemon or service and our service execute some queries for some settings or update some flags |
if (time() - $this->_openedAt > $this->reconnectTimeout) {
$this->close();
} 👍
But if you will have a network problem it will not help :) |
http://www.php.net/pdo.connections
|
This is persistant connect, but it will not rescue when you will have connection timed out. |
You should ping anyway in order to keep connection open even it's opened as persistent one. |
I don't think that there is any good and universal solution, because it depends on application specifics and developer is responsible for approach choosing. I'm closing the issue, thanks to all for participating in this discussion. |
So overall issue is the same when using both regular and persistent connection. You attempting making a query and DB server replies that it "has gone away". Sending additional query doesn't help the situation in any way. try-catch seems to be better solution but re-querying automatically could be dangerous if you use transactions so it seems overall it's very app-specific and could not be introduced at the framework level. |
thx |
Yes, but this is crunch if we will cover each request. So, the next way is extend Command with my example |
Indeed but your example could be dangerous if transactions are used. May lead to inconsistent DB state. |
Why? |
It was expected that post title change, comment 1 and comment 2 are written but only comment 2 was. |
You can exclude this for transaction :) For other ways it will help |
You still need to reconnect for transactions but you need to repeat the whole transaction instead of part of it. |
I don't want to give a way to shoot yourself in the foot silently. |
Yeah. That's tricky to solve in a good way for all cases. |
Absolutely agry with you! This is not issue for Yii2, this is crunch for current project ;) |
For Yii 1.1 I solve this in this way: class ImportDbConnection extends CDbConnection {
private $stamp;
/**
* {@inheritdoc}
*/
public function createCommand($query = null) {
$this->setActive(true);
try {
// send ping on every 10 seconds
if ($this->stamp < time()) {
$this->stamp = time() + 10;
$ping = new CDbCommand($this, 'SELECT 1');
$ping->execute();
}
} catch (CDbException $e) {
// if ping fail, reconnect
$this->setActive(false);
$this->setActive(true);
}
return parent::createCommand($query);
}
} 10 seconds could be parametrized and set by default to |
It won't send ping if there aren't queries for a minute, right? |
@samdark No, it will not prevent disconnecting, only try to reconnect if connection is lost. I use this for read only database for some long-running import scripts. |
Yes. For read-only it's safe. I wonder why doing a special query instead of try-catch-ing the real one? |
|
You can grep for "gone away", right? |
This is dirty hack - what if I will have 'gone away' string in my query that failed and it will be part of error message? Or for some reason (localization?) in one of the servers message will be different? In my case the performance was not so crucial to waste time on the nuances. |
please refactor class \yii\db\Command
The text was updated successfully, but these errors were encountered: