[Contracts] Add Cache contract to extend PSR-6 with tag invalidation, callback-based computation and stampede protection
This commit is contained in:
parent
68a910f528
commit
ca6478bbbb
@ -6,3 +6,4 @@ CHANGELOG
|
||||
|
||||
* added `Service\ResetInterface` to provide a way to reset an object to its initial state
|
||||
* added `Translation\TranslatorInterface` and `Translation\TranslatorTrait`
|
||||
* added `Cache` contract to extend PSR-6 with tag invalidation, callback-based computation and stampede protection
|
||||
|
40
src/Symfony/Contracts/Cache/CacheInterface.php
Normal file
40
src/Symfony/Contracts/Cache/CacheInterface.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Contracts\Cache;
|
||||
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Gets/Stores items from/to a cache.
|
||||
*
|
||||
* On cache misses, a callback is called that should return the missing value.
|
||||
* This callback is given an ItemInterface object corresponding to the needed key,
|
||||
* that could be used e.g. for expiration control.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface CacheInterface
|
||||
{
|
||||
/**
|
||||
* @param callable(ItemInterface):mixed $callback Should return the computed value for the given key/item
|
||||
* @param float|null $beta A float that, as it grows, controls the likeliness of triggering
|
||||
* early expiration. 0 disables it, INF forces immediate expiration.
|
||||
* The default (or providing null) is implementation dependent but should
|
||||
* typically be 1.0, which should provide optimal stampede protection.
|
||||
* See https://en.wikipedia.org/wiki/Cache_stampede#Probabilistic_early_expiration
|
||||
*
|
||||
* @return mixed The value corresponding to the provided key
|
||||
*
|
||||
* @throws InvalidArgumentException When $key is not valid or when $beta is negative
|
||||
*/
|
||||
public function get(string $key, callable $callback, float $beta = null);
|
||||
}
|
43
src/Symfony/Contracts/Cache/GetForCacheItemPoolTrait.php
Normal file
43
src/Symfony/Contracts/Cache/GetForCacheItemPoolTrait.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Contracts\Cache;
|
||||
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* A simple implementation of CacheInterface::get() for PSR-6 CacheItemPoolInterface classes.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
trait GetForCacheItemPoolTrait
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get(string $key, callable $callback, float $beta = null)
|
||||
{
|
||||
if (0 > $beta) {
|
||||
throw new class(sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', \get_class($this), $beta)) extends \InvalidArgumentException implements InvalidArgumentException {
|
||||
};
|
||||
}
|
||||
|
||||
$item = $this->getItem($key);
|
||||
|
||||
if (INF === $beta || !$item->isHit()) {
|
||||
$value = $callback($item);
|
||||
$item->set($value);
|
||||
}
|
||||
|
||||
return $item->get();
|
||||
}
|
||||
}
|
58
src/Symfony/Contracts/Cache/ItemInterface.php
Normal file
58
src/Symfony/Contracts/Cache/ItemInterface.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Contracts\Cache;
|
||||
|
||||
use Psr\Cache\CacheException;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface ItemInterface extends CacheItemInterface
|
||||
{
|
||||
/**
|
||||
* References the Unix timestamp stating when the item will expire.
|
||||
*/
|
||||
const METADATA_EXPIRY = 'expiry';
|
||||
|
||||
/**
|
||||
* References the time the item took to be created, in milliseconds.
|
||||
*/
|
||||
const METADATA_CTIME = 'ctime';
|
||||
|
||||
/**
|
||||
* References the list of tags that were assigned to the item, as string[].
|
||||
*/
|
||||
const METADATA_TAGS = 'tags';
|
||||
|
||||
/**
|
||||
* Adds a tag to a cache item.
|
||||
*
|
||||
* Tags are strings that follow the same validation rules as keys.
|
||||
*
|
||||
* @param string|string[] $tags A tag or array of tags
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidArgumentException When $tag is not valid
|
||||
* @throws CacheException When the item comes from a pool that is not tag-aware
|
||||
*/
|
||||
public function tag($tags): self;
|
||||
|
||||
/**
|
||||
* Returns a list of metadata info that were saved alongside with the cached value.
|
||||
*
|
||||
* See ItemInterface::METADATA_* consts for keys potentially found in the returned array.
|
||||
*/
|
||||
public function getMetadata(): array;
|
||||
}
|
38
src/Symfony/Contracts/Cache/TagAwareCacheInterface.php
Normal file
38
src/Symfony/Contracts/Cache/TagAwareCacheInterface.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Contracts\Cache;
|
||||
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Interface for invalidating cached items using tags.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface TagAwareCacheInterface extends CacheInterface
|
||||
{
|
||||
/**
|
||||
* Invalidates cached items using tags.
|
||||
*
|
||||
* When implemented on a PSR-6 pool, invalidation should not apply
|
||||
* to deferred items. Instead, they should be committed as usual.
|
||||
* This allows replacing old tagged values by new ones without
|
||||
* race conditions.
|
||||
*
|
||||
* @param string[] $tags An array of tags to invalidate
|
||||
*
|
||||
* @return bool True on success
|
||||
*
|
||||
* @throws InvalidArgumentException When $tags is not valid
|
||||
*/
|
||||
public function invalidateTags(array $tags);
|
||||
}
|
@ -15,6 +15,10 @@ Design Principles
|
||||
* all contracts must have a proven implementation to enter this repository;
|
||||
* they must be backward compatible with existing Symfony components.
|
||||
|
||||
Packages that implement specific contracts should list them in the "provide"
|
||||
section of their "composer.json" file, using the `symfony/*-contracts`
|
||||
convention (e.g. `"provide": { "symfony/cache-contracts": "1.0" }`).
|
||||
|
||||
FAQ
|
||||
---
|
||||
|
||||
|
@ -18,6 +18,9 @@
|
||||
"require": {
|
||||
"php": "^7.1.3"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/cache": "When using the Cache contract"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Contracts\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
|
Reference in New Issue
Block a user