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

chore: use multiple redis connection pools

parent 443c38d4
Branches
Tags
No related merge requests found
......@@ -4,18 +4,17 @@ declare(strict_types=1);
namespace Distantmagic\Resonance;
use SensitiveParameter;
use Ds\Map;
readonly class RedisConfiguration
{
public function __construct(
#[SensitiveParameter]
public string $host,
#[SensitiveParameter]
public string $password,
#[SensitiveParameter]
public int $port,
#[SensitiveParameter]
public string $prefix,
) {}
/**
* @var Map<string,RedisConnectionPoolConfiguration>
*/
public Map $connectionPoolConfiguration;
public function __construct()
{
$this->connectionPoolConfiguration = new Map();
}
}
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance;
use SensitiveParameter;
readonly class RedisConnectionPoolConfiguration
{
public function __construct(
#[SensitiveParameter]
public int $dbIndex,
#[SensitiveParameter]
public string $host,
#[SensitiveParameter]
public string $password,
#[SensitiveParameter]
public int $port,
#[SensitiveParameter]
public string $prefix,
#[SensitiveParameter]
public int $timeout,
) {}
}
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance;
use Ds\Map;
use OutOfBoundsException;
use Redis;
use Swoole\Database\RedisPool;
readonly class RedisConnectionPoolRepository
{
/**
* @var Map<string,RedisPool>
*/
public Map $redisConnectionPool;
public function __construct()
{
$this->redisConnectionPool = new Map();
}
public function getConnection(string $name): Redis
{
if (!$this->redisConnectionPool->hasKey($name)) {
throw new OutOfBoundsException(sprintf(
'Redis connection pool is not configured: "%s". Available connection pools: "%s"',
$name,
$this->redisConnectionPool->keys()->join('", "'),
));
}
/**
* @var Redis
*/
return $this->redisConnectionPool->get($name)->get();
}
public function putConnection(string $name, Redis $redis): void
{
$this->redisConnectionPool->get($name)->put($redis);
}
}
......@@ -6,7 +6,6 @@ namespace Distantmagic\Resonance;
use Ds\Map;
use Redis;
use Swoole\Database\RedisPool;
use Swoole\Event;
readonly class Session
......@@ -16,11 +15,21 @@ readonly class Session
public function __construct(
RedisConfiguration $redisConfiguration,
private RedisPool $redisPool,
private RedisConnectionPoolRepository $redisConnectionPoolRepository,
private SessionConfiguration $sessionConfiguration,
public string $id,
) {
$this->redis = $this->redisPool->get();
$this->redis->setOption(Redis::OPT_PREFIX, $redisConfiguration->prefix.'session:');
$redisPrefix = $redisConfiguration
->connectionPoolConfiguration
->get($this->sessionConfiguration->redisConnectionPool)
->prefix
;
$this->redis = $this
->redisConnectionPoolRepository
->getConnection($this->sessionConfiguration->redisConnectionPool)
;
$this->redis->setOption(Redis::OPT_PREFIX, $redisPrefix.'session:');
$this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);
$this->data = new Map($this->restoreSessionData());
......@@ -29,7 +38,10 @@ readonly class Session
public function __destruct()
{
Event::defer(function () {
$this->redisPool->put($this->redis);
$this->redisConnectionPoolRepository->putConnection(
$this->sessionConfiguration->redisConnectionPool,
$this->redis,
);
});
}
......
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance;
readonly class SessionConfiguration
{
public function __construct(
public string $cookieName,
public int $cookieLifespan,
public string $redisConnectionPool,
) {}
}
......@@ -6,7 +6,6 @@ namespace Distantmagic\Resonance;
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\Event\HttpResponseReady;
use Swoole\Database\RedisPool;
use Swoole\Http\Request;
use Swoole\Http\Response;
use WeakMap;
......@@ -25,7 +24,8 @@ final readonly class SessionManager extends EventListener
public function __construct(
private EventListenerAggregate $eventListenerAggregate,
private RedisConfiguration $redisConfiguration,
private RedisPool $redisPool,
private RedisConnectionPoolRepository $redisConnectionPoolRepository,
private SessionConfiguration $sessionConfiguration,
) {
/**
* False positive, $this IS an EventListenerInterface
......@@ -69,14 +69,17 @@ final readonly class SessionManager extends EventListener
return $this->sessions->offsetGet($request);
}
if (!is_array($request->cookie) || !isset($request->cookie[DM_SESSSION_COOKIE_NAME])) {
if (
!is_array($request->cookie)
|| !isset($request->cookie[$this->sessionConfiguration->cookieName])
) {
return null;
}
/**
* @var string
*/
$sessionId = $request->cookie[DM_SESSSION_COOKIE_NAME];
$sessionId = $request->cookie[$this->sessionConfiguration->cookieName];
if (!uuid_is_valid($sessionId)) {
return null;
......@@ -88,9 +91,9 @@ final readonly class SessionManager extends EventListener
public function setSessionCookie(Response $response, Session $session): void
{
$response->cookie(
expires: time() + 60 * 60 * 24,
expires: time() + $this->sessionConfiguration->cookieLifespan,
httponly: true,
name: DM_SESSSION_COOKIE_NAME,
name: $this->sessionConfiguration->cookieName,
samesite: 'strict',
secure: true,
value: $session->id,
......@@ -115,7 +118,8 @@ final readonly class SessionManager extends EventListener
{
$session = new Session(
$this->redisConfiguration,
$this->redisPool,
$this->redisConnectionPoolRepository,
$this->sessionConfiguration,
$sessionId,
);
......
......@@ -6,17 +6,23 @@ namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\RedisConfiguration;
use Distantmagic\Resonance\RedisConnectionPoolConfiguration;
use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
use Nette\Schema\Expect;
use Nette\Schema\Schema;
/**
* @template-extends ConfigurationProvider<RedisConfiguration, object{
* host: string,
* password: string,
* port: int,
* prefix: string,
* }>
* @template-extends ConfigurationProvider<
* RedisConfiguration,
* array<string, object{
* db_index: int,
* host: string,
* password: string,
* port: int,
* prefix: string,
* timeout: int,
* }>
* >
*/
#[Singleton(provides: RedisConfiguration::class)]
final readonly class RedisConfigurationProvider extends ConfigurationProvider
......@@ -28,21 +34,38 @@ final readonly class RedisConfigurationProvider extends ConfigurationProvider
protected function getSchema(): Schema
{
return Expect::structure([
$keySchema = Expect::string()->min(1)->required();
$valueSchema = Expect::structure([
'db_index' => Expect::int()->min(0)->required(),
'host' => Expect::string()->min(1)->required(),
'password' => Expect::string()->required(),
'port' => Expect::int()->min(1)->max(65535)->required(),
'prefix' => Expect::string()->min(1)->required(),
'timeout' => Expect::int()->min(0)->required(),
]);
return Expect::arrayOf($valueSchema, $keySchema);
}
protected function provideConfiguration($validatedData): RedisConfiguration
{
return new RedisConfiguration(
host: $validatedData->host,
password: $validatedData->password,
port: $validatedData->port,
prefix: $validatedData->prefix,
);
$databaseconfiguration = new RedisConfiguration();
foreach ($validatedData as $name => $connectionPoolConfiguration) {
$databaseconfiguration->connectionPoolConfiguration->put(
$name,
new RedisConnectionPoolConfiguration(
dbIndex: $connectionPoolConfiguration->db_index,
host: $connectionPoolConfiguration->host,
password: $connectionPoolConfiguration->password,
port: $connectionPoolConfiguration->port,
prefix: $connectionPoolConfiguration->prefix,
timeout: $connectionPoolConfiguration->timeout,
),
);
}
return $databaseconfiguration;
}
}
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\ConfigurationFile;
use Distantmagic\Resonance\RedisConfiguration;
use Distantmagic\Resonance\SessionConfiguration;
use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
use Nette\Schema\Expect;
use Nette\Schema\Processor;
use Nette\Schema\Schema;
/**
* @template-extends ConfigurationProvider<SessionConfiguration, object{
* cookie_lifespan: int,
* cookie_name: string,
* redis_connection_pool: string,
* }>
*/
#[Singleton(provides: SessionConfiguration::class)]
final readonly class SessionConfigurationProvider extends ConfigurationProvider
{
public function __construct(
private ConfigurationFile $configurationFile,
private Processor $processor,
private RedisConfiguration $redisConfiguration,
) {
parent::__construct($configurationFile, $processor);
}
protected function getConfigurationKey(): string
{
return 'session';
}
protected function getSchema(): Schema
{
$redisConnectionPools = $this
->redisConfiguration
->connectionPoolConfiguration
->keys()
->toArray()
;
return Expect::structure([
'cookie_lifespan' => Expect::int()->min(1)->required(),
'cookie_name' => Expect::string()->min(1)->required(),
'redis_connection_pool' => Expect::anyOf(...$redisConnectionPools),
]);
}
protected function provideConfiguration($validatedData): SessionConfiguration
{
return new SessionConfiguration(
cookieLifespan: $validatedData->cookie_lifespan,
cookieName: $validatedData->cookie_name,
redisConnectionPool: $validatedData->redis_connection_pool,
);
}
}
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance\SingletonProvider;
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\EventDispatcherInterface;
use Distantmagic\Resonance\PHPProjectFiles;
use Distantmagic\Resonance\RedisConfiguration;
use Distantmagic\Resonance\RedisConnectionPoolRepository;
use Distantmagic\Resonance\SingletonContainer;
use Distantmagic\Resonance\SingletonProvider;
use Swoole\Database\RedisConfig;
use Swoole\Database\RedisPool;
/**
* @template-extends SingletonProvider<RedisConnectionPoolRepository>
*/
#[Singleton(provides: RedisConnectionPoolRepository::class)]
final readonly class RedisConnectionPoolRepositoryProvider extends SingletonProvider
{
public function __construct(
private RedisConfiguration $databaseConfiguration,
private EventDispatcherInterface $eventDispatcher,
) {}
public function provide(SingletonContainer $singletons, PHPProjectFiles $phpProjectFiles): RedisConnectionPoolRepository
{
$redisConnectionPoolRepository = new RedisConnectionPoolRepository();
foreach ($this->databaseConfiguration->connectionPoolConfiguration as $name => $connectionPoolConfiguration) {
$redisConfig = (new RedisConfig())
->withHost($connectionPoolConfiguration->host)
->withPort($connectionPoolConfiguration->port)
->withAuth($connectionPoolConfiguration->password)
->withDbIndex($connectionPoolConfiguration->dbIndex)
->withTimeout($connectionPoolConfiguration->timeout)
;
$redisConnectionPoolRepository->redisConnectionPool->put(
$name,
new RedisPool($redisConfig),
);
}
return $redisConnectionPoolRepository;
}
}
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance\SingletonProvider;
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\PHPProjectFiles;
use Distantmagic\Resonance\RedisConfiguration;
use Distantmagic\Resonance\SingletonContainer;
use Distantmagic\Resonance\SingletonProvider;
use Swoole\Database\RedisConfig;
/**
* @template-extends SingletonProvider<RedisConfig>
*/
#[Singleton(provides: RedisConfig::class)]
final readonly class SwooleRedisConfigProvider extends SingletonProvider
{
public function __construct(private RedisConfiguration $redisConfiguration) {}
public function provide(SingletonContainer $singletons, PHPProjectFiles $phpProjectFiles): RedisConfig
{
$redisConfig = new RedisConfig();
return $redisConfig
->withHost($this->redisConfiguration->host)
->withPort($this->redisConfiguration->port)
->withAuth($this->redisConfiguration->password)
->withDbIndex(0)
->withTimeout(1)
;
}
}
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance\SingletonProvider;
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\PHPProjectFiles;
use Distantmagic\Resonance\SingletonContainer;
use Distantmagic\Resonance\SingletonProvider;
use Swoole\Database\RedisConfig;
use Swoole\Database\RedisPool;
/**
* @template-extends SingletonProvider<RedisPool>
*/
#[Singleton(provides: RedisPool::class)]
final readonly class SwooleRedisPoolProvider extends SingletonProvider
{
public function __construct(private RedisConfig $redisConfig) {}
public function provide(SingletonContainer $singletons, PHPProjectFiles $phpProjectFiles): RedisPool
{
return new RedisPool($this->redisConfig);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment