diff --git a/lib/Doctrine/ORM/Cache/CacheConfiguration.php b/lib/Doctrine/ORM/Cache/CacheConfiguration.php index 4891cac49..eb3ec428d 100644 --- a/lib/Doctrine/ORM/Cache/CacheConfiguration.php +++ b/lib/Doctrine/ORM/Cache/CacheConfiguration.php @@ -110,7 +110,9 @@ class CacheConfiguration public function getQueryValidator() { if ($this->queryValidator === null) { - $this->queryValidator = new TimestampQueryCacheValidator(); + $this->queryValidator = new TimestampQueryCacheValidator( + $this->cacheFactory->getTimestampRegion() + ); } return $this->queryValidator; diff --git a/lib/Doctrine/ORM/Cache/TimestampQueryCacheValidator.php b/lib/Doctrine/ORM/Cache/TimestampQueryCacheValidator.php index 71ac3e828..c6404d3b2 100644 --- a/lib/Doctrine/ORM/Cache/TimestampQueryCacheValidator.php +++ b/lib/Doctrine/ORM/Cache/TimestampQueryCacheValidator.php @@ -26,15 +26,49 @@ namespace Doctrine\ORM\Cache; */ class TimestampQueryCacheValidator implements QueryCacheValidator { + /** + * @var TimestampRegion + */ + private $timestampRegion; + + /** + * @param TimestampRegion $timestampRegion + */ + public function __construct(TimestampRegion $timestampRegion) + { + $this->timestampRegion = $timestampRegion; + } + /** * {@inheritdoc} */ public function isValid(QueryCacheKey $key, QueryCacheEntry $entry) { + if ($this->regionUpdated($key, $entry)) { + return false; + } + if ($key->lifetime == 0) { return true; } return ($entry->time + $key->lifetime) > microtime(true); } + + /** + * @param QueryCacheKey $key + * @param QueryCacheEntry $entry + * + * @return bool + */ + private function regionUpdated(QueryCacheKey $key, QueryCacheEntry $entry) + { + if ($key->timestampKey === null) { + return false; + } + + $timestamp = $this->timestampRegion->get($key->timestampKey); + + return $timestamp && $timestamp->time > $entry->time; + } } diff --git a/tests/Doctrine/Tests/ORM/Cache/CacheConfigTest.php b/tests/Doctrine/Tests/ORM/Cache/CacheConfigTest.php index 321207c39..0de086387 100644 --- a/tests/Doctrine/Tests/ORM/Cache/CacheConfigTest.php +++ b/tests/Doctrine/Tests/ORM/Cache/CacheConfigTest.php @@ -6,6 +6,7 @@ use Doctrine\ORM\Cache\CacheConfiguration; use Doctrine\ORM\Cache\CacheFactory; use Doctrine\ORM\Cache\QueryCacheValidator; use Doctrine\ORM\Cache\Logging\CacheLogger; +use Doctrine\ORM\Cache\TimestampRegion; use Doctrine\Tests\DoctrineTestCase; /** @@ -67,6 +68,11 @@ class CacheConfigTest extends DoctrineTestCase public function testSetGetQueryValidator() { + $factory = $this->createMock(CacheFactory::class); + $factory->method('getTimestampRegion')->willReturn($this->createMock(TimestampRegion::class)); + + $this->config->setCacheFactory($factory); + $validator = $this->createMock(QueryCacheValidator::class); $this->assertInstanceOf('Doctrine\ORM\Cache\TimestampQueryCacheValidator', $this->config->getQueryValidator()); @@ -75,4 +81,4 @@ class CacheConfigTest extends DoctrineTestCase $this->assertEquals($validator, $this->config->getQueryValidator()); } -} \ No newline at end of file +} diff --git a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php index 97d8ce82a..b34b0f825 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php @@ -1120,4 +1120,37 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest ->setCacheable(true) ->getResult(); } -} \ No newline at end of file + + public function testQueryCacheShouldBeEvictedOnTimestampUpdate() + { + $this->loadFixturesCountries(); + $this->_em->clear(); + + $queryCount = $this->getCurrentQueryCount(); + $dql = 'SELECT country FROM Doctrine\Tests\Models\Cache\Country country'; + + $result1 = $this->_em->createQuery($dql) + ->setCacheable(true) + ->getResult(); + + $this->assertCount(2, $result1); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + + $this->_em->persist(new Country('France')); + $this->_em->flush(); + $this->_em->clear(); + + $queryCount = $this->getCurrentQueryCount(); + + $result2 = $this->_em->createQuery($dql) + ->setCacheable(true) + ->getResult(); + + $this->assertCount(3, $result2); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + + foreach ($result2 as $entity) { + $this->assertInstanceOf(Country::CLASSNAME, $entity); + } + } +}