[CORE][Cache] Fix bug where empty lists must be stored as a string in Redis (not supported natively), so we can't directly push to it, but the key still exists

This commit is contained in:
Hugo Sales 2022-03-01 11:07:21 +00:00
parent f8c1b0f71d
commit 4cc4d06b11
No known key found for this signature in database
GPG Key ID: 7D0C7EAFC9D835A0

View File

@ -248,7 +248,7 @@ 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])) {
$result = self::redisMaybeRecompute( return self::redisMaybeRecompute(
$key, $key,
recompute: /** recompute: /**
* Caculate and trim the list to the correct size * Caculate and trim the list to the correct size
@ -270,11 +270,19 @@ abstract class Cache
no_recompute: /** no_recompute: /**
* Fetch (a portion of) the list from the cache * Fetch (a portion of) the list from the cache
*/ */
fn () => self::$redis[$pool]->lRange($key, $left ?? 0, ($right ?? $max_count ?? 0) - 1), function () use ($pool, $key, $left, $right, $max_count) {
$res = self::$redis[$pool]->lRange($key, $left ?? 0, ($right ?? $max_count ?? 0) - 1);
if ($res === false) {
// Empty lists are not supported by redis, but MSGPACK stores them as a
// string, so check if they exist
return self::$redis[$pool]->get($key);
} else {
return $res;
}
},
pool: $pool, pool: $pool,
beta: $beta, beta: $beta,
); );
return empty($result) ? [] : $result; // TODO may be wrong? not sure
} else { } else {
return self::get( return self::get(
$key, $key,
@ -359,13 +367,18 @@ abstract class Cache
public static function listPushLeft(string $key, mixed $value, string $pool = 'default', ?int $max_count = null, float $beta = 1.0): void public static function listPushLeft(string $key, mixed $value, string $pool = 'default', ?int $max_count = null, float $beta = 1.0): void
{ {
if (isset(self::$redis[$pool])) { if (isset(self::$redis[$pool])) {
self::$redis[$pool] $res = self::$redis[$pool]
// doesn't need to be atomic, adding at one end, deleting at the other // doesn't need to be atomic, adding at one end, deleting at the other
->multi(Redis::PIPELINE) ->multi(Redis::PIPELINE)
->lPush($key, $value) ->lPush($key, $value)
// trim to $max_count, if given // trim to $max_count, if given
->lTrim($key, 0, ($max_count ?? 0) - 1) ->lTrim($key, 0, ($max_count ?? 0) - 1)
->exec(); ->exec();
// $res has a bool per operation in the pipeline. False means it failed
if (\in_array(false, $res)) {
// Empty lists are stored as strings, so you can't push to it. Delete it and then push
self::$redis[$pool]->multi(Redis::PIPELINE)->del($key)->lPush($key, $value)->exec();
}
} else { } else {
$res = self::get($key, fn () => [], $pool, $beta); $res = self::get($key, fn () => [], $pool, $beta);
array_unshift($res, $value); array_unshift($res, $value);
@ -382,13 +395,18 @@ abstract class Cache
public static function listPushRight(string $key, mixed $value, string $pool = 'default', ?int $max_count = null, float $beta = 1.0): void public static function listPushRight(string $key, mixed $value, string $pool = 'default', ?int $max_count = null, float $beta = 1.0): void
{ {
if (isset(self::$redis[$pool])) { if (isset(self::$redis[$pool])) {
self::$redis[$pool] $res = self::$redis[$pool]
// doesn't need to be atomic, adding at one end, deleting at the other // doesn't need to be atomic, adding at one end, deleting at the other
->multi(Redis::PIPELINE) ->multi(Redis::PIPELINE)
->rPush($key, $value) ->rPush($key, $value)
// trim to $max_count, if given // trim to $max_count, if given
->lTrim($key, -($max_count ?? 0), -1) ->lTrim($key, -($max_count ?? 0), -1)
->exec(); ->exec();
// $res has a bool per operation in the pipeline. False means it failed
if (\in_array(false, $res)) {
// Empty lists are stored as strings, so you can't push to it. Delete it and then push
self::$redis[$pool]->multi(Redis::PIPELINE)->del($key)->rPush($key, $value)->exec();
}
} else { } else {
$res = self::get($key, fn () => [], $pool, $beta); $res = self::get($key, fn () => [], $pool, $beta);
$res[] = $value; $res[] = $value;