Skip to content

Commit

Permalink
Added an error count to the database connection to ensure that the co…
Browse files Browse the repository at this point in the history
…nnection can be reset when occur too many exceptions. (#6085)
  • Loading branch information
limingxinleo authored Aug 25, 2023
1 parent 41f4668 commit fbbc604
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 44 deletions.
5 changes: 5 additions & 0 deletions src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ public function release(): void
if ($this->connection instanceof \Hyperf\Database\Connection) {
// Reset $recordsModified property of connection to false before the connection release into the pool.
$this->connection->resetRecordsModified();
if ($this->connection->getErrorCount() > 100) {
// If the error count of connection is more than 100, we think it is a bad connection,
// So we'll reset it at the next time
$this->lastUseTime = 0.0;
}
}

if ($this->transactionLevel() > 0) {
Expand Down
133 changes: 89 additions & 44 deletions tests/ConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
*/
namespace HyperfTest\DbConnection;

use Exception;
use Hyperf\Context\Context;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Database\ConnectionResolverInterface;
use Hyperf\Database\Exception\QueryException;
use Hyperf\DbConnection\Connection;
use Hyperf\DbConnection\Pool\PoolFactory;
use Hyperf\Support\Reflection\ClassInvoker;
use HyperfTest\DbConnection\Stubs\ConnectionStub;
use HyperfTest\DbConnection\Stubs\ContainerStub;
use HyperfTest\DbConnection\Stubs\PDOStub;
Expand Down Expand Up @@ -117,17 +120,21 @@ public function testPdoDontDestruct()
$pool = $container->get(PoolFactory::class)->getPool('default');
$config = $container->get(ConfigInterface::class)->get('databases.default');

$callables = [function ($connection) {
$connection->selectOne('SELECT 1;');
}, function ($connection) {
$connection->table('user')->leftJoin('user_ext', 'user.id', '=', 'user_ext.id')->get();
}];

$closes = [function ($connection) {
$connection->close();
}, function ($connection) {
$connection->reconnect();
}];
$callables = [
function ($connection) {
$connection->selectOne('SELECT 1;');
}, function ($connection) {
$connection->table('user')->leftJoin('user_ext', 'user.id', '=', 'user_ext.id')->get();
},
];

$closes = [
function ($connection) {
$connection->close();
}, function ($connection) {
$connection->reconnect();
},
];

foreach ($callables as $callable) {
foreach ($closes as $closure) {
Expand All @@ -147,45 +154,83 @@ public function testConnectionSticky()
{
$container = ContainerStub::mockReadWriteContainer();

parallel([function () use ($container) {
$resolver = $container->get(ConnectionResolverInterface::class);

/** @var \Hyperf\Database\Connection $connection */
$connection = $resolver->connection();
$connection->statement('UPDATE hyperf.test SET name = 1 WHERE id = 1;');

/** @var PDOStub $pdo */
$pdo = $connection->getPdo();
$this->assertSame('mysql:host=192.168.1.2;dbname=hyperf', $pdo->dsn);
$pdo = $connection->getReadPdo();
$this->assertSame('mysql:host=192.168.1.2;dbname=hyperf', $pdo->dsn);
}]);

parallel([function () use ($container) {
$resolver = $container->get(ConnectionResolverInterface::class);

/** @var \Hyperf\Database\Connection $connection */
$connection = $resolver->connection();

/** @var PDOStub $pdo */
$pdo = $connection->getPdo();
$this->assertSame('mysql:host=192.168.1.2;dbname=hyperf', $pdo->dsn);
$pdo = $connection->getReadPdo();
$this->assertSame('mysql:host=192.168.1.1;dbname=hyperf', $pdo->dsn);
}]);
parallel([
function () use ($container) {
$resolver = $container->get(ConnectionResolverInterface::class);

/** @var \Hyperf\Database\Connection $connection */
$connection = $resolver->connection();
$connection->statement('UPDATE hyperf.test SET name = 1 WHERE id = 1;');

/** @var PDOStub $pdo */
$pdo = $connection->getPdo();
$this->assertSame('mysql:host=192.168.1.2;dbname=hyperf', $pdo->dsn);
$pdo = $connection->getReadPdo();
$this->assertSame('mysql:host=192.168.1.2;dbname=hyperf', $pdo->dsn);
},
]);

parallel([
function () use ($container) {
$resolver = $container->get(ConnectionResolverInterface::class);

/** @var \Hyperf\Database\Connection $connection */
$connection = $resolver->connection();

/** @var PDOStub $pdo */
$pdo = $connection->getPdo();
$this->assertSame('mysql:host=192.168.1.2;dbname=hyperf', $pdo->dsn);
$pdo = $connection->getReadPdo();
$this->assertSame('mysql:host=192.168.1.1;dbname=hyperf', $pdo->dsn);
},
]);
}

public function testDbConnectionUseInDefer()
{
$container = ContainerStub::mockReadWriteContainer();

parallel([function () use ($container) {
$resolver = $container->get(ConnectionResolverInterface::class);
parallel([
function () use ($container) {
$resolver = $container->get(ConnectionResolverInterface::class);

defer(function () {
$this->assertFalse(Context::has('database.connection.default'));
});
$resolver->connection();
},
]);
}

public function testDbConnectionResetWhenThrowTooManyExceptions()
{
$container = ContainerStub::mockContainer();
$pool = $container->get(PoolFactory::class)->getPool('default');
$dbConnection = $pool->get();
$connection = $dbConnection->getConnection();
$this->assertSame(0, $connection->getErrorCount());
$id = spl_object_hash((new ClassInvoker($connection))->connection);

$dbConnection->release();
$dbConnection = $pool->get();
$connection = $dbConnection->getConnection();
$id2 = spl_object_hash((new ClassInvoker($connection))->connection);

$this->assertSame($id, $id2);

$invoker = new ClassInvoker($connection);
for ($i = 0; $i < 101; ++$i) {
try {
(new ClassInvoker($invoker->connection))->runQueryCallback('', [], fn () => throw new Exception('xxx'));
} catch (QueryException) {
}
}
$this->assertSame(101, $connection->getErrorCount());

defer(function () {
$this->assertFalse(Context::has('database.connection.default'));
});
$resolver->connection();
}]);
$dbConnection->release();
$dbConnection = $pool->get();
$connection = $dbConnection->getConnection();
$id3 = spl_object_hash((new ClassInvoker($connection))->connection);
$this->assertNotSame($id, $id3);
}
}

0 comments on commit fbbc604

Please sign in to comment.