cache persistence improvement, only update keys which have changed. More efficient, and less likely to cause race conditions.
This commit is contained in:
+60
-50
@@ -11,18 +11,17 @@ use Psr\Cache\InvalidArgumentException;
|
||||
/* We must not store the key-list item or values within this object,
|
||||
* because it can change from outside this object instance. */
|
||||
final readonly class MonitorCacheKeys implements CacheItemPoolInterface {
|
||||
/* TODO store clean/dirty status in the key-list, so that in PersistCache,
|
||||
* we can only need to update/remove specific items that changed,
|
||||
* instead of the full list */
|
||||
private const KEY_LIST = '__key_list';
|
||||
private const IS_DIRTY = '__is_dirty';
|
||||
private const CHANGE_LIST = '__chg_list';
|
||||
public const UPDATED = 1;
|
||||
public const REMOVED = 2;
|
||||
|
||||
private CacheItemPoolInterface $cache;
|
||||
|
||||
/** @throws InvalidArgumentException */
|
||||
public function __construct(CacheItemPoolInterface $cache) {
|
||||
$this->cache = $cache;
|
||||
$items = $cache->getItems([self::KEY_LIST, self::IS_DIRTY]);
|
||||
$items = $cache->getItems([self::KEY_LIST, self::CHANGE_LIST]);
|
||||
foreach ($items as $item) {
|
||||
if ( ! $item->isHit()) {
|
||||
$this->initialize();
|
||||
@@ -34,11 +33,11 @@ final readonly class MonitorCacheKeys implements CacheItemPoolInterface {
|
||||
/** @throws InvalidArgumentException */
|
||||
private function initialize(): void {
|
||||
$keyList = $this->cache->getItem(self::KEY_LIST);
|
||||
$isDirty = $this->cache->getItem(self::IS_DIRTY);
|
||||
$changeList = $this->cache->getItem(self::CHANGE_LIST);
|
||||
$keyList->set([]);
|
||||
$isDirty->set(false);
|
||||
$changeList->set([]);
|
||||
$this->cache->saveDeferred($keyList);
|
||||
$this->cache->saveDeferred($isDirty);
|
||||
$this->cache->saveDeferred($changeList);
|
||||
$this->cache->commit();
|
||||
}
|
||||
|
||||
@@ -49,22 +48,24 @@ final readonly class MonitorCacheKeys implements CacheItemPoolInterface {
|
||||
}
|
||||
|
||||
/** @throws InvalidArgumentException */
|
||||
public function isDirty(): bool {
|
||||
$isDirty = $this->cache->getItem(self::IS_DIRTY);
|
||||
return $isDirty->get() ?? false;
|
||||
public function getChanges(): array {
|
||||
$changeList = $this->cache->getItem(self::CHANGE_LIST);
|
||||
return $changeList->get() ?? [];
|
||||
}
|
||||
|
||||
/** @throws InvalidArgumentException */
|
||||
public function markClean(): void {
|
||||
$isDirty = $this->cache->getItem(self::IS_DIRTY);
|
||||
$isDirty->set(false);
|
||||
$this->cache->save($isDirty);
|
||||
$changeList = $this->cache->getItem(self::CHANGE_LIST);
|
||||
$changeList->set([]);
|
||||
$this->cache->save($changeList);
|
||||
}
|
||||
|
||||
public function getItem(string $key): CacheItemInterface {
|
||||
return $this->cache->getItem($key);
|
||||
}
|
||||
|
||||
/** @return CacheItemInterface[]
|
||||
* @throws InvalidArgumentException */
|
||||
public function getItems(array $keys = []): iterable {
|
||||
return $this->cache->getItems($keys);
|
||||
}
|
||||
@@ -86,20 +87,14 @@ final readonly class MonitorCacheKeys implements CacheItemPoolInterface {
|
||||
}
|
||||
|
||||
public function deleteItem(string $key): bool {
|
||||
if ($key === self::KEY_LIST || $key === self::IS_DIRTY) {
|
||||
throw new OutOfBoundsException(
|
||||
'Can not delete the private key list or is dirty flag'
|
||||
);
|
||||
}
|
||||
$this->isValid($key);
|
||||
$keyList = $this->cache->getItem(self::KEY_LIST);
|
||||
$isDirty = $this->cache->getItem(self::IS_DIRTY);
|
||||
$keyValues = $keyList->get();
|
||||
if (isset($keyValues[$key])) {
|
||||
unset($keyValues[$key]);
|
||||
$keyList->set($keyValues);
|
||||
$isDirty->set(true);
|
||||
$this->cache->saveDeferred($keyList);
|
||||
$this->cache->saveDeferred($isDirty);
|
||||
$this->logChange($key, MonitorCacheKeys::REMOVED);
|
||||
$this->cache->commit();
|
||||
}
|
||||
|
||||
@@ -107,25 +102,17 @@ final readonly class MonitorCacheKeys implements CacheItemPoolInterface {
|
||||
}
|
||||
|
||||
public function deleteItems(array $keys): bool {
|
||||
if (in_array(self::KEY_LIST, $keys, true) ||
|
||||
in_array(self::IS_DIRTY, $keys, true)
|
||||
) {
|
||||
throw new OutOfBoundsException(
|
||||
'Can not delete the private key list or is dirty flag'
|
||||
);
|
||||
}
|
||||
$this->allValid($keys);
|
||||
$keyList = $this->cache->getItem(self::KEY_LIST);
|
||||
$isDirty = $this->cache->getItem(self::IS_DIRTY);
|
||||
$keyValues = $keyList->get();
|
||||
foreach ($keys as $key) {
|
||||
if (isset($keyValues[$key])) {
|
||||
unset($keyValues[$key]);
|
||||
$isDirty->set(true);
|
||||
$this->logChange($key, MonitorCacheKeys::REMOVED);
|
||||
}
|
||||
}
|
||||
$keyList->set($keyValues);
|
||||
$this->cache->saveDeferred($keyList);
|
||||
$this->cache->saveDeferred($isDirty);
|
||||
$this->cache->commit();
|
||||
|
||||
return $this->cache->deleteItems($keys);
|
||||
@@ -143,25 +130,48 @@ final readonly class MonitorCacheKeys implements CacheItemPoolInterface {
|
||||
return $this->cache->saveDeferred($item);
|
||||
}
|
||||
|
||||
/** @throws InvalidArgumentException */
|
||||
private function update(CacheItemInterface $item) {
|
||||
if ($item->getKey() === self::KEY_LIST || $item->getKey() === self::IS_DIRTY) {
|
||||
throw new OutOfBoundsException(
|
||||
'Can not alter the private key list or is dirty flag'
|
||||
);
|
||||
}
|
||||
$keyList = $this->cache->getItem(self::KEY_LIST);
|
||||
$isDirty = $this->cache->getItem(self::IS_DIRTY);
|
||||
$keyValues = $keyList->get();
|
||||
$keyValues[$item->getKey()] = true;
|
||||
$keyList->set($keyValues);
|
||||
$isDirty->set(true);
|
||||
$this->cache->saveDeferred($keyList);
|
||||
$this->cache->saveDeferred($isDirty);
|
||||
$this->cache->commit();
|
||||
}
|
||||
|
||||
public function commit(): bool {
|
||||
return $this->cache->commit();
|
||||
}
|
||||
|
||||
/** @throws InvalidArgumentException|OutOfBoundsException */
|
||||
private function update(CacheItemInterface $item) {
|
||||
$this->isValid($item->getKey());
|
||||
$keyList = $this->cache->getItem(self::KEY_LIST);
|
||||
$keyValues = $keyList->get();
|
||||
$keyValues[$item->getKey()] = true;
|
||||
$keyList->set($keyValues);
|
||||
$this->logChange($item->getKey());
|
||||
$this->cache->saveDeferred($keyList);
|
||||
$this->cache->commit();
|
||||
}
|
||||
|
||||
/** @throws OutOfBoundsException */
|
||||
private function isValid(string $key): void {
|
||||
if ($key === self::KEY_LIST || $key === self::CHANGE_LIST) {
|
||||
throw new OutOfBoundsException(
|
||||
'Can not modify the private key or change lists'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** @throws OutOfBoundsException */
|
||||
private function allValid(array $keys): void {
|
||||
if (in_array(self::KEY_LIST, $keys, true) ||
|
||||
in_array(self::CHANGE_LIST, $keys, true)
|
||||
) {
|
||||
throw new OutOfBoundsException(
|
||||
'Can not modify the private key or change lists'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** @throws InvalidArgumentException */
|
||||
private function logChange(string $key, int $code = MonitorCacheKeys::UPDATED): void {
|
||||
$changeList = $this->cache->getItem(self::CHANGE_LIST);
|
||||
$changeValues = $changeList->get();
|
||||
$changeValues[$key] = $code;
|
||||
$changeList->set($changeValues);
|
||||
$this->cache->saveDeferred($changeList);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user