forked from GNUsocial/gnu-social
[CORE][Cache] Add fast path for redis cache interactions
This commit is contained in:
parent
10ddbf692a
commit
3ba7e1804b
@ -135,26 +135,85 @@ abstract class Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function redisMaybeRecompute(string $key, callable $recompute, callable $no_recompute, string $pool = 'default', float $beta = 1.0): mixed
|
||||||
|
{
|
||||||
|
$should_recompute = $beta === \INF || !self::$redis[$pool]->exists($key);
|
||||||
|
if (!$should_recompute) {
|
||||||
|
$er = Common::config('cache', 'early_recompute');
|
||||||
|
if (\is_float($er)) {
|
||||||
|
if ($should_recompute = (mt_rand() / mt_getrandmax() > $er)) {
|
||||||
|
Log::info('Item "{key}" elected for early recomputation', ['key' => $key]);
|
||||||
|
}
|
||||||
|
} elseif ($er === true) {
|
||||||
|
if ($should_recompute = ($idletime = self::$redis[$pool]->object('idletime', $key) ?? false) && ($expiry = self::$redis[$pool]->ttl($key) ?? false) && $expiry <= $idletime / 1000 * $beta * log(random_int(1, \PHP_INT_MAX) / \PHP_INT_MAX)) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
Log::info('Item "{key}" elected for early recomputation {delta}s before its expiration', [
|
||||||
|
'key' => $key,
|
||||||
|
'delta' => sprintf('%.1f', $expiry - microtime(true)),
|
||||||
|
]);
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$should_recompute = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($should_recompute) {
|
||||||
|
return $recompute();
|
||||||
|
} else {
|
||||||
|
return $no_recompute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function set(string $key, mixed $value, string $pool = 'default')
|
public static function set(string $key, mixed $value, string $pool = 'default')
|
||||||
{
|
{
|
||||||
|
if (isset(self::$redis[$pool])) {
|
||||||
|
return self::$redis[$pool]->set($key, $value);
|
||||||
|
} else {
|
||||||
// there's no set method, must be done this way
|
// there's no set method, must be done this way
|
||||||
return self::$pools[$pool]->get($key, fn ($i) => $value, \INF);
|
return self::$pools[$pool]->get($key, fn ($i) => $value, \INF);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function get(string $key, callable $calculate, string $pool = 'default', float $beta = 1.0)
|
public static function get(string $key, callable $calculate, string $pool = 'default', float $beta = 1.0)
|
||||||
{
|
{
|
||||||
|
if (isset(self::$redis[$pool])) {
|
||||||
|
return self::redisMaybeRecompute(
|
||||||
|
$key,
|
||||||
|
recompute: function () use ($key, $calculate, $pool) {
|
||||||
|
$save = true; // Pass by reference
|
||||||
|
$res = $calculate(null, $save);
|
||||||
|
if ($save) {
|
||||||
|
self::set($key, $res, $pool);
|
||||||
|
}
|
||||||
|
return $res;
|
||||||
|
},
|
||||||
|
no_recompute: fn () => self::$redis[$pool]->get($key),
|
||||||
|
pool: $pool,
|
||||||
|
beta: $beta,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
return self::$pools[$pool]->get($key, $calculate, $beta);
|
return self::$pools[$pool]->get($key, $calculate, $beta);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function delete(string $key, string $pool = 'default'): bool
|
public static function delete(string $key, string $pool = 'default'): bool
|
||||||
{
|
{
|
||||||
|
if (isset(self::$redis[$pool])) {
|
||||||
|
return self::$redis[$pool]->del($key);
|
||||||
|
} else {
|
||||||
return self::$pools[$pool]->delete($key);
|
return self::$pools[$pool]->delete($key);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function exists(string $key, string $pool = 'default'): bool
|
public static function exists(string $key, string $pool = 'default'): bool
|
||||||
{
|
{
|
||||||
|
if (isset(self::$redis[$pool])) {
|
||||||
|
return self::$redis[$pool]->exists($key);
|
||||||
|
} else {
|
||||||
|
// there's no set method, must be done this way
|
||||||
return self::$pools[$pool]->hasItem($key);
|
return self::$pools[$pool]->hasItem($key);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a list from the cache, with a different implementation
|
* Retrieve a list from the cache, with a different implementation
|
||||||
@ -165,26 +224,14 @@ abstract class Cache
|
|||||||
public static function getList(string $key, callable $calculate, string $pool = 'default', ?int $max_count = null, ?int $left = null, ?int $right = null, float $beta = 1.0): array
|
public static function getList(string $key, callable $calculate, string $pool = 'default', ?int $max_count = null, ?int $left = null, ?int $right = null, float $beta = 1.0): array
|
||||||
{
|
{
|
||||||
if (isset(self::$redis[$pool])) {
|
if (isset(self::$redis[$pool])) {
|
||||||
if (!($recompute = $beta === \INF || !(self::$redis[$pool]->exists($key)))) {
|
return self::redisMaybeRecompute(
|
||||||
if (\is_float($er = Common::config('cache', 'early_recompute'))) {
|
$key,
|
||||||
$recompute = (mt_rand() / mt_getrandmax() > $er);
|
recompute: function () use ($key, $calculate, $pool, $max_count, $left, $right, $beta) {
|
||||||
Log::info('Item "{key}" elected for early recomputation', ['key' => $key]);
|
|
||||||
} else {
|
|
||||||
if ($recompute = ($idletime = self::$redis[$pool]->object('idletime', $key) ?? false) && ($expiry = self::$redis[$pool]->ttl($key) ?? false) && $expiry <= $idletime / 1000 * $beta * log(random_int(1, \PHP_INT_MAX) / \PHP_INT_MAX)) {
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
Log::info('Item "{key}" elected for early recomputation {delta}s before its expiration', [
|
|
||||||
'key' => $key,
|
|
||||||
'delta' => sprintf('%.1f', $expiry - microtime(true)),
|
|
||||||
]);
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($recompute) {
|
|
||||||
$save = true; // Pass by reference
|
$save = true; // Pass by reference
|
||||||
$res = $calculate(null, $save);
|
$res = $calculate(null, $save);
|
||||||
if ($save) {
|
if ($save) {
|
||||||
self::setList($key, $res, $pool, $max_count, $beta);
|
self::setList($key, $res, $pool, $max_count, $beta);
|
||||||
|
}
|
||||||
$offset = $left ?? 0;
|
$offset = $left ?? 0;
|
||||||
if (\is_null($right) && \is_null($max_count)) {
|
if (\is_null($right) && \is_null($max_count)) {
|
||||||
$length = null;
|
$length = null;
|
||||||
@ -192,9 +239,11 @@ abstract class Cache
|
|||||||
$length = ($right ?? $max_count) - $offset;
|
$length = ($right ?? $max_count) - $offset;
|
||||||
}
|
}
|
||||||
return \array_slice($res, $offset, $length);
|
return \array_slice($res, $offset, $length);
|
||||||
}
|
},
|
||||||
}
|
no_recompute: fn () => self::$redis[$pool]->lRange($key, $left ?? 0, ($right ?? $max_count ?? 0) - 1),
|
||||||
return self::$redis[$pool]->lRange($key, $left ?? 0, ($right ?? $max_count ?? 0) - 1);
|
pool: $pool,
|
||||||
|
beta: $beta,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return self::get($key, function () use ($calculate, $max_count) {
|
return self::get($key, function () use ($calculate, $max_count) {
|
||||||
$save = true;
|
$save = true;
|
||||||
@ -274,30 +323,20 @@ abstract class Cache
|
|||||||
public static function getHashMap(string $map_key, callable $calculate, string $pool = 'default', float $beta = 1.0): array
|
public static function getHashMap(string $map_key, callable $calculate, string $pool = 'default', float $beta = 1.0): array
|
||||||
{
|
{
|
||||||
if (isset(self::$redis[$pool])) {
|
if (isset(self::$redis[$pool])) {
|
||||||
if (!($recompute = $beta === \INF || !(self::$redis[$pool]->exists($map_key)))) {
|
return self::redisMaybeRecompute(
|
||||||
if (\is_float($er = Common::config('cache', 'early_recompute'))) {
|
$map_key,
|
||||||
$recompute = (mt_rand() / mt_getrandmax() > $er);
|
recompute: function () use ($map_key, $calculate, $pool) {
|
||||||
Log::info('Item "{map_key}" elected for early recomputation', ['key' => $map_key]);
|
|
||||||
} else {
|
|
||||||
if ($recompute = ($idletime = self::$redis[$pool]->object('idletime', $map_key) ?? false) && ($expiry = self::$redis[$pool]->ttl($map_key) ?? false) && $expiry <= $idletime / 1000 * $beta * log(random_int(1, \PHP_INT_MAX) / \PHP_INT_MAX)) {
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
Log::info('Item "{key}" elected for early recomputation {delta}s before its expiration', [
|
|
||||||
'key' => $map_key,
|
|
||||||
'delta' => sprintf('%.1f', $expiry - microtime(true)),
|
|
||||||
]);
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($recompute) {
|
|
||||||
$save = true; // Pass by reference
|
$save = true; // Pass by reference
|
||||||
$res = $calculate(null, $save);
|
$res = $calculate(null, $save);
|
||||||
if ($save) {
|
if ($save) {
|
||||||
self::setHashMap($map_key, $res, $pool);
|
self::setHashMap($map_key, $res, $pool);
|
||||||
|
}
|
||||||
return $res;
|
return $res;
|
||||||
}
|
},
|
||||||
}
|
no_recompute: fn () => self::$redis[$pool]->hGetAll($map_key),
|
||||||
return self::$redis[$pool]->hGetAll($map_key);
|
pool: $pool,
|
||||||
|
beta: $beta,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user