Flesh out the connection pool class.

This commit is contained in:
Dave Smith-Hayes 2024-10-06 21:30:00 -04:00
parent 07b73e7f7f
commit f5abbcb8c9
6 changed files with 54 additions and 30 deletions

View File

@ -15,7 +15,8 @@
"dotenv-org/phpdotenv-vault": "^0.2.4",
"react/react": "^1.4",
"robmorgan/phinx": "^0.16.1",
"react/mysql": "^0.7dev"
"react/mysql": "^0.7dev",
"react/async": "^4.3"
},
"require-dev": {
"phpunit/phpunit": "^11.1",

2
app/composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "525ed6c2ef7a52e8447108c1427f1b7d",
"content-hash": "df12c4f8e3bfd8ecbaa4864a9d702c27",
"packages": [
{
"name": "cakephp/chronos",

View File

@ -32,6 +32,3 @@ try {
fprintf(STDERR, $e->getMessage() . "\n");
return -1;
}

View File

@ -15,7 +15,13 @@ interface ConnectionPoolInterface
* @return bool
*/
public function hasIdleConnection(): bool;
public function setWaitTimeout(int $seconds): void;
/**
* Sets the new wait timeout for acquiring a connection.
*
* @param int $ms The amount of time in seconds.
*/
public function setWaitTimeout(int $s): void;
public function getWaitTimeout(): int;
public function getConnectionLimit(): int;

View File

@ -2,65 +2,84 @@
namespace Slovocast\Infrastructure\Database;
use React\Mysql\MysqlClient;
use Slovocast\Infrastructure\Api\Database\ConnectionPoolInterface;
use Slovocast\Infrastructure\Api\Database\PooledConnectionInterface;
use SplObjectStorage;
class ConnectionPool implements ConnectionPoolInterface
{
/**
* Set a default wait timeout for acquiring a connection to 100ms
*/
const int DEFAULT_WAIT_TIMEOUT = 100;
private array $idleConnections;
private array $activeConnections;
private int $waitTimeout;
private SplObjectStorage $idleConnections;
private SplObjectStorage $activeConnections;
private ConnectionPoolConfig $config;
public function __construct(ConnectionPoolConfig $config)
{
$this->config = $config;
for ($i = 0; $i < $config->getTotalConnections(); $i++) {
$this->idleConnections[] = new MysqlClient($this->config->getDsn());
$this->idleConnections = new SplObjectStorage();
$this->activeConnections = new SplObjectStorage();
for ($i = 0; $i < $this->config->getTotalConnections(); $i++) {
$pooledConnection = new PooledConnection($this->config->getDsnString());
$this->idleConnections->attach($$pooledConnection);
}
$this->waitTimeout = $config->getPoolWaitTimeout();
}
public function getTotalIdleConnections(): int
{
// TODO: Implement getTotalIdleConnections() method.
return $this->idleConnections->count();
}
public function getTotalActiveConnections(): int
{
// TODO: Implement getTotalActiveConnections() method.
return $this->activeConnections->count();
}
public function hasIdleConnection(): bool
{
// TODO: Implement hasIdleConnection() method.
return $this->idleConnections->count() > 0;
}
public function setWaitTimeout(int $seconds): void
public function setWaitTimeout(int $s): void
{
// TODO: Implement setWaitTimeout() method.
$this->waitTimeout = $s;
}
public function getWaitTimeout(): int
{
// TODO: Implement getWaitTimeout() method.
return $this->waitTimeout;
}
public function getConnectionLimit(): int
{
// TODO: Implement getConnectionLimit() method.
return $this->config->getTotalConnections();
}
/**
* @TODO Throw an exception when a total timeout is exceeded. We do not want
* to get into an infinite loop.
*
* @return PooledConnectionInterface
*/
public function getConnection(): PooledConnectionInterface
{
// TODO: Implement getConnection() method.
if (!$this->hasIdleConnection()) {
\React\Async\delay((float) $this->getWaitTimeout());
return this->getConnection();
}
$conn = $this->idleConnections->current();
$this->idleConnections->detach($conn);
$this->activeConnections->attach($conn);
return $conn;
}
public function releaseConnection(PooledConnectionInterface $connection): void
{
// TODO: Implement releaseConnection() method.
if ($this->activeConnections->contains($connection)) {
$this->activeConnections->detach($connection);
$this->idleConnections->attach($connection);
}
}
}

View File

@ -2,18 +2,19 @@
namespace Slovocast\Infrastructure\Database;
use React\Mysql\MysqlClient;
class ConnectionPoolConfig
{
const DEFAULT_WAIT_TIMEOUT = 60;
const DEFAULT_TOTAL_CONNECTIONS = 10;
public function __construct(
public readonly string $username,
public readonly string $password,
public readonly string $database,
public readonly string $host,
protected int $port = 3306,
protected int $poolWaitTimeout = ConnectionPool::DEFAULT_WAIT_TIMEOUT,
protected int $totalConnections = 10,
protected int $poolWaitTimeout = self::DEFAULT_WAIT_TIMEOUT,
protected int $totalConnections = self::DEFAULT_TOTAL_CONNECTIONS
) { }
public function getPort(): int
@ -31,7 +32,7 @@ class ConnectionPoolConfig
return $this->totalConnections;
}
public function getDsn(): string
public function getDsnString(): string
{
return sprintf(
"%s:%s@%s/%s",