Skip to content
Snippets Groups Projects
Commit 0ac7842a authored by Mateusz Charytoniuk's avatar Mateusz Charytoniuk
Browse files

feat: observable tasks timeout iterator

parent 02c55fe1
No related branches found
No related tags found
No related merge requests found
Showing
with 561 additions and 580 deletions
...@@ -60,7 +60,8 @@ ...@@ -60,7 +60,8 @@
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^11.0", "phpunit/phpunit": "^11.0",
"swoole/ide-helper": "^5.1" "swoole/ide-helper": "^5.1",
"symfony/var-dumper": "^7.0"
}, },
"suggest": { "suggest": {
"ext-ds": "For better memory management", "ext-ds": "For better memory management",
......
This diff is collapsed.
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
xmlns="https://getpsalm.org/schema/config" xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
> >
<forbiddenFunctions>
<function name="dump"/>
</forbiddenFunctions>
<projectFiles> <projectFiles>
<directory name="src" /> <directory name="src" />
<file name="constants.php" /> <file name="constants.php" />
......
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance\Attribute;
use Attribute;
use Distantmagic\Resonance\Attribute as BaseAttribute;
#[Attribute(Attribute::TARGET_CLASS)]
readonly class BuildsPDOPoolConnection extends BaseAttribute
{
/**
* @param non-empty-string $name
*/
public function __construct(
public string $name,
) {}
}
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance;
use CurlHandle;
use Stringable;
readonly class CurlErrorMessage implements Stringable
{
public function __construct(private CurlHandle $curlHandle) {}
public function __toString(): string
{
return sprintf(
'curl request failed because of error: (%d)"%s"',
curl_errno($this->curlHandle),
curl_error($this->curlHandle),
);
}
}
...@@ -11,10 +11,6 @@ class CurlException extends RuntimeException ...@@ -11,10 +11,6 @@ class CurlException extends RuntimeException
{ {
public function __construct(CurlHandle $ch) public function __construct(CurlHandle $ch)
{ {
parent::__construct(sprintf( parent::__construct((string) new CurlErrorMessage($ch));
'curl request failed because of error: (%d)"%s"',
curl_errno($ch),
curl_error($ch),
));
} }
} }
...@@ -8,6 +8,7 @@ use Ds\Map; ...@@ -8,6 +8,7 @@ use Ds\Map;
use OutOfBoundsException; use OutOfBoundsException;
use PDO; use PDO;
use RuntimeException; use RuntimeException;
use Swoole\Database\PDOPool;
use Swoole\Database\PDOProxy; use Swoole\Database\PDOProxy;
readonly class DatabaseConnectionPoolRepository readonly class DatabaseConnectionPoolRepository
......
...@@ -9,7 +9,9 @@ use Distantmagic\Resonance\Attribute\RequiresPhpExtension; ...@@ -9,7 +9,9 @@ use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\Singleton; use Distantmagic\Resonance\Attribute\Singleton;
use Generator; use Generator;
use JsonSerializable; use JsonSerializable;
use Psr\Log\LoggerInterface;
use RuntimeException; use RuntimeException;
use Swoole\Coroutine;
use Swoole\Coroutine\Channel; use Swoole\Coroutine\Channel;
#[RequiresPhpExtension('curl')] #[RequiresPhpExtension('curl')]
...@@ -20,6 +22,7 @@ readonly class LlamaCppClient ...@@ -20,6 +22,7 @@ readonly class LlamaCppClient
private JsonSerializer $jsonSerializer, private JsonSerializer $jsonSerializer,
private LlamaCppConfiguration $llamaCppConfiguration, private LlamaCppConfiguration $llamaCppConfiguration,
private LlamaCppLinkBuilder $llamaCppLinkBuilder, private LlamaCppLinkBuilder $llamaCppLinkBuilder,
private LoggerInterface $logger,
) {} ) {}
public function generateCompletion(LlamaCppCompletionRequest $request): LlamaCppCompletionIterator public function generateCompletion(LlamaCppCompletionRequest $request): LlamaCppCompletionIterator
...@@ -167,31 +170,34 @@ readonly class LlamaCppClient ...@@ -167,31 +170,34 @@ readonly class LlamaCppClient
SwooleCoroutineHelper::mustGo(function () use ($channel, $path, $requestData): void { SwooleCoroutineHelper::mustGo(function () use ($channel, $path, $requestData): void {
$curlHandle = $this->createCurlHandle(); $curlHandle = $this->createCurlHandle();
try { Coroutine::defer(static function () use ($channel) {
curl_setopt($curlHandle, CURLOPT_POST, true); $channel->close();
curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $requestData); });
curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, false);
curl_setopt($curlHandle, CURLOPT_URL, $this->llamaCppLinkBuilder->build($path)); Coroutine::defer(static function () use ($curlHandle) {
curl_setopt($curlHandle, CURLOPT_WRITEFUNCTION, function (CurlHandle $curlHandle, string $data) use ($channel): int {
if ($channel->push($data, $this->llamaCppConfiguration->completionTokenTimeout)) {
return strlen($data);
}
return 0;
});
if (false === curl_exec($curlHandle)) {
$curlErrno = curl_errno($curlHandle);
if (CURLE_WRITE_ERROR !== $curlErrno) {
throw new CurlException($curlHandle);
}
} else {
$this->assertStatusCode($curlHandle, 200);
}
} finally {
curl_close($curlHandle); curl_close($curlHandle);
});
curl_setopt($curlHandle, CURLOPT_TIMEOUT, 180);
curl_setopt($curlHandle, CURLOPT_POST, true);
curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $requestData);
curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, false);
curl_setopt($curlHandle, CURLOPT_URL, $this->llamaCppLinkBuilder->build($path));
curl_setopt($curlHandle, CURLOPT_WRITEFUNCTION, function (CurlHandle $curlHandle, string $data) use ($channel): int {
if ($channel->push($data, $this->llamaCppConfiguration->completionTokenTimeout)) {
return strlen($data);
}
$channel->close(); return 0;
});
if (false === curl_exec($curlHandle)) {
$curlErrno = curl_errno($curlHandle);
if (CURLE_WRITE_ERROR !== $curlErrno) {
$this->logger->error(new CurlErrorMessage($curlHandle));
}
} else {
$this->assertStatusCode($curlHandle, 200);
} }
}); });
......
...@@ -6,21 +6,32 @@ namespace Distantmagic\Resonance; ...@@ -6,21 +6,32 @@ namespace Distantmagic\Resonance;
use Closure; use Closure;
use Generator; use Generator;
use Throwable;
/**
* @template TTaskStatus of ObservableTaskStatusUpdate
*
* @template-implements ObservableTaskInterface<TTaskStatus>
*/
readonly class ObservableTask implements ObservableTaskInterface readonly class ObservableTask implements ObservableTaskInterface
{ {
/** /**
* @param Closure():Generator<TTaskStatus> $iterableTask * @var Closure():iterable<ObservableTaskStatusUpdate>
*/ */
public function __construct(private Closure $iterableTask) {} private Closure $iterableTask;
/**
* @param callable():iterable<ObservableTaskStatusUpdate> $iterableTask
*/
public function __construct(callable $iterableTask)
{
$this->iterableTask = Closure::fromCallable($iterableTask);
}
public function getIterator(): Generator public function getIterator(): Generator
{ {
yield from ($this->iterableTask)(); try {
yield from ($this->iterableTask)();
} catch (Throwable $throwable) {
yield new ObservableTaskStatusUpdate(
ObservableTaskStatus::Failed,
$throwable,
);
}
} }
} }
...@@ -7,8 +7,6 @@ namespace Distantmagic\Resonance; ...@@ -7,8 +7,6 @@ namespace Distantmagic\Resonance;
use IteratorAggregate; use IteratorAggregate;
/** /**
* @template TTaskStatus of ObservableTaskStatusUpdate * @template-extends IteratorAggregate<ObservableTaskStatusUpdate>
*
* @template-extends IteratorAggregate<TTaskStatus>
*/ */
interface ObservableTaskInterface extends IteratorAggregate {} interface ObservableTaskInterface extends IteratorAggregate {}
...@@ -8,6 +8,8 @@ use Distantmagic\Resonance\Attribute\Singleton; ...@@ -8,6 +8,8 @@ use Distantmagic\Resonance\Attribute\Singleton;
use Ds\Set; use Ds\Set;
use Generator; use Generator;
use IteratorAggregate; use IteratorAggregate;
use RuntimeException;
use Swoole\Coroutine;
use Swoole\Coroutine\Channel; use Swoole\Coroutine\Channel;
use Swoole\Table; use Swoole\Table;
...@@ -41,11 +43,6 @@ readonly class ObservableTaskTable implements IteratorAggregate ...@@ -41,11 +43,6 @@ readonly class ObservableTaskTable implements IteratorAggregate
$this->table->create(); $this->table->create();
} }
public function __destruct()
{
$this->table->destroy();
}
/** /**
* @return Generator<non-empty-string,?ObservableTaskStatusUpdate> * @return Generator<non-empty-string,?ObservableTaskStatusUpdate>
*/ */
...@@ -76,30 +73,37 @@ readonly class ObservableTaskTable implements IteratorAggregate ...@@ -76,30 +73,37 @@ readonly class ObservableTaskTable implements IteratorAggregate
$slotId = $this->availableRowsPool->nextAvailableRow(); $slotId = $this->availableRowsPool->nextAvailableRow();
SwooleCoroutineHelper::mustGo(function () use ($slotId, $observableTask) { SwooleCoroutineHelper::mustGo(function () use ($slotId, $observableTask) {
try { Coroutine::defer(function () use ($slotId) {
$this->table->set($slotId, [ $this->availableRowsPool->freeAvailableRow($slotId);
});
if (
!$this->table->set($slotId, [
'status' => $this->serializedPendingStatus, 'status' => $this->serializedPendingStatus,
]); ])
) {
throw new RuntimeException('Unable to set an initial slot status');
}
foreach ($observableTask as $statusUpdate) { foreach ($observableTask as $statusUpdate) {
$this->table->set($slotId, [ if (!$this->table->set($slotId, [
'status' => $this->serializer->serialize($statusUpdate), 'status' => $this->serializer->serialize($statusUpdate),
]); ])
) {
throw new RuntimeException('Unable to update a slot status.');
}
if (!$this->observableChannels->isEmpty()) { if (!$this->observableChannels->isEmpty()) {
$slotStatusUpdate = new ObservableTaskSlotStatusUpdate($slotId, $statusUpdate); $slotStatusUpdate = new ObservableTaskSlotStatusUpdate($slotId, $statusUpdate);
foreach ($this->observableChannels as $observableChannel) { foreach ($this->observableChannels as $observableChannel) {
$observableChannel->push($slotStatusUpdate); $observableChannel->push($slotStatusUpdate);
}
} }
}
if (ObservableTaskStatus::Running !== $statusUpdate->status) { if (ObservableTaskStatus::Running !== $statusUpdate->status) {
break; break;
}
} }
} finally {
$this->availableRowsPool->freeAvailableRow($slotId);
} }
}); });
......
...@@ -7,6 +7,7 @@ namespace Distantmagic\Resonance; ...@@ -7,6 +7,7 @@ namespace Distantmagic\Resonance;
use Distantmagic\Resonance\Serializer\Vanilla; use Distantmagic\Resonance\Serializer\Vanilla;
use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Swoole\Coroutine;
use Swoole\Coroutine\WaitGroup; use Swoole\Coroutine\WaitGroup;
use Swoole\Event; use Swoole\Event;
...@@ -59,23 +60,23 @@ final class ObservableTaskTableSlotStatusUpdateIteratorTest extends TestCase ...@@ -59,23 +60,23 @@ final class ObservableTaskTableSlotStatusUpdateIteratorTest extends TestCase
SwooleCoroutineHelper::mustGo(function () use ($wg) { SwooleCoroutineHelper::mustGo(function () use ($wg) {
self::assertNotNull($this->observableTaskTable); self::assertNotNull($this->observableTaskTable);
try { Coroutine::defer(static function () use ($wg) {
$iterator = new ObservableTaskTableSlotStatusUpdateIterator($this->observableTaskTable); $wg->done();
});
foreach ($iterator as $statusUpdate) { $iterator = new ObservableTaskTableSlotStatusUpdateIterator($this->observableTaskTable);
self::assertInstanceOf(ObservableTaskSlotStatusUpdate::class, $statusUpdate);
self::assertEquals('0', $statusUpdate->slotId);
if (ObservableTaskStatus::Finished === $statusUpdate->observableTaskStatusUpdate->status) { foreach ($iterator as $statusUpdate) {
self::assertEquals('test2', $statusUpdate->observableTaskStatusUpdate->data); self::assertInstanceOf(ObservableTaskSlotStatusUpdate::class, $statusUpdate);
self::assertEquals('0', $statusUpdate->slotId);
break; if (ObservableTaskStatus::Finished === $statusUpdate->observableTaskStatusUpdate->status) {
} self::assertEquals('test2', $statusUpdate->observableTaskStatusUpdate->data);
self::assertEquals('test1', $statusUpdate->observableTaskStatusUpdate->data); break;
} }
} finally {
$wg->done(); self::assertEquals('test1', $statusUpdate->observableTaskStatusUpdate->data);
} }
}); });
......
...@@ -7,6 +7,7 @@ namespace Distantmagic\Resonance; ...@@ -7,6 +7,7 @@ namespace Distantmagic\Resonance;
use Distantmagic\Resonance\Serializer\Vanilla; use Distantmagic\Resonance\Serializer\Vanilla;
use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Swoole\Coroutine;
use Swoole\Coroutine\Channel; use Swoole\Coroutine\Channel;
use Swoole\Coroutine\WaitGroup; use Swoole\Coroutine\WaitGroup;
use Swoole\Event; use Swoole\Event;
...@@ -61,19 +62,19 @@ final class ObservableTaskTableTest extends TestCase ...@@ -61,19 +62,19 @@ final class ObservableTaskTableTest extends TestCase
$wg->add(); $wg->add();
SwooleCoroutineHelper::mustGo(static function () use ($channel, $wg) { SwooleCoroutineHelper::mustGo(static function () use ($channel, $wg) {
try { Coroutine::defer(static function () use ($wg) {
$status1 = $channel->pop(); $wg->done();
});
self::assertInstanceOf(ObservableTaskSlotStatusUpdate::class, $status1); $status1 = $channel->pop();
self::assertSame(ObservableTaskStatus::Running, $status1->observableTaskStatusUpdate->status);
$status2 = $channel->pop(); self::assertInstanceOf(ObservableTaskSlotStatusUpdate::class, $status1);
self::assertSame(ObservableTaskStatus::Running, $status1->observableTaskStatusUpdate->status);
self::assertInstanceOf(ObservableTaskSlotStatusUpdate::class, $status2); $status2 = $channel->pop();
self::assertSame(ObservableTaskStatus::Finished, $status2->observableTaskStatusUpdate->status);
} finally { self::assertInstanceOf(ObservableTaskSlotStatusUpdate::class, $status2);
$wg->done(); self::assertSame(ObservableTaskStatus::Finished, $status2->observableTaskStatusUpdate->status);
}
}); });
$this->observableTaskTable?->observe($observableTask); $this->observableTaskTable?->observe($observableTask);
......
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance;
use Closure;
use Generator;
use IteratorAggregate;
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
/**
* @template-implements IteratorAggregate<ObservableTaskStatusUpdate>
*/
readonly class ObservableTaskTimeoutIterator implements IteratorAggregate
{
/**
* @var Closure():Generator<ObservableTaskStatusUpdate>
*/
private Closure $iterableTask;
/**
* @param callable():Generator<ObservableTaskStatusUpdate> $iterableTask
*/
public function __construct(
callable $iterableTask,
private float $inactivityTimeout,
) {
$this->iterableTask = Closure::fromCallable($iterableTask);
}
/**
* @return SwooleChannelIterator<ObservableTaskStatusUpdate>
*/
public function __invoke(): SwooleChannelIterator
{
return $this->getIterator();
}
/**
* @psalm-suppress UnusedVariable $generatorCoroutineId is used, just asynchronously
*
* @return SwooleChannelIterator<ObservableTaskStatusUpdate>
*/
public function getIterator(): SwooleChannelIterator
{
/**
* @var null|int $generatorCoroutineId
*/
$generatorCoroutineId = null;
$channel = new Channel(1);
$swooleTimeout = new SwooleTimeout(static function () use (&$generatorCoroutineId) {
if (is_int($generatorCoroutineId)) {
Coroutine::cancel($generatorCoroutineId);
}
});
$generatorCoroutineId = SwooleCoroutineHelper::mustGo(function () use ($channel, $swooleTimeout) {
$swooleTimeoutScheduled = $swooleTimeout->setTimeout($this->inactivityTimeout);
Coroutine::defer(static function () use ($channel) {
$channel->close();
});
Coroutine::defer(static function () use (&$swooleTimeoutScheduled) {
$swooleTimeoutScheduled->cancel();
});
foreach (($this->iterableTask)() as $observableTaskStatusUpdate) {
if (Coroutine::isCanceled()) {
break;
}
$swooleTimeoutScheduled = $swooleTimeoutScheduled->reschedule($this->inactivityTimeout);
$channel->push($observableTaskStatusUpdate, $this->inactivityTimeout);
}
});
/**
* @var SwooleChannelIterator<ObservableTaskStatusUpdate>
*/
return new SwooleChannelIterator(
channel: $channel,
timeout: $this->inactivityTimeout,
);
}
}
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance;
use Ds\Set;
use PDO;
use Swoole\ConnectionPool;
use Swoole\Database\PDOConfig;
use Swoole\Database\PDOProxy;
/**
* @psalm-suppress PropertyNotSetInConstructor swoole internals
*/
class PDOPool extends ConnectionPool
{
/**
* @param Set<PDOPoolConnectionBuilderInterface> $connectionBuilders
*/
public function __construct(
private Set $connectionBuilders,
private PDOConfig $config,
int $size,
) {
parent::__construct(
$this->createConnection(...),
$size,
PDOProxy::class,
);
}
public function get(float $timeout = -1): PDOProxy
{
/**
* @var PDOProxy $pdo
*/
$pdo = parent::get($timeout);
$pdo->reset();
return $pdo;
}
private function createConnection(): PDO
{
$pdo = $this->createPDO();
foreach ($this->connectionBuilders as $connectionBuilder) {
$pdo = $connectionBuilder->buildPDOConnection($pdo);
}
return $pdo;
}
private function createDSN(string $driver): string
{
return match ($driver) {
'mariadb', 'mysql' => $this->config->hasUnixSocket()
? sprintf(
'mysql:unix_socket=%s;dbname=%s;charset=%s',
(string) $this->config->getUnixSocket(),
$this->config->getDbname(),
$this->config->getCharset()
)
: sprintf(
'mysql:host=%s;port=%d;dbname=%s;charset=%s',
$this->config->getHost(),
$this->config->getPort(),
$this->config->getDbname(),
$this->config->getCharset()
),
'pgsql' => sprintf(
'pgsql:host=%s;port=%s;dbname=%s',
(string) ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()),
$this->config->getPort(),
$this->config->getDbname(),
),
'oci' => sprintf(
'oci:dbname=%s:%d/%s;charset=%s',
(string) ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()),
$this->config->getPort(),
$this->config->getDbname(),
$this->config->getCharset()
),
};
}
private function createPDO(): PDO
{
$driver = $this->config->getDriver();
if ('sqlite' !== $driver) {
return new PDO(
$this->createDSN($driver),
$this->config->getUsername(),
$this->config->getPassword(),
$this->config->getOptions()
);
}
return new PDO(sprintf(
'sqlite:%s',
$this->config->getDbname()
));
}
}
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance;
abstract readonly class PDOPoolConnectionBuilder implements PDOPoolConnectionBuilderInterface {}
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance;
use Ds\Map;
use Ds\Set;
readonly class PDOPoolConnectionBuilderCollection
{
/**
* @var Map<non-empty-string,Set<PDOPoolConnectionBuilderInterface>>
*/
private Map $connectionBuilders;
public function __construct()
{
$this->connectionBuilders = new Map();
}
/**
* @param non-empty-string $name
*/
public function addBuilder(
string $name,
PDOPoolConnectionBuilderInterface $pdoPoolConnectionBuilder,
): void {
$this->getBuildersForConnection($name)->add($pdoPoolConnectionBuilder);
}
/**
* @param non-empty-string $name
*
* @return Set<PDOPoolConnectionBuilderInterface>
*/
public function getBuildersForConnection(string $name): Set
{
if (!$this->connectionBuilders->hasKey($name)) {
$this->connectionBuilders->put($name, new Set());
}
return $this->connectionBuilders->get($name);
}
}
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance;
use PDO;
interface PDOPoolConnectionBuilderInterface
{
public function buildPDOConnection(PDO $pdo): PDO;
}
...@@ -8,12 +8,11 @@ use Distantmagic\Resonance\Attribute\RequiresPhpExtension; ...@@ -8,12 +8,11 @@ use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\Singleton; use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\DatabaseConfiguration; use Distantmagic\Resonance\DatabaseConfiguration;
use Distantmagic\Resonance\DatabaseConnectionPoolRepository; use Distantmagic\Resonance\DatabaseConnectionPoolRepository;
use Distantmagic\Resonance\PDOPool;
use Distantmagic\Resonance\PDOPoolConnectionBuilderCollection;
use Distantmagic\Resonance\PHPProjectFiles; use Distantmagic\Resonance\PHPProjectFiles;
use Distantmagic\Resonance\SingletonContainer; use Distantmagic\Resonance\SingletonContainer;
use Distantmagic\Resonance\SingletonProvider; use Distantmagic\Resonance\SingletonProvider;
use Swoole\Database\PDOConfig; use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
/** /**
* @template-extends SingletonProvider<DatabaseConnectionPoolRepository> * @template-extends SingletonProvider<DatabaseConnectionPoolRepository>
...@@ -24,7 +23,6 @@ final readonly class DatabaseConnectionPoolRepositoryProvider extends SingletonP ...@@ -24,7 +23,6 @@ final readonly class DatabaseConnectionPoolRepositoryProvider extends SingletonP
{ {
public function __construct( public function __construct(
private DatabaseConfiguration $databaseConfiguration, private DatabaseConfiguration $databaseConfiguration,
private PDOPoolConnectionBuilderCollection $pdoPoolConnectionBuilderCollection,
) {} ) {}
public function provide(SingletonContainer $singletons, PHPProjectFiles $phpProjectFiles): DatabaseConnectionPoolRepository public function provide(SingletonContainer $singletons, PHPProjectFiles $phpProjectFiles): DatabaseConnectionPoolRepository
...@@ -53,7 +51,6 @@ final readonly class DatabaseConnectionPoolRepositoryProvider extends SingletonP ...@@ -53,7 +51,6 @@ final readonly class DatabaseConnectionPoolRepositoryProvider extends SingletonP
} }
$pdoPool = new PDOPool( $pdoPool = new PDOPool(
$this->pdoPoolConnectionBuilderCollection->getBuildersForConnection($name),
$pdoConfig, $pdoConfig,
$connectionPoolConfiguration->poolSize, $connectionPoolConfiguration->poolSize,
); );
......
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance\SingletonProvider;
use Distantmagic\Resonance\Attribute\BuildsPDOPoolConnection;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\RequiresSingletonCollection;
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\HttpResponderCollection;
use Distantmagic\Resonance\PDOPoolConnectionBuilderCollection;
use Distantmagic\Resonance\PDOPoolConnectionBuilderInterface;
use Distantmagic\Resonance\PHPProjectFiles;
use Distantmagic\Resonance\SingletonAttribute;
use Distantmagic\Resonance\SingletonCollection;
use Distantmagic\Resonance\SingletonContainer;
use Distantmagic\Resonance\SingletonProvider;
/**
* @template-extends SingletonProvider<HttpResponderCollection>
*/
#[RequiresPhpExtension('pdo')]
#[RequiresSingletonCollection(SingletonCollection::PDOPoolConnectionBuilder)]
#[Singleton(provides: PDOPoolConnectionBuilderCollection::class)]
final readonly class PDOPoolConnectionBuilderCollectionProvider extends SingletonProvider
{
public function provide(SingletonContainer $singletons, PHPProjectFiles $phpProjectFiles): PDOPoolConnectionBuilderCollection
{
$pdoPoolConnectionBuilderCollection = new PDOPoolConnectionBuilderCollection();
foreach ($this->collectBuilders($singletons) as $builderAttribute) {
$pdoPoolConnectionBuilderCollection->addBuilder(
$builderAttribute->attribute->name,
$builderAttribute->singleton
);
}
return $pdoPoolConnectionBuilderCollection;
}
/**
* @return iterable<SingletonAttribute<PDOPoolConnectionBuilderInterface,BuildsPDOPoolConnection>>
*/
private function collectBuilders(SingletonContainer $singletons): iterable
{
return $this->collectAttributes(
$singletons,
PDOPoolConnectionBuilderInterface::class,
BuildsPDOPoolConnection::class,
);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment