Make having redis offline more survivable.

This commit is contained in:
Greyscale 2020-11-27 11:43:02 +01:00
parent 14da1e0513
commit b8bb6757a8
No known key found for this signature in database
GPG key ID: C6178C19949CFFE3
4 changed files with 79 additions and 27 deletions

View file

@ -14,6 +14,7 @@
},
"require": {
"php": ">=7.4",
"ext-apcu": "*",
"ext-curl": "*",
"ext-json": "*",
"ext-openssl": "*",

View file

@ -254,7 +254,7 @@ class App
return $faker;
});
$container->set(CachePoolChain::class, function (\Redis $redis) {
$container->set(CachePoolChain::class, function (Redis $redis) {
$caches = [];
// If apc/apcu present, add it to the pool
@ -265,7 +265,9 @@ class App
}
// If Redis is configured, add it to the pool.
$caches[] = new RedisCachePool($redis);
if($redis->isAvailable()) {
$caches[] = new RedisCachePool($redis);
}
$caches[] = new ArrayCachePool();
return new CachePoolChain($caches);
@ -287,13 +289,15 @@ class App
return $monolog;
});
$container->set(\Redis::class, function (EnvironmentService $environmentService) {
$redis = new Redis();
$redis->connect(
$container->set(Redis::class, function (EnvironmentService $environmentService) {
return (new Redis(
$environmentService->get('REDIS_HOST', 'redis'),
$environmentService->get('REDIS_PORT', 6379)
);
$environmentService->get('REDIS_PORT', 6379),
$environmentService->get('REDIS_TIMEOUT', 0.0)
));
});
$container->set(\Redis::class, function(Redis $redis){
return $redis;
});

View file

@ -4,11 +4,34 @@ namespace Benzine\Redis;
class Redis extends \Redis
{
private string $host;
private int $port;
private int $timeout;
public function __construct($host, $port = 6379, $timeout = 0.0)
{
$this->host = $host;
$this->port = $port;
$this->timeout = $timeout;
parent::__construct();
}
public function isAvailable() : bool
{
try {
$this->ping('am I human?');
return true;
}catch(\RedisException $redisException){
return false;
}
}
/** @var Lua\AbstractLuaExtension[] */
private array $scripts;
public function __call($name, $arguments)
{
\Kint::dump($name, $arguments);exit;
foreach ($this->scripts as $script) {
foreach ($script->getFunctionNames() as $functionName) {
if (strtolower($name) == strtolower($functionName)) {
@ -20,6 +43,15 @@ class Redis extends \Redis
}
}
public function get($key)
{
if(!$this->isConnected()){
parent::pconnect($this->host, $this->port, $this->timeout);
$this->initialiseExtensions();
}
parent::get($key); // TODO: Change the autogenerated stub
}
public function initialiseExtensions(): void
{
$this->scripts[] = new Lua\SetIfHigher($this);
@ -30,7 +62,6 @@ class Redis extends \Redis
public function connect($host, $port = 6379, $timeout = 0.0, $reserved = null, $retryInterval = 0, $readTimeout = 0.0): void
{
parent::connect($host, $port, $timeout, $reserved, $retryInterval, $readTimeout);
$this->initialiseExtensions();
throw new \RedisException("Do not directly call connect()");
}
}

View file

@ -2,15 +2,18 @@
namespace Benzine\Services;
use Benzine\Redis\Redis;
class SessionService implements \SessionHandlerInterface
{
protected \Redis $redis;
protected Redis $redis;
private ?bool $redisIsAvailable = null;
protected $oldID;
private int $lifetime = 43200;
private array $dirtyCheck = [];
public function __construct(\Redis $redis)
public function __construct(Redis $redis)
{
$this->redis = $redis;
}
@ -72,6 +75,13 @@ class SessionService implements \SessionHandlerInterface
return true;
}
public function useRedis() : bool {
if($this->redisIsAvailable === null){
$this->redisIsAvailable = $this->redis->isAvailable();
}
return $this->redisIsAvailable;
}
public function read($session_id)
{
if ($this->useAPCU()) {
@ -84,16 +94,17 @@ class SessionService implements \SessionHandlerInterface
$session_id = $this->oldID ? $this->oldID : $session_id;
}
$serialised = $this->redis->get("session:{$session_id}");
if (null != $serialised) {
if (!empty($this->oldID)) {
// clean up old session after regenerate
$this->redis->del("session:{$session_id}");
$this->oldID = null;
$result = '';
if($this->useRedis()) {
$serialised = $this->redis->get("session:{$session_id}");
if (null != $serialised) {
if (!empty($this->oldID)) {
// clean up old session after regenerate
$this->redis->del("session:{$session_id}");
$this->oldID = null;
}
$result = unserialize($serialised);
}
$result = unserialize($serialised);
} else {
$result = '';
}
if ($this->useAPCU()) {
@ -105,19 +116,27 @@ class SessionService implements \SessionHandlerInterface
return $result;
}
public function write($session_id, $session_data)
/**
* @param string $session_id
* @param string $session_data
* @return bool Always returns true.
*/
public function write($session_id, $session_data) : bool
{
$dirty = false;
if ($this->useAPCU()) {
$dirty = crc32(apcu_fetch('read-'.$session_id)) != crc32($session_data);
} else {
$dirty = $this->dirtyCheck['read-'.$session_id] != crc32($session_data);
}
if ($dirty) {
if ($this->useRedis() && $dirty) {
$this->redis->set("session:{$session_id}", serialize($session_data));
$this->redis->expire("session:{$session_id}", $this->getLifetime());
}
apcu_store('read-'.$session_id, $session_data);
if($this->useAPCU()) {
apcu_store('read-' . $session_id, $session_data);
}
return true;
}
@ -180,9 +199,6 @@ class SessionService implements \SessionHandlerInterface
private function useAPCU(): bool
{
// @todo fix apcu damnit
return false;
return function_exists('apcu_store');
}
}