-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
87 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<?php | ||
|
||
namespace ipl\Sql; | ||
|
||
use Exception; | ||
use PDOStatement; | ||
|
||
class RetryConnection extends Connection | ||
{ | ||
/** @var int Number of retries to be performed before giving up */ | ||
protected $retries; | ||
|
||
/** @var string[] A list of PDO retryable errors */ | ||
protected static $retryableErrors = [ | ||
'server has gone away', | ||
'no connection to the server', | ||
'Lost connection', | ||
'Connection was killed', | ||
'Connection refused', | ||
'Error while sending', | ||
'is dead or not enabled', | ||
'decryption failed or bad record mac', | ||
'server closed the connection unexpectedly', | ||
'SSL connection has been closed unexpectedly', | ||
'Error writing data to the connection', | ||
'Resource deadlock avoided', | ||
'Transaction() on null', | ||
'child connection forced to terminate due to client_idle_limit', | ||
'query_wait_timeout', | ||
'reset by peer', | ||
'Physical connection is not usable', | ||
'TCP Provider: Error code 0x68', | ||
'ORA-03114', | ||
'Packets out of order. Expected', | ||
'Adaptive Server connection failed', | ||
'Communication link failure', | ||
'No such file or directory', | ||
]; | ||
|
||
public function __construct($config, int $numberRetries = 1) | ||
{ | ||
parent::__construct($config); | ||
|
||
$this->retries = $numberRetries; | ||
} | ||
|
||
public function prepexec($stmt, $values = null) | ||
{ | ||
$retries = 0; | ||
$retryHandler = function () use (&$retryHandler, &$retries, $stmt, $values): PDOStatement { | ||
try { | ||
return parent::prepexec($stmt, $values); | ||
} catch (Exception $err) { | ||
if ($retries < $this->retries && $this->isRetryable($err)) { | ||
$retries++; | ||
|
||
$this->disconnect(); | ||
|
||
return $retryHandler(); | ||
} | ||
|
||
throw $err; | ||
} | ||
}; | ||
|
||
return $retryHandler(); | ||
} | ||
|
||
/** | ||
* Get whether the given (PDO) exception can be fixed by reconnecting to the database. | ||
* | ||
* @param Exception $err | ||
* | ||
* @return bool | ||
*/ | ||
public static function isRetryable(Exception $err): bool | ||
{ | ||
$message = $err->getMessage(); | ||
foreach (static::$retryableErrors as $error) { | ||
if (strpos($message, $error) !== false) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} |