8000 [Cache] Add (pdo|chain) cache (adapter|simple) prune method by robfrawley · Pull Request #23603 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Cache] Add (pdo|chain) cache (adapter|simple) prune method #23603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/Symfony/Component/Cache/Adapter/ChainAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\PruneableInterface;

/**
* Chains several adapters together.
Expand All @@ -24,7 +25,7 @@
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class ChainAdapter implements AdapterInterface
class ChainAdapter implements AdapterInterface, PruneableInterface
{
private $adapters = array();
private $adapterCount;
Expand Down Expand Up @@ -231,4 +232,20 @@ public function commit()

return $committed;
}

/**
* {@inheritdoc}
*/
public function prune()
{
$pruned = true;

foreach ($this->adapters as $adapter) {
if ($adapter instanceof PruneableInterface) {
$pruned = $adapter->prune() && $pruned;
}
}

return $pruned;
}
}
3 changes: 2 additions & 1 deletion src/Symfony/Component/Cache/Adapter/PdoAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@

namespace Symfony\Component\Cache\Adapter;

use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Traits\PdoTrait;

class PdoAdapter extends AbstractAdapter
class PdoAdapter extends AbstractAdapter implements PruneableInterface
{
use PdoTrait;

Expand Down
6 changes: 3 additions & 3 deletions src/Symfony/Component/Cache/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ CHANGELOG
-----

* added PruneableInterface so PSR-6 or PSR-16 cache implementations can declare support for manual stale cache pruning
* added FilesystemTrait::prune() and PhpFilesTrait::prune() implementations
* now FilesystemAdapter, PhpFilesAdapter, FilesystemCache, and PhpFilesCache implement PruneableInterface and support
manual stale cache pruning
* added prune logic to FilesystemTrait, PhpFilesTrait, PdoTrait, and ChainTrait
* now FilesystemAdapter, PhpFilesAdapter, FilesystemCache, PhpFilesCache, PdoAdapter, PdoCache, ChainAdapter, and
ChainCache implement PruneableInterface and support manual stale cache pruning

3.3.0
-----
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/Cache/PruneableInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace Symfony\Component\Cache;

/**
* Interface for adapters and simple cache implementations that allow pruning expired items.
* Interface extends psr-6 and psr-16 caches to allow for pruning (deletion) of all expired cache items.
*/
interface PruneableInterface
{
Expand Down
19 changes: 18 additions & 1 deletion src/Symfony/Component/Cache/Simple/ChainCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\PruneableInterface;

/**
* Chains several caches together.
Expand All @@ -22,7 +23,7 @@
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class ChainCache implements CacheInterface
class ChainCache implements CacheInterface, PruneableInterface
{
private $miss;
private $caches = array();
Expand Down Expand Up @@ -219,4 +220,20 @@ public function setMultiple($values, $ttl = null)

return $saved;
}

/**
* {@inheritdoc}
*/
public function prune()
{
$pruned = true;

foreach ($this->caches as $cache) {
if ($cache instanceof PruneableInterface) {
$pruned = $cache->prune() && $pruned;
}
}

return $pruned;
}
}
3 changes: 2 additions & 1 deletion src/Symfony/Component/Cache/Simple/PdoCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@

namespace Symfony\Component\Cache\Simple;

use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Traits\PdoTrait;

class PdoCache extends AbstractCache
class PdoCache extends AbstractCache implements PruneableInterface
{
use PdoTrait;

Expand Down
14 changes: 14 additions & 0 deletions src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Cache\Tests\Adapter;

use Cache\IntegrationTests\CachePoolTest;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\PruneableInterface;

abstract class AdapterTestCase extends CachePoolTest
Expand Down Expand Up @@ -83,6 +84,7 @@ public function testPrune()
$this->fail('Test classes for pruneable caches must implement `isPruned($cache, $name)` method.');
}

/** @var PruneableInterface|CacheItemPoolInterface $cache */
$cache = $this->createCachePool();

$doSet = function ($name, $value, \DateInterval $expiresAfter = null) use ($cache) {
Expand All @@ -96,6 +98,18 @@ public function testPrune()
$cache->save($item);
};

$doSet('foo', 'foo-val', new \DateInterval('PT05S'));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may seem to duplicate tests, but the prior tests didn't check pruning of more than one item at a time; so this test ensures four items can be all pruned at once.

$doSet('bar', 'bar-val', new \DateInterval('PT10S'));
$doSet('baz', 'baz-val', new \DateInterval('PT15S'));
$doSet('qux', 'qux-val', new \DateInterval('PT20S'));

sleep(30);
$cache->prune();
$this->assertTrue($this->isPruned($cache, 'foo'));
$this->assertTrue($this->isPruned($cache, 'bar'));
$this->assertTrue($this->isPruned($cache, 'baz'));
$this->assertTrue($this->isPruned($cache, 'qux'));

$doSet('foo', 'foo-val');
$doSet('bar', 'bar-val', new \DateInterval('PT20S'));
$doSet('baz', 'baz-val', new \DateInterval('PT40S'));
Expand Down
71 changes: 71 additions & 0 deletions src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@

namespace Symfony\Component\Cache\Tests\Adapter;

use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Tests\Fixtures\ExternalAdapter;

/**
Expand Down Expand Up @@ -44,4 +46,73 @@ public function testInvalidAdapterException()
{
new ChainAdapter(array(new \stdClass()));
}

public function testPrune()
{
if (isset($this->skippedTests[__FUNCTION__])) {
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
}

$cache = new ChainAdapter(array(
$this->getPruneableMock(),
$this->getNonPruneableMock(),
$this->getPruneableMock(),
));
$this->assertTrue($cache->prune());

$cache = new ChainAdapter(array(
$this->getPruneableMock(),
$this->getFailingPruneableMock(),
$this->getPruneableMock(),
));
$this->assertFalse($cache->prune());
}

/**
* @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface
*/
private function getPruneableMock()
{
$pruneable = $this
->getMockBuilder(PruneableCacheInterface::class)
->getMock();

$pruneable
->expects($this->atLeastOnce())
->method('prune')
->will($this->returnValue(true));

return $pruneable;
}

/**
* @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface
*/
private function getFailingPruneableMock()
{
$pruneable = $this
->getMockBuilder(PruneableCacheInterface::class)
->getMock();

$pruneable
->expects($this->atLeastOnce())
->method('prune')
->will($this->returnValue(false));

return $pruneable;
}

/**
* @return \PHPUnit_Framework_MockObject_MockObject|AdapterInterface
*/
private function getNonPruneableMock()
{
return $this
->getMockBuilder(AdapterInterface::class)
->getMock();
}
}

