Skip to content

Commit

Permalink
Merge pull request #30 from swiftotter/22-mysql-connection-lost
Browse files Browse the repository at this point in the history
#22 - switching native PDO to custom ReconnectingPDO which prevents timeouts
  • Loading branch information
michalbiarda authored Jun 30, 2022
2 parents a2a1f04 + 05f6fd7 commit 095ce6b
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 27 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"description": "A database production to sandbox utility to sanitize data.",
"type": "library",
"require": {
"ext-pdo": "*",
"php-di/php-di": "^6.0",
"aws/aws-sdk-php": "^3.19",
"symfony/yaml": ">=2.3",
Expand Down
2 changes: 1 addition & 1 deletion src/Engines/ConnectionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface ConnectionInterface
{
public function isAvailable(): bool;

public function getConnection(): \PDO;
public function getConnection(): ReconnectingPDO;

public function getDSN(): string;

Expand Down
34 changes: 10 additions & 24 deletions src/Engines/ConnectionTrait.php
Original file line number Diff line number Diff line change
@@ -1,41 +1,27 @@
<?php
/**
* SwiftOtter_Base is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SwiftOtter_Base is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with SwiftOtter_Base. If not, see <http://www.gnu.org/licenses/>.
*
* @author Joseph Maxwell
* @copyright SwiftOtter Studios, 12/3/16
* @package default
**/

declare(strict_types=1);

namespace Driver\Engines;

use PDO;

trait ConnectionTrait
{
private $connection;
private ?ReconnectingPDO $connection = null;

public function getConnection(): \PDO
public function getConnection(): ReconnectingPDO
{
if (!$this->connection) {
$options = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
];

$this->connection = new \PDO($this->getDSN(), $this->getUser(), $this->getPassword(), $options);
$this->connection = new ReconnectingPDO($this->getDSN(), $this->getUser(), $this->getPassword(), $options);
}

return $this->connection;
}
}
}
3 changes: 2 additions & 1 deletion src/Engines/MySql/Transformation.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

use Driver\Commands\CommandInterface;
use Driver\Engines\MySql\Sandbox\Utilities;
use Driver\Engines\ReconnectingPDO;
use Driver\Engines\RemoteConnectionInterface;
use Driver\Pipeline\Environment\EnvironmentInterface;
use Driver\Pipeline\Transport\Status;
Expand Down Expand Up @@ -71,7 +72,7 @@ public function getProperties()
return $this->properties;
}

private function applyTransformationsTo(\PDO $connection, $transformations)
private function applyTransformationsTo(ReconnectingPDO $connection, $transformations)
{
array_walk($transformations, function ($query) use ($connection) {
try {
Expand Down
87 changes: 87 additions & 0 deletions src/Engines/ReconnectingPDO.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

namespace Driver\Engines;

use PDO;
use PDOException;
use PDOStatement;

/**
* @method PDOStatement|false prepare($query, array $options = [])
* @method bool beginTransaction()
* @method bool commit()
* @method bool rollBack()
* @method bool inTransaction()
* @method bool setAttribute(int $attribute, $value)
* @method int|false exec(string $statement)
* @method PDOStatement|false query(string $statement, int $mode = PDO::ATTR_DEFAULT_FETCH_MODE, $arg3 = null, array $ctorargs = [])
* @method string|false lastInsertId(?string $name)
* @method mixed errorCode()
* @method array errorInfo()
* @method mixed getAttribute(int $attribute)
* @method string|false quote(string $string, int $type = PDO::PARAM_STR)
* @method static array getAvailableDrivers()
* @method bool sqliteCreateFunction($function_name, $callback, int $num_args = -1, int $flags = 0)
* @method bool pgsqlCopyFromArray(string $tableName, array $rows, string $separator, string $nullAs, ?string $fields)
* @method bool pgsqlCopyFromFile(string $tableName, string $filename, string $separator = "\t", string $nullAs = "\\\\N", ?string $fields = null)
* @method array|false pgsqlCopyToArray(string $tableName, string $separator = "\t", string $nullAs = "\\\\N", ?string $fields = null)
* @method bool pgsqlCopyToFile(string $tableName, string $filename, string $separator = "\t", string $nullAs = "\\\\N", ?string $fields = null)
* @method string|false pgsqlLOBCreate()
* @method resource|false pgsqlLOBOpen(string $oid, string $mode = "rb")
* @method bool pgsqlLOBUnlink(string $oid)
* @method array|false pgsqlGetNotify(int $fetchMode = 0, int $timeoutMilliseconds = 0)
* @method int pgsqlGetPid()
*/
class ReconnectingPDO
{
private const MYSQL_GENERAL_ERROR_CODE = 'HY000';
private const SERVER_HAS_GONE_AWAY_ERROR_CODE = 2006;

private string $dsn;
private ?string $username;
private ?string $password;
private ?array $options;
private PDO $pdo;

/**
* @throws PDOException
*/
public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
{
$this->dsn = $dsn;
$this->username = $username;
$this->password = $password;
$this->options = $options;
$this->pdo = $this->createPDO();
}

/**
* @throws PDOException
*/
public function __call(string $name, array $arguments)
{
try {
$this->pdo->query('SELECT 1')->fetchColumn();
} catch (PDOException $e) {
if ($e->errorInfo[0] !== self::MYSQL_GENERAL_ERROR_CODE
|| $e->errorInfo[1] !== self::SERVER_HAS_GONE_AWAY_ERROR_CODE
) {
throw $e;
}
$this->pdo = $this->createPDO();
}
return $this->pdo->$name(...$arguments);
}

/**
* @throws PDOException
*/
private function createPDO(): PDO
{
$pdo = new PDO($this->dsn, $this->username, $this->password, $this->options);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
}
}
3 changes: 2 additions & 1 deletion src/System/LocalConnectionLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use DI\Container;
use Driver\Engines\ConnectionInterface;
use Driver\Engines\LocalConnectionInterface;
use Driver\Engines\ReconnectingPDO;

class LocalConnectionLoader implements LocalConnectionInterface
{
Expand All @@ -35,7 +36,7 @@ public function __construct(
$this->container = $container;
}

public function getConnection(): \PDO
public function getConnection(): ReconnectingPDO
{
return $this->get()->getConnection();
}
Expand Down

0 comments on commit 095ce6b

Please sign in to comment.