-
-
Save Elendev/9625bb6216466520accd32d1d0aa25c4 to your computer and use it in GitHub Desktop.
KeyValueStores
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Drupal\sq_readonly\KeyValueStore; | |
use Drupal\Core\Cache\CacheBackendInterface; | |
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface; | |
use Drupal\sq_readonly\ReadonlyHelper; | |
class KeyValueDecoratorFactory implements KeyValueFactoryInterface { | |
/** | |
* @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface | |
*/ | |
private $keyValueFactory; | |
/** | |
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface[] | |
*/ | |
private $stores = []; | |
/** | |
* @var \Drupal\Core\Cache\CacheBackendInterface | |
*/ | |
private $cacheBackend; | |
public function __construct(KeyValueFactoryInterface $keyValueFactory, CacheBackendInterface $cacheBackend) { | |
$this->keyValueFactory = $keyValueFactory; | |
$this->cacheBackend = $cacheBackend; | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function get($collection) { | |
if (!ReadonlyHelper::isReadonlyEnabled()) { | |
return $this->keyValueFactory->get($collection); | |
} | |
if (empty($this->stores[$collection])) { | |
$this->stores[$collection] = new KeyValueStoreLocalCacheDecorator($this->keyValueFactory->get($collection), $this->cacheBackend); | |
} | |
return $this->stores[$collection]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Drupal\sq_readonly\KeyValueStore; | |
use Drupal\Core\Cache\CacheBackendInterface; | |
use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface; | |
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface; | |
use Drupal\sq_readonly\ReadonlyHelper; | |
class KeyValueExpirableDecoratorFactory implements KeyValueExpirableFactoryInterface { | |
/** | |
* @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface | |
*/ | |
private $keyValueFactory; | |
/** | |
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface[] | |
*/ | |
private $stores = []; | |
/** | |
* @var \Drupal\Core\Cache\CacheBackendInterface | |
*/ | |
private $cacheBackend; | |
public function __construct(KeyValueFactoryInterface $keyValueFactory, CacheBackendInterface $cacheBackend) { | |
$this->keyValueFactory = $keyValueFactory; | |
$this->cacheBackend = $cacheBackend; | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function get($collection) { | |
if (!ReadonlyHelper::isReadonlyEnabled()) { | |
return $this->keyValueFactory->get($collection); | |
} | |
if (empty($this->stores[$collection])) { | |
$this->stores[$collection] = new KeyValueStoreExpirableLocalCacheDecorator($this->keyValueFactory->get($collection), $this->cacheBackend); | |
} | |
return $this->stores[$collection]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Drupal\sq_readonly\KeyValueStore; | |
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; | |
class KeyValueStoreExpirableLocalCacheDecorator extends KeyValueStoreLocalCacheDecorator implements KeyValueStoreExpirableInterface { | |
/** | |
* @inheritDoc | |
*/ | |
public function setWithExpire($key, $value, $expire) { | |
$this->set($key, $value, $expire); | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function setWithExpireIfNotExists($key, $value, $expire) { | |
$this->setIfNotExists($key, $value, $expire); | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function setMultipleWithExpire(array $data, $expire) { | |
$this->setMultiple($data, $expire); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Drupal\sq_readonly\KeyValueStore; | |
use Drupal\Core\Cache\Cache; | |
use Drupal\Core\Cache\CacheBackendInterface; | |
use Drupal\Core\KeyValueStore\KeyValueStoreInterface; | |
class KeyValueStoreLocalCacheDecorator implements KeyValueStoreInterface { | |
const CACHE_ORIGINAL_KEYVALUE_KEY = 'original_keyvalue'; | |
const CACHE_VALUE_KEY = 'value'; | |
const CACHE_EMPTY_VALUE_KEY = 'empty_value'; | |
/** | |
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface | |
*/ | |
private $keyValueStore; | |
/** | |
* @var \Drupal\Core\Cache\CacheBackendInterface | |
*/ | |
private $cache; | |
public function __construct(KeyValueStoreInterface $keyValueStore, CacheBackendInterface $cache) { | |
$this->keyValueStore = $keyValueStore; | |
$this->cache = $cache; | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function getCollectionName() { | |
return $this->keyValueStore->getCollectionName(); | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function has($key) { | |
$cacheKey = $this->getCacheKey($key); | |
$cachedValue = $this->cache->get($cacheKey); | |
// No valid local override | |
if (!$this->isCacheKeyUpToDate($key) || empty($cachedValue)) { | |
return $this->keyValueStore->has($key); | |
} | |
return !$cachedValue->data[self::CACHE_EMPTY_VALUE_KEY]; | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function get($key, $default = NULL) { | |
$cacheKey = $this->getCacheKey($key); | |
$cachedValue = $this->cache->get($cacheKey); | |
if (!$this->isCacheKeyUpToDate($key) || empty($cachedValue)) { | |
return $this->keyValueStore->get($key, $default); | |
} | |
return $cachedValue->data[self::CACHE_VALUE_KEY]; | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function getMultiple(array $keys) { | |
$values = $this->keyValueStore->getMultiple($keys); | |
foreach ($values as $key => $value) { | |
$cacheKey = $this->getCacheKey($key); | |
$cachedValue = $this->cache->get($cacheKey); | |
if ($this->isCacheKeyUpToDate($key) && !empty($cachedValue)) { | |
$values[$key] = $cachedValue->data[self::CACHE_VALUE_KEY]; | |
} | |
} | |
return $values; | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function getAll() { | |
$values = $this->keyValueStore->getAll(); | |
foreach ($values as $key => $value) { | |
$cacheKey = $this->getCacheKey($key); | |
$cachedValue = $this->cache->get($cacheKey); | |
if ($this->isCacheKeyUpToDate($key) && !empty($cachedValue)) { | |
$values[$key] = $cachedValue->data[self::CACHE_VALUE_KEY]; | |
} | |
} | |
return $values; | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function set($key, $value, $expire = Cache::PERMANENT) { | |
$this->cache->set($this->getCacheKey($key), [ | |
self::CACHE_ORIGINAL_KEYVALUE_KEY => $this->keyValueStore->get($key), | |
self::CACHE_VALUE_KEY => $value, | |
self::CACHE_EMPTY_VALUE_KEY => FALSE, | |
], $expire); | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function setIfNotExists($key, $value, $expire = Cache::PERMANENT) { | |
if (!$this->has($key)) { | |
$this->set($key, $value, $expire); | |
} | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function setMultiple(array $data, $expire = Cache::PERMANENT) { | |
/* | |
* It's not possible to take advantage of multiple insert in the cache here | |
* because of the expire option that is not supported in the multiple-insert | |
* option of the cache. | |
*/ | |
foreach ($data as $key => $value) { | |
$this->set($this->getCacheKey($key), $value, $expire); | |
} | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function rename($key, $new_key) { | |
/* | |
* Set the previous value as empty and add a new value | |
*/ | |
$value = $this->get($key); | |
$this->delete($key); | |
$this->set($new_key, $value); | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function delete($key) { | |
$cacheKey = $this->getCacheKey($key); | |
if (!$this->keyValueStore->has($key)) { | |
// Re-use the already non-existent value of the keyValueStore | |
$this->cache->delete($cacheKey); | |
} else { | |
// Replace the existent keyValueStore value by a non-existent value in the cache | |
$this->cache->set($cacheKey, [ | |
self::CACHE_ORIGINAL_KEYVALUE_KEY => $this->keyValueStore->get($key), | |
self::CACHE_VALUE_KEY => NULL, | |
self::CACHE_EMPTY_VALUE_KEY => TRUE, | |
]); | |
} | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function deleteMultiple(array $keys) { | |
foreach ($keys as $key) { | |
$this->delete($key); | |
} | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function deleteAll() { | |
$values = $this->getAll(); | |
foreach ($values as $key => $value) { | |
$this->delete($key); | |
} | |
} | |
/** | |
* The response of this function doesn't tell if the cache is set or not, only if we can rely on it (cache empty => use the keystore, | |
* cache full => use the value of the cache). | |
* @param $key | |
* | |
* @return bool true if the cache is up to date. | |
*/ | |
private function isCacheKeyUpToDate($key) { | |
$cacheKey = $this->getCacheKey($key); | |
$cachedValue = $this->cache->get($cacheKey); | |
if (!empty($cachedValue)) { | |
// Special case: the cache is badly formatted, we return false | |
if ( | |
!array_key_exists(self::CACHE_ORIGINAL_KEYVALUE_KEY, $cachedValue->data) | |
|| !array_key_exists(self::CACHE_VALUE_KEY, $cachedValue->data) | |
|| !array_key_exists(self::CACHE_EMPTY_VALUE_KEY, $cachedValue->data) | |
) { | |
return false; | |
} | |
return $cachedValue->data[self::CACHE_ORIGINAL_KEYVALUE_KEY] === $this->keyValueStore->get($key); | |
} | |
return true; | |
} | |
/** | |
* Generate a cache key based on the given key and the collection name. | |
* @param $key | |
* | |
* @return string | |
*/ | |
private function getCacheKey($key) { | |
return 'KeyValueStoreDecorator:' . $this->getCollectionName() . ':' . $key; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Great code! I'm trying to use it myself and test it but I can seem to find the ReadonlyHelper class.
Can you share it please?