interface PruneableCacheInterface extends PruneableInterface, AdapterInterface
{
}
3 changes: 3 additions & 0 deletions src/Symfony/Component/Cache/Tests/Adapter/PdoAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@
namespace Symfony\Component\Cache\Tests\Adapter;

use Symfony\Component\Cache\Adapter\PdoAdapter;
use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait;

/**
* @group time-sensitive
*/
class PdoAdapterTest extends AdapterTestCase
{
use PdoPruneableTrait;

protected static $dbFile;

public static function setupBeforeClass()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@

use Doctrine\DBAL\DriverManager;
use Symfony\Component\Cache\Adapter\PdoAdapter;
use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait;

/**
* @group time-sensitive
*/
class PdoDbalAdapterTest extends AdapterTestCase
{
use PdoPruneableTrait;

protected static $dbFile;

public static function setupBeforeClass()
Expand Down
14 changes: 14 additions & 0 deletions src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Cache\Tests\Simple;

use Cache\IntegrationTests\SimpleCacheTest;
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Cache\PruneableInterface;

abstract class CacheTestCase extends SimpleCacheTest
Expand Down Expand Up @@ -80,8 +81,21 @@ public function testPrune()
$this->fail('Test classes for pruneable caches must implement `isPruned($cache, $name)` method.');
}

/** @var PruneableInterface|CacheInterface $cache */
$cache = $this->createSimpleCache();

$cache->set('foo', 'foo-val', new \DateInterval('PT05S'));
$cache->set('bar', 'bar-val', new \DateInterval('PT10S'));
$cache->set('baz', 'baz-val', new \DateInterval('PT15S'));
$cache->set('qux', 'qux-val', new \DateInterval('PT20S'));

sleep(30);
$cache->prune();
$this->assertTrue($this->isPruned($cache, 'foo'));
$this->assertTrue($this->isPruned($cache, 'bar'));
$this->assertTrue($this->isPruned($cache, 'baz'));
$this->assertTrue($this->isPruned($cache, 'qux'));

$cache->set('foo', 'foo-val');
$cache->set('bar', 'bar-val', new \DateInterval('PT20S'));
$cache->set('baz', 'baz-val', new \DateInterval('PT40S'));
Expand Down
73 changes: 72 additions & 1 deletion src/Symfony/Component/Cache/Tests/Simple/ChainCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Component\Cache\Tests\Simple;

use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Simple\ArrayCache;
use Symfony\Component\Cache\Simple\ChainCache;
use Symfony\Component\Cache\Simple\FilesystemCache;
Expand Down Expand Up @@ -40,6 +42,75 @@ public function testEmptyCachesException()
*/
public function testInvalidCacheException()
{
new Chaincache(array(new \stdClass()));
new ChainCache(array(new \stdClass()));
}

public function testPrune()
{
if (isset($this->skippedTests[__FUNCTION__])) {
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
}

$cache = new ChainCache(array(
$this->getPruneableMock(),
$this->getNonPruneableMock(),
$this->getPruneableMock(),
));
$this->assertTrue($cache->prune());

$cache = new ChainCache(array(
$this->getPruneableMock(),
$this->getFailingPruneableMock(),
$this->getPruneableMock(),
));
$this->assertFalse($cache->prune());
}

/**
* @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface
*/
private function getPruneableMock()
{
$pruneable = $this
->getMockBuilder(PruneableCacheInterface::class)
->getMock();

$pruneable
->expects($this->atLeastOnce())
->method('prune')
->will($this->returnValue(true));

return $pruneable;
}

/**
* @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface
*/
private function getFailingPruneableMock()
{
$pruneable = $this
->getMockBuilder(PruneableCacheInterface::class)
->getMock();

$pruneable
->expects($this->atLeastOnce())
->method('prune')
->will($this->returnValue(false));

return $pruneable;
}

/**
* @return \PHPUnit_Framework_MockObject_MockObject|CacheInterface
*/
private function getNonPruneableMock()
{
return $this
->getMockBuilder(CacheInterface::class)
->getMock();
}
}

interface PruneableCacheInterface extends PruneableInterface, CacheInterface
{
}
Loading
0