Skip to content

Commit

Permalink
#170: misc updates after the PR gets merged
Browse files Browse the repository at this point in the history
  • Loading branch information
deminy committed Mar 1, 2024
1 parent 7f56fe0 commit 041533d
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Fixed:

* MR swoole/library#169: Fix broken requests when keep-alive is turned on in the FastCGI client. (by @NathanFreeman)
* MR swoole/library#170: Enhance database pool stability by verifying PDO connection existence while fetching. (by @DevZer0x00)
* Fix accessing undefined properties in method \Swoole\NameResolver::checkResponse(). ([commit](https://github.com/swoole/library/commit/7a6396e45f4d4517a049584a746285d6501cf71d))
* Fix the implementation of method `\Swoole\MultibyteStringObject::chunk()`. ([commit](https://github.com/swoole/library/commit/031eba5f6db2ffac66ce1cca6d1d63a213203724))
* Connection pool in Swoole does not support in-memory or temporary SQLite databases. ([commit](https://github.com/swoole/library/commit/eaf6a43f2fdd403e7d4968fd6f4bd0d1b05e48c3))
Expand Down
1 change: 1 addition & 0 deletions src/__init__.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
'core/StringObject.php',
'core/MultibyteStringObject.php',
'core/Exception/ArrayKeyNotExists.php',
'core/Exception/TimeoutException.php',
'core/ArrayObject.php',
'core/ObjectProxy.php',
'core/Coroutine/WaitGroup.php',
Expand Down
6 changes: 6 additions & 0 deletions src/core/ConnectionPool.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ public function fill(): void
}
}

/**
* Get a connection from the pool.
*
* @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.
* @return mixed|false Returns a connection object from the pool, or false if the pool is full and the timeout is reached.
*/
public function get(float $timeout = -1)
{
if ($this->pool === null) {
Expand Down
12 changes: 9 additions & 3 deletions src/core/Database/PDOPool.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,19 @@ public function __construct(protected PDOConfig $config, int $size = self::DEFAU
}, $size, PDOProxy::class);
}

/**
* Get a PDO connection from the pool. The PDO connection (a PDO object) is wrapped in a PDOProxy object returned.
*
* @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.
* @return PDOProxy|false Returns a PDOProxy object from the pool, or false if the pool is full and the timeout is reached.
* {@inheritDoc}
*/
public function get(float $timeout = -1)
{
/* @var \Swoole\Database\PDOProxy|bool $pdo */
/* @var \Swoole\Database\PDOProxy|false $pdo */
$pdo = parent::get($timeout);

if ($pdo === false) {
throw new TimeoutException();
throw new TimeoutException('Failed to get a PDO connection: The pool is at full capacity, yet all connections are currently in use.');
}

$pdo->reset();
Expand Down
37 changes: 22 additions & 15 deletions tests/unit/Database/PDOPoolTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,12 @@ public function testSqlite(): void
});
}

public function testTimeoutException()
public function testTimeoutException(): void
{
self::saveHookFlags();
self::setHookFlags(SWOOLE_HOOK_ALL);
run(function () use (&$timeoutOccured) {
$failed = false;
run(function () use (&$failed) {
$config = (new PDOConfig())
->withHost(MYSQL_SERVER_HOST)
->withPort(MYSQL_SERVER_PORT)
Expand All @@ -232,24 +233,30 @@ public function testTimeoutException()
->withPassword(MYSQL_SERVER_PWD)
;

$pool = new PDOPool($config, 1);
$timeoutOccured = false;
go(function () use ($pool, &$timeoutOccured) {
$pool = new PDOPool($config, 1);
$waitGroup = new WaitGroup(2); // A wait group to wait for the next 2 coroutines to finish.

go(function () use ($pool, $waitGroup) {
$pool->get()->exec('SELECT SLEEP(1)'); // Hold the connection for 1 second before putting it back into the pool.
$waitGroup->done();
});

go(function () use ($pool, $waitGroup, &$failed) {
Coroutine::sleep(0.1); // Sleep for 0.1 second to ensure the 1st connection is in use by the 1st coroutine.
try {
$pool->get(2);
$pool->get(0.5); // Try to get a 2nd connection from the pool within 0.5 seconds.
} catch (TimeoutException) {
$timeoutOccured = true;
$failed = true;
} finally {
$waitGroup->done();
}
});

go(function () use ($pool) {
$pdo = $pool->get(1);
$pdo->exec('SELECT SLEEP(2)');
});
});

$this->assertTrue($timeoutOccured);
$waitGroup->wait();
$pool->close();
self::restoreHookFlags();

self::restoreHookFlags();
self::assertTrue($failed, 'Failed to get a 2nd connection from the pool within 0.5 seconds');
});
}
}

0 comments on commit 041533d

Please sign in to comment.