Fix persister query cache invalidation
This commit is contained in:
parent
1bfa8f0fc3
commit
1438a59c00
@ -30,7 +30,9 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*/
|
||||
interface Cache
|
||||
{
|
||||
const DEFAULT_QUERY_REGION_NAME = 'query.cache.region';
|
||||
const DEFAULT_QUERY_REGION_NAME = 'query_cache_region';
|
||||
|
||||
const DEFAULT_TIMESTAMP_REGION_NAME = 'timestamp_cache_region';
|
||||
|
||||
/**
|
||||
* May read items from the cache, but will not add items.
|
||||
|
@ -92,4 +92,11 @@ interface CacheFactory
|
||||
* @return \Doctrine\ORM\Cache\Region The cache region.
|
||||
*/
|
||||
public function getRegion(array $cache);
|
||||
|
||||
/**
|
||||
* Build timestamp cache region
|
||||
*
|
||||
* @return \Doctrine\ORM\Cache\TimestampRegion The timestamp region.
|
||||
*/
|
||||
public function getTimestampRegion();
|
||||
}
|
||||
|
@ -22,13 +22,15 @@ namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Cache\Region;
|
||||
use Doctrine\ORM\Cache\TimestampRegion;
|
||||
|
||||
use Doctrine\ORM\Cache\RegionsConfiguration;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Cache\Region\DefaultRegion;
|
||||
use Doctrine\ORM\Cache\Region\FileLockRegion;
|
||||
use Doctrine\ORM\Cache\Region\UpdateTimestampCache;
|
||||
use Doctrine\Common\Cache\Cache as CacheDriver;
|
||||
|
||||
use Doctrine\ORM\Persisters\EntityPersister;
|
||||
use Doctrine\ORM\Persisters\CollectionPersister;
|
||||
use Doctrine\ORM\Cache\Persister\ReadOnlyCachedEntityPersister;
|
||||
@ -54,6 +56,11 @@ class DefaultCacheFactory implements CacheFactory
|
||||
*/
|
||||
private $regionsConfig;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\ORM\Cache\TimestampRegion
|
||||
*/
|
||||
private $timestampRegion;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@ -98,6 +105,15 @@ class DefaultCacheFactory implements CacheFactory
|
||||
$this->regions[$region->getName()] = $region;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Doctrine\ORM\Cache\TimestampRegion $region
|
||||
*/
|
||||
public function setTimestampRegion(TimestampRegion $region)
|
||||
{
|
||||
$this->timestampRegion = $region;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -180,9 +196,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
return $this->regions[$cache['region']];
|
||||
}
|
||||
|
||||
$region = new DefaultRegion($cache['region'], clone $this->cache, array(
|
||||
'lifetime' => $this->regionsConfig->getLifetime($cache['region'])
|
||||
));
|
||||
$region = new DefaultRegion($cache['region'], clone $this->cache, $this->regionsConfig->getLifetime($cache['region']));
|
||||
|
||||
if ($cache['usage'] === ClassMetadata::CACHE_USAGE_READ_WRITE) {
|
||||
|
||||
@ -199,4 +213,19 @@ class DefaultCacheFactory implements CacheFactory
|
||||
|
||||
return $this->regions[$cache['region']] = $region;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTimestampRegion()
|
||||
{
|
||||
if ($this->timestampRegion === null) {
|
||||
$name = Cache::DEFAULT_TIMESTAMP_REGION_NAME;
|
||||
$lifetime = $this->regionsConfig->getLifetime($name);
|
||||
|
||||
$this->timestampRegion = new UpdateTimestampCache($name, clone $this->cache, $lifetime);
|
||||
}
|
||||
|
||||
return $this->timestampRegion;
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,11 @@ class DefaultQueryCache implements QueryCache
|
||||
*/
|
||||
private $validator;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\ORM\Cache\Logging\CacheLogger
|
||||
*/
|
||||
protected $cacheLogger;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@ -72,12 +77,13 @@ class DefaultQueryCache implements QueryCache
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $em, Region $region)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->region = $region;
|
||||
$this->uow = $em->getUnitOfWork();
|
||||
$this->validator = $em->getConfiguration()
|
||||
->getSecondLevelCacheConfiguration()
|
||||
->getQueryValidator();
|
||||
$cacheConfig = $em->getConfiguration()->getSecondLevelCacheConfiguration();
|
||||
|
||||
$this->em = $em;
|
||||
$this->region = $region;
|
||||
$this->uow = $em->getUnitOfWork();
|
||||
$this->cacheLogger = $cacheConfig->getCacheLogger();
|
||||
$this->validator = $cacheConfig->getQueryValidator();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,14 +112,24 @@ class DefaultQueryCache implements QueryCache
|
||||
$hasRelation = ( ! empty($rsm->relationMap));
|
||||
$persister = $this->uow->getEntityPersister($entityName);
|
||||
$region = $persister->getCacheRegion();
|
||||
$regionName = $region->getName();
|
||||
|
||||
// @TODO - move to cache hydration componente
|
||||
foreach ($entry->result as $index => $entry) {
|
||||
|
||||
if (($entityEntry = $region->get(new EntityCacheKey($entityName, $entry['identifier']))) === null) {
|
||||
if (($entityEntry = $region->get($entityKey = new EntityCacheKey($entityName, $entry['identifier']))) === null) {
|
||||
|
||||
if ($this->cacheLogger !== null) {
|
||||
$this->cacheLogger->entityCacheMiss($regionName, $entityKey);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->cacheLogger !== null) {
|
||||
$this->cacheLogger->entityCacheHit($regionName, $entityKey);
|
||||
}
|
||||
|
||||
if ( ! $hasRelation) {
|
||||
$result[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->data, self::$hints);
|
||||
|
||||
@ -129,12 +145,21 @@ class DefaultQueryCache implements QueryCache
|
||||
|
||||
if ($assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
|
||||
if (($assocEntry = $assocRegion->get(new EntityCacheKey($assoc['targetEntity'], $assoc['identifier']))) === null) {
|
||||
if (($assocEntry = $assocRegion->get($assocKey = new EntityCacheKey($assoc['targetEntity'], $assoc['identifier']))) === null) {
|
||||
|
||||
if ($this->cacheLogger !== null) {
|
||||
$this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKey);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->data, self::$hints);
|
||||
|
||||
if ($this->cacheLogger !== null) {
|
||||
$this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKey);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -147,13 +172,22 @@ class DefaultQueryCache implements QueryCache
|
||||
|
||||
foreach ($assoc['list'] as $assocIndex => $assocId) {
|
||||
|
||||
if (($assocEntry = $assocRegion->get(new EntityCacheKey($assoc['targetEntity'], $assocId))) === null) {
|
||||
if (($assocEntry = $assocRegion->get($assocKey = new EntityCacheKey($assoc['targetEntity'], $assocId))) === null) {
|
||||
|
||||
if ($this->cacheLogger !== null) {
|
||||
$this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKey);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$element = $this->uow->createEntity($assocEntry->class, $assocEntry->data, self::$hints);
|
||||
|
||||
$collection->hydrateSet($assocIndex, $element);
|
||||
|
||||
if ($this->cacheLogger !== null) {
|
||||
$this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKey);
|
||||
}
|
||||
}
|
||||
|
||||
$data[$name] = $collection;
|
||||
|
156
lib/Doctrine/ORM/Cache/Logging/CacheLoggerChain.php
Normal file
156
lib/Doctrine/ORM/Cache/Logging/CacheLoggerChain.php
Normal file
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Cache\Logging;
|
||||
|
||||
use Doctrine\ORM\Cache\CollectionCacheKey;
|
||||
use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
|
||||
/**
|
||||
* Cache logger chain
|
||||
*
|
||||
* @since 2.5
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
class CacheLoggerChain implements CacheLogger
|
||||
{
|
||||
/**
|
||||
* @var array<\Doctrine\ORM\Cache\Logging\CacheLogger>
|
||||
*/
|
||||
private $loggers = array();
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param \Doctrine\ORM\Cache\Logging\CacheLogger $logger
|
||||
*/
|
||||
public function setLogger($name, CacheLogger $logger)
|
||||
{
|
||||
$this->loggers[$name] = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return \Doctrine\ORM\Cache\Logging\CacheLogger|null
|
||||
*/
|
||||
public function getLogger($name)
|
||||
{
|
||||
return isset($this->loggers[$name]) ? $this->loggers[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<\Doctrine\ORM\Cache\Logging\CacheLogger>
|
||||
*/
|
||||
public function getLoggers()
|
||||
{
|
||||
return $this->loggers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collectionCacheHit($regionName, CollectionCacheKey $key)
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
$logger->collectionCacheHit($regionName, $key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collectionCacheMiss($regionName, CollectionCacheKey $key)
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
$logger->collectionCacheMiss($regionName, $key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collectionCachePut($regionName, CollectionCacheKey $key)
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
$logger->collectionCachePut($regionName, $key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityCacheHit($regionName, EntityCacheKey $key)
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
$logger->entityCacheHit($regionName, $key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityCacheMiss($regionName, EntityCacheKey $key)
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
$logger->entityCacheMiss($regionName, $key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityCachePut($regionName, EntityCacheKey $key)
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
$logger->entityCachePut($regionName, $key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function queryCacheHit($regionName, QueryCacheKey $key)
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
$logger->queryCacheHit($regionName, $key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function queryCacheMiss($regionName, QueryCacheKey $key)
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
$logger->queryCacheMiss($regionName, $key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function queryCachePut($regionName, QueryCacheKey $key)
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
$logger->queryCachePut($regionName, $key);
|
||||
}
|
||||
}
|
||||
}
|
@ -173,6 +173,30 @@ class StatisticsCacheLogger implements CacheLogger
|
||||
return isset($this->cachePutCountMap[$regionName]) ? $this->cachePutCountMap[$regionName] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRegionsMiss()
|
||||
{
|
||||
return $this->cacheMissCountMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRegionsHit()
|
||||
{
|
||||
return $this->cacheHitCountMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRegionsPut()
|
||||
{
|
||||
return $this->cachePutCountMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear region statistics
|
||||
*
|
||||
|
@ -24,6 +24,7 @@ use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Cache\Region;
|
||||
use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
use Doctrine\ORM\Cache\CollectionCacheKey;
|
||||
use Doctrine\ORM\Cache\TimestampCacheKey;
|
||||
use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
@ -69,6 +70,16 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
*/
|
||||
protected $region;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\ORM\Cache\TimestampRegion
|
||||
*/
|
||||
protected $timestampRegion;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\ORM\Cache\TimestampCacheKey
|
||||
*/
|
||||
protected $timestampKey;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\ORM\Cache\EntityHydrator
|
||||
*/
|
||||
@ -109,7 +120,9 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
$this->uow = $em->getUnitOfWork();
|
||||
$this->metadataFactory = $em->getMetadataFactory();
|
||||
$this->cacheLogger = $cacheConfig->getCacheLogger();
|
||||
$this->timestampRegion = $cacheFactory->getTimestampRegion();
|
||||
$this->hydrator = $cacheFactory->buildEntityHydrator($em, $class);
|
||||
$this->timestampKey = new TimestampCacheKey($this->class->getTableName());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -218,13 +231,20 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* Generates a string of currently query
|
||||
*
|
||||
* @param array $query
|
||||
* @param string $criteria
|
||||
* @param array $orderBy
|
||||
* @param integer $limit
|
||||
* @param integer $offset
|
||||
* @param integer $timestamp
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null, $timestamp = null)
|
||||
{
|
||||
list($params) = $this->expandParameters($criteria);
|
||||
list($params) = $this->persister->expandParameters($criteria);
|
||||
|
||||
return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset);
|
||||
return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset . $timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -288,8 +308,9 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
}
|
||||
|
||||
//handle only EntityRepository#findOneBy
|
||||
$timestamp = $this->timestampRegion->get($this->timestampKey);
|
||||
$query = $this->persister->getSelectSQL($criteria, null, 0, $limit, 0, $orderBy);
|
||||
$hash = $this->getHash($query, $criteria);
|
||||
$hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null);
|
||||
$rsm = $this->getResultSetMapping();
|
||||
$querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL);
|
||||
$queryCache = $this->cache->getQueryCache($this->regionName);
|
||||
@ -326,8 +347,9 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
*/
|
||||
public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null)
|
||||
{
|
||||
$timestamp = $this->timestampRegion->get($this->timestampKey);
|
||||
$query = $this->persister->getSelectSQL($criteria, null, 0, $limit, $offset, $orderBy);
|
||||
$hash = $this->getHash($query, $criteria);
|
||||
$hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null);
|
||||
$rsm = $this->getResultSetMapping();
|
||||
$querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL);
|
||||
$queryCache = $this->cache->getQueryCache($this->regionName);
|
||||
|
@ -37,6 +37,8 @@ class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
*/
|
||||
public function afterTransactionComplete()
|
||||
{
|
||||
$isChanged = false;
|
||||
|
||||
if (isset($this->queuedCache['insert'])) {
|
||||
foreach ($this->queuedCache['insert'] as $entity) {
|
||||
|
||||
@ -47,9 +49,10 @@ class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
$class = $this->metadataFactory->getMetadataFor($className);
|
||||
}
|
||||
|
||||
$key = new EntityCacheKey($class->rootEntityName, $this->uow->getEntityIdentifier($entity));
|
||||
$entry = $this->hydrator->buildCacheEntry($class, $key, $entity);
|
||||
$cached = $this->region->put($key, $entry);
|
||||
$key = new EntityCacheKey($class->rootEntityName, $this->uow->getEntityIdentifier($entity));
|
||||
$entry = $this->hydrator->buildCacheEntry($class, $key, $entity);
|
||||
$cached = $this->region->put($key, $entry);
|
||||
$isChanged = $isChanged ?: $cached;
|
||||
|
||||
if ($this->cacheLogger && $cached) {
|
||||
$this->cacheLogger->entityCachePut($this->regionName, $key);
|
||||
@ -67,9 +70,10 @@ class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
$class = $this->metadataFactory->getMetadataFor($className);
|
||||
}
|
||||
|
||||
$key = new EntityCacheKey($class->rootEntityName, $this->uow->getEntityIdentifier($entity));
|
||||
$entry = $this->hydrator->buildCacheEntry($class, $key, $entity);
|
||||
$cached = $this->region->put($key, $entry);
|
||||
$key = new EntityCacheKey($class->rootEntityName, $this->uow->getEntityIdentifier($entity));
|
||||
$entry = $this->hydrator->buildCacheEntry($class, $key, $entity);
|
||||
$cached = $this->region->put($key, $entry);
|
||||
$isChanged = $isChanged ?: $cached;
|
||||
|
||||
if ($this->cacheLogger && $cached) {
|
||||
$this->cacheLogger->entityCachePut($this->regionName, $key);
|
||||
@ -80,9 +84,15 @@ class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
if (isset($this->queuedCache['delete'])) {
|
||||
foreach ($this->queuedCache['delete'] as $key) {
|
||||
$this->region->evict($key);
|
||||
|
||||
$isChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isChanged) {
|
||||
$this->timestampRegion->update($this->timestampKey);
|
||||
}
|
||||
|
||||
$this->queuedCache = array();
|
||||
}
|
||||
|
||||
|
@ -56,18 +56,28 @@ class ReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
*/
|
||||
public function afterTransactionComplete()
|
||||
{
|
||||
$isChanged = true;
|
||||
|
||||
if (isset($this->queuedCache['update'])) {
|
||||
foreach ($this->queuedCache['update'] as $item) {
|
||||
$this->region->evict($item['key']);
|
||||
|
||||
$isChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->queuedCache['delete'])) {
|
||||
foreach ($this->queuedCache['delete'] as $item) {
|
||||
$this->region->evict($item['key']);
|
||||
|
||||
$isChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isChanged) {
|
||||
$this->timestampRegion->update($this->timestampKey);
|
||||
}
|
||||
|
||||
$this->queuedCache = array();
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ class QueryCacheKey extends CacheKey
|
||||
* @param integer $lifetime Query lifetime
|
||||
* @param integer $cacheMode Query cache mode
|
||||
*/
|
||||
public function __construct($hash, $lifetime, $cacheMode = 3)
|
||||
public function __construct($hash, $lifetime = 0, $cacheMode = 3)
|
||||
{
|
||||
$this->hash = $hash;
|
||||
$this->lifetime = $lifetime;
|
||||
|
@ -39,33 +39,30 @@ class DefaultRegion implements Region
|
||||
/**
|
||||
* @var \Doctrine\Common\Cache\CacheProvider
|
||||
*/
|
||||
private $cache;
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
private $lifetime = 0;
|
||||
protected $lifetime = 0;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param \Doctrine\Common\Cache\CacheProvider $cache
|
||||
* @param array $configuration
|
||||
* @param integer $lifetime
|
||||
*/
|
||||
public function __construct($name, CacheProvider $cache, array $configuration = array())
|
||||
public function __construct($name, CacheProvider $cache, $lifetime = 0)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->cache = $cache;
|
||||
$this->name = $name;
|
||||
$this->cache = $cache;
|
||||
$this->lifetime = $lifetime;
|
||||
|
||||
$this->cache->setNamespace($this->name);
|
||||
|
||||
if (isset($configuration['lifetime']) && $configuration['lifetime'] > 0) {
|
||||
$this->lifetime = (integer) $configuration['lifetime'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
42
lib/Doctrine/ORM/Cache/Region/UpdateTimestampCache.php
Normal file
42
lib/Doctrine/ORM/Cache/Region/UpdateTimestampCache.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Cache\Region;
|
||||
|
||||
use Doctrine\ORM\Cache\TimestampCacheEntry;
|
||||
use Doctrine\ORM\Cache\TimestampRegion;
|
||||
use Doctrine\ORM\Cache\CacheKey;
|
||||
|
||||
/**
|
||||
* Tracks the timestamps of the most recent updates to particular keys.
|
||||
*
|
||||
* @since 2.5
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
class UpdateTimestampCache extends DefaultRegion implements TimestampRegion
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function update(CacheKey $key)
|
||||
{
|
||||
$this->put($key, new TimestampCacheEntry);
|
||||
}
|
||||
}
|
51
lib/Doctrine/ORM/Cache/TimestampCacheEntry.php
Normal file
51
lib/Doctrine/ORM/Cache/TimestampCacheEntry.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
/**
|
||||
* Timestamp cache entry
|
||||
*
|
||||
* @since 2.5
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
class TimestampCacheEntry implements CacheEntry
|
||||
{
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
public $time;
|
||||
|
||||
/**
|
||||
* @param array $result
|
||||
*/
|
||||
public function __construct($time = null)
|
||||
{
|
||||
$this->time = $time ?: microtime(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
*/
|
||||
public static function __set_state(array $values)
|
||||
{
|
||||
return new self($values['time']);
|
||||
}
|
||||
}
|
38
lib/Doctrine/ORM/Cache/TimestampCacheKey.php
Normal file
38
lib/Doctrine/ORM/Cache/TimestampCacheKey.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
/**
|
||||
* A key that identifies a timestamped space.
|
||||
*
|
||||
* @since 2.5
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
class TimestampCacheKey extends CacheKey
|
||||
{
|
||||
/**
|
||||
* @param string $space Result cache id
|
||||
*/
|
||||
public function __construct($space)
|
||||
{
|
||||
$this->hash = $space;
|
||||
}
|
||||
}
|
39
lib/Doctrine/ORM/Cache/TimestampRegion.php
Normal file
39
lib/Doctrine/ORM/Cache/TimestampRegion.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
/**
|
||||
* Defines the contract for a cache region which will specifically be used to store entity "update timestamps".
|
||||
*
|
||||
* @since 2.5
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
interface TimestampRegion extends Region
|
||||
{
|
||||
/**
|
||||
* Update an specific key into the cache region.
|
||||
*
|
||||
* @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to lock.
|
||||
*
|
||||
* @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region.
|
||||
*/
|
||||
public function update(CacheKey $key);
|
||||
}
|
@ -26,7 +26,7 @@ namespace Doctrine\ORM\Mapping;
|
||||
* @since 2.5
|
||||
*
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
* @Target({"CLASS","PROPERTY"})
|
||||
*/
|
||||
final class Cache implements Annotation
|
||||
{
|
||||
|
14
tests/Doctrine/Tests/Mocks/TimestampRegionMock.php
Normal file
14
tests/Doctrine/Tests/Mocks/TimestampRegionMock.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Mocks;
|
||||
|
||||
use Doctrine\ORM\Cache\TimestampRegion;
|
||||
use Doctrine\ORM\Cache\CacheKey;
|
||||
|
||||
class TimestampRegionMock extends CacheRegionMock implements TimestampRegion
|
||||
{
|
||||
public function update(CacheKey $key)
|
||||
{
|
||||
$this->calls[__FUNCTION__][] = array('key' => $key);
|
||||
}
|
||||
}
|
118
tests/Doctrine/Tests/ORM/Cache/CacheLoggerChainTest.php
Normal file
118
tests/Doctrine/Tests/ORM/Cache/CacheLoggerChainTest.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Cache;
|
||||
|
||||
use Doctrine\ORM\Cache\Logging\CacheLoggerChain;
|
||||
use Doctrine\ORM\Cache\CollectionCacheKey;
|
||||
use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Doctrine\Tests\Models\Cache\State;
|
||||
use Doctrine\Tests\DoctrineTestCase;
|
||||
|
||||
/**
|
||||
* @group DDC-2183
|
||||
*/
|
||||
class CacheLoggerChainTest extends DoctrineTestCase
|
||||
{
|
||||
/**
|
||||
* @var \Doctrine\ORM\Cache\Logging\CacheLoggerChain
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject|\Doctrine\ORM\Cache\Logging\CacheLogger
|
||||
*/
|
||||
private $mock;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->logger = new CacheLoggerChain();
|
||||
$this->mock = $this->getMock('Doctrine\ORM\Cache\Logging\CacheLogger');
|
||||
}
|
||||
|
||||
public function testGetAndSetLogger()
|
||||
{
|
||||
$this->assertEmpty($this->logger->getLoggers());
|
||||
|
||||
$this->assertNull($this->logger->getLogger('mock'));
|
||||
|
||||
$this->logger->setLogger('mock', $this->mock);
|
||||
|
||||
$this->assertSame($this->mock, $this->logger->getLogger('mock'));
|
||||
$this->assertEquals(array('mock' => $this->mock), $this->logger->getLoggers());
|
||||
}
|
||||
|
||||
public function testEntityCacheChain()
|
||||
{
|
||||
$name = 'my_entity_region';
|
||||
$key = new EntityCacheKey(State::CLASSNAME, array('id' => 1));
|
||||
|
||||
$this->logger->setLogger('mock', $this->mock);
|
||||
|
||||
$this->mock->expects($this->once())
|
||||
->method('entityCacheHit')
|
||||
->with($this->equalTo($name), $this->equalTo($key));
|
||||
|
||||
$this->mock->expects($this->once())
|
||||
->method('entityCachePut')
|
||||
->with($this->equalTo($name), $this->equalTo($key));
|
||||
|
||||
$this->mock->expects($this->once())
|
||||
->method('entityCacheMiss')
|
||||
->with($this->equalTo($name), $this->equalTo($key));
|
||||
|
||||
$this->logger->entityCacheHit($name, $key);
|
||||
$this->logger->entityCachePut($name, $key);
|
||||
$this->logger->entityCacheMiss($name, $key);
|
||||
}
|
||||
|
||||
public function testCollectionCacheChain()
|
||||
{
|
||||
$name = 'my_collection_region';
|
||||
$key = new CollectionCacheKey(State::CLASSNAME, 'cities', array('id' => 1));
|
||||
|
||||
$this->logger->setLogger('mock', $this->mock);
|
||||
|
||||
$this->mock->expects($this->once())
|
||||
->method('collectionCacheHit')
|
||||
->with($this->equalTo($name), $this->equalTo($key));
|
||||
|
||||
$this->mock->expects($this->once())
|
||||
->method('collectionCachePut')
|
||||
->with($this->equalTo($name), $this->equalTo($key));
|
||||
|
||||
$this->mock->expects($this->once())
|
||||
->method('collectionCacheMiss')
|
||||
->with($this->equalTo($name), $this->equalTo($key));
|
||||
|
||||
$this->logger->collectionCacheHit($name, $key);
|
||||
$this->logger->collectionCachePut($name, $key);
|
||||
$this->logger->collectionCacheMiss($name, $key);
|
||||
}
|
||||
|
||||
public function testQueryCacheChain()
|
||||
{
|
||||
$name = 'my_query_region';
|
||||
$key = new QueryCacheKey('my_query_hash');
|
||||
|
||||
$this->logger->setLogger('mock', $this->mock);
|
||||
|
||||
$this->mock->expects($this->once())
|
||||
->method('queryCacheHit')
|
||||
->with($this->equalTo($name), $this->equalTo($key));
|
||||
|
||||
$this->mock->expects($this->once())
|
||||
->method('queryCachePut')
|
||||
->with($this->equalTo($name), $this->equalTo($key));
|
||||
|
||||
$this->mock->expects($this->once())
|
||||
->method('queryCacheMiss')
|
||||
->with($this->equalTo($name), $this->equalTo($key));
|
||||
|
||||
$this->logger->queryCacheHit($name, $key);
|
||||
$this->logger->queryCachePut($name, $key);
|
||||
$this->logger->queryCacheMiss($name, $key);
|
||||
}
|
||||
}
|
@ -525,4 +525,9 @@ class CacheFactoryDefaultQueryCacheTest extends \Doctrine\ORM\Cache\DefaultCache
|
||||
{
|
||||
return $this->region;
|
||||
}
|
||||
|
||||
public function getTimestampRegion()
|
||||
{
|
||||
return new \Doctrine\Tests\Mocks\TimestampRegionMock();
|
||||
}
|
||||
}
|
134
tests/Doctrine/Tests/ORM/Cache/StatisticsCacheLoggerTest.php
Normal file
134
tests/Doctrine/Tests/ORM/Cache/StatisticsCacheLoggerTest.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Cache;
|
||||
|
||||
use Doctrine\ORM\Cache\Logging\StatisticsCacheLogger;
|
||||
use Doctrine\ORM\Cache\CollectionCacheKey;
|
||||
use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Doctrine\Tests\Models\Cache\State;
|
||||
use Doctrine\Tests\DoctrineTestCase;
|
||||
|
||||
/**
|
||||
* @group DDC-2183
|
||||
*/
|
||||
class StatisticsCacheLoggerTest extends DoctrineTestCase
|
||||
{
|
||||
/**
|
||||
* @var \Doctrine\ORM\Cache\Logging\StatisticsCacheLogger
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->logger = new StatisticsCacheLogger();
|
||||
}
|
||||
|
||||
public function testEntityCache()
|
||||
{
|
||||
$name = 'my_entity_region';
|
||||
$key = new EntityCacheKey(State::CLASSNAME, array('id' => 1));
|
||||
|
||||
$this->logger->entityCacheHit($name, $key);
|
||||
$this->logger->entityCachePut($name, $key);
|
||||
$this->logger->entityCacheMiss($name, $key);
|
||||
|
||||
$this->assertEquals(1, $this->logger->getHitCount());
|
||||
$this->assertEquals(1, $this->logger->getPutCount());
|
||||
$this->assertEquals(1, $this->logger->getMissCount());
|
||||
$this->assertEquals(1, $this->logger->getRegionHitCount($name));
|
||||
$this->assertEquals(1, $this->logger->getRegionPutCount($name));
|
||||
$this->assertEquals(1, $this->logger->getRegionMissCount($name));
|
||||
}
|
||||
|
||||
public function testCollectionCache()
|
||||
{
|
||||
$name = 'my_collection_region';
|
||||
$key = new CollectionCacheKey(State::CLASSNAME, 'cities', array('id' => 1));
|
||||
|
||||
$this->logger->collectionCacheHit($name, $key);
|
||||
$this->logger->collectionCachePut($name, $key);
|
||||
$this->logger->collectionCacheMiss($name, $key);
|
||||
|
||||
$this->assertEquals(1, $this->logger->getHitCount());
|
||||
$this->assertEquals(1, $this->logger->getPutCount());
|
||||
$this->assertEquals(1, $this->logger->getMissCount());
|
||||
$this->assertEquals(1, $this->logger->getRegionHitCount($name));
|
||||
$this->assertEquals(1, $this->logger->getRegionPutCount($name));
|
||||
$this->assertEquals(1, $this->logger->getRegionMissCount($name));
|
||||
}
|
||||
|
||||
public function testQueryCache()
|
||||
{
|
||||
$name = 'my_query_region';
|
||||
$key = new QueryCacheKey('my_query_hash');
|
||||
|
||||
$this->logger->queryCacheHit($name, $key);
|
||||
$this->logger->queryCachePut($name, $key);
|
||||
$this->logger->queryCacheMiss($name, $key);
|
||||
|
||||
$this->assertEquals(1, $this->logger->getHitCount());
|
||||
$this->assertEquals(1, $this->logger->getPutCount());
|
||||
$this->assertEquals(1, $this->logger->getMissCount());
|
||||
$this->assertEquals(1, $this->logger->getRegionHitCount($name));
|
||||
$this->assertEquals(1, $this->logger->getRegionPutCount($name));
|
||||
$this->assertEquals(1, $this->logger->getRegionMissCount($name));
|
||||
}
|
||||
|
||||
public function testMultipleCaches()
|
||||
{
|
||||
$coolRegion = 'my_collection_region';
|
||||
$entityRegion = 'my_entity_region';
|
||||
$queryRegion = 'my_query_region';
|
||||
|
||||
$coolKey = new CollectionCacheKey(State::CLASSNAME, 'cities', array('id' => 1));
|
||||
$entityKey = new EntityCacheKey(State::CLASSNAME, array('id' => 1));
|
||||
$queryKey = new QueryCacheKey('my_query_hash');
|
||||
|
||||
$this->logger->queryCacheHit($queryRegion, $queryKey);
|
||||
$this->logger->queryCachePut($queryRegion, $queryKey);
|
||||
$this->logger->queryCacheMiss($queryRegion, $queryKey);
|
||||
|
||||
$this->logger->entityCacheHit($entityRegion, $entityKey);
|
||||
$this->logger->entityCachePut($entityRegion, $entityKey);
|
||||
$this->logger->entityCacheMiss($entityRegion, $entityKey);
|
||||
|
||||
$this->logger->collectionCacheHit($coolRegion, $coolKey);
|
||||
$this->logger->collectionCachePut($coolRegion, $coolKey);
|
||||
$this->logger->collectionCacheMiss($coolRegion, $coolKey);
|
||||
|
||||
$this->assertEquals(3, $this->logger->getHitCount());
|
||||
$this->assertEquals(3, $this->logger->getPutCount());
|
||||
$this->assertEquals(3, $this->logger->getMissCount());
|
||||
|
||||
$this->assertEquals(1, $this->logger->getRegionHitCount($queryRegion));
|
||||
$this->assertEquals(1, $this->logger->getRegionPutCount($queryRegion));
|
||||
$this->assertEquals(1, $this->logger->getRegionMissCount($queryRegion));
|
||||
|
||||
$this->assertEquals(1, $this->logger->getRegionHitCount($coolRegion));
|
||||
$this->assertEquals(1, $this->logger->getRegionPutCount($coolRegion));
|
||||
$this->assertEquals(1, $this->logger->getRegionMissCount($coolRegion));
|
||||
|
||||
$this->assertEquals(1, $this->logger->getRegionHitCount($entityRegion));
|
||||
$this->assertEquals(1, $this->logger->getRegionPutCount($entityRegion));
|
||||
$this->assertEquals(1, $this->logger->getRegionMissCount($entityRegion));
|
||||
|
||||
$miss = $this->logger->getRegionsMiss();
|
||||
$hit = $this->logger->getRegionsHit();
|
||||
$put = $this->logger->getRegionsPut();
|
||||
|
||||
$this->assertArrayHasKey($coolRegion, $miss);
|
||||
$this->assertArrayHasKey($queryRegion, $miss);
|
||||
$this->assertArrayHasKey($entityRegion, $miss);
|
||||
|
||||
$this->assertArrayHasKey($coolRegion, $put);
|
||||
$this->assertArrayHasKey($queryRegion, $put);
|
||||
$this->assertArrayHasKey($entityRegion, $put);
|
||||
|
||||
$this->assertArrayHasKey($coolRegion, $hit);
|
||||
$this->assertArrayHasKey($queryRegion, $hit);
|
||||
$this->assertArrayHasKey($entityRegion, $hit);
|
||||
}
|
||||
}
|
@ -138,4 +138,9 @@ class CacheFactorySecondLevelCacheConcurrentTest extends \Doctrine\ORM\Cache\Def
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
public function getTimestampRegion()
|
||||
{
|
||||
return new \Doctrine\Tests\Mocks\TimestampRegionMock();
|
||||
}
|
||||
}
|
@ -53,7 +53,7 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
|
||||
$this->assertCount(2, $result2);
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getPutCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(3, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getMissCount());
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));
|
||||
@ -70,7 +70,7 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
|
||||
$this->assertEquals($result1[1]->getName(), $result2[1]->getName());
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getPutCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(3, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getMissCount());
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));
|
||||
@ -256,7 +256,7 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
|
||||
$this->assertCount(2, $result2);
|
||||
|
||||
$this->assertEquals(3, $this->secondLevelCacheLogger->getPutCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(3, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getMissCount());
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));
|
||||
@ -273,7 +273,7 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
|
||||
$this->assertEquals($result1[1]->getName(), $result2[1]->getName());
|
||||
|
||||
$this->assertEquals(3, $this->secondLevelCacheLogger->getPutCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(3, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getMissCount());
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));
|
||||
@ -357,7 +357,7 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
|
||||
$this->assertCount(2, $result2);
|
||||
|
||||
$this->assertEquals(5, $this->secondLevelCacheLogger->getPutCount());
|
||||
$this->assertEquals(2, $this->secondLevelCacheLogger->getMissCount());
|
||||
$this->assertEquals(3, $this->secondLevelCacheLogger->getMissCount());
|
||||
$this->assertEquals(2, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));
|
||||
$this->assertEquals(2, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName()));
|
||||
|
||||
@ -639,7 +639,7 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
|
||||
$this->assertCount(2, $result2);
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getPutCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(3, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getMissCount());
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));
|
||||
@ -656,7 +656,7 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
|
||||
$this->assertEquals($result1[1]->getName(), $result2[1]->getName());
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getPutCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(3, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getMissCount());
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionPutCount($this->getDefaultQueryRegionName()));
|
||||
@ -804,7 +804,7 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
|
||||
|
||||
$this->assertNotEmpty($result3);
|
||||
$this->assertEquals($queryCount + 2, $this->getCurrentQueryCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(3, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(2, $this->secondLevelCacheLogger->getPutCount());
|
||||
$this->assertEquals(2, $this->secondLevelCacheLogger->getMissCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount('foo_region'));
|
||||
@ -818,7 +818,7 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
|
||||
|
||||
$this->assertNotEmpty($result3);
|
||||
$this->assertEquals($queryCount + 2, $this->getCurrentQueryCount());
|
||||
$this->assertEquals(2, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(6, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(2, $this->secondLevelCacheLogger->getPutCount());
|
||||
$this->assertEquals(2, $this->secondLevelCacheLogger->getMissCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount('bar_region'));
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Tests\Models\Cache\Country;
|
||||
use Doctrine\Tests\Models\Cache\State;
|
||||
|
||||
/**
|
||||
* @group DDC-2183
|
||||
@ -60,13 +61,61 @@ class SecondLevelCacheRepositoryTest extends SecondLevelCacheAbstractTest
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $countries[0]);
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $countries[1]);
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(3, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getMissCount());
|
||||
|
||||
$this->assertTrue($this->cache->containsEntity(Country::CLASSNAME, $this->countries[0]->getId()));
|
||||
$this->assertTrue($this->cache->containsEntity(Country::CLASSNAME, $this->countries[1]->getId()));
|
||||
}
|
||||
|
||||
public function testRepositoryCacheFindAllInvalidation()
|
||||
{
|
||||
$this->loadFixturesCountries();
|
||||
$this->evictRegions();
|
||||
$this->secondLevelCacheLogger->clearStats();
|
||||
$this->_em->clear();
|
||||
|
||||
$this->assertFalse($this->cache->containsEntity(Country::CLASSNAME, $this->countries[0]->getId()));
|
||||
$this->assertFalse($this->cache->containsEntity(Country::CLASSNAME, $this->countries[1]->getId()));
|
||||
|
||||
$repository = $this->_em->getRepository(Country::CLASSNAME);
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$this->assertCount(2, $repository->findAll());
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
$countries = $repository->findAll();
|
||||
|
||||
$this->assertEquals($queryCount, $this->getCurrentQueryCount());
|
||||
|
||||
$this->assertCount(2, $countries);
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $countries[0]);
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $countries[1]);
|
||||
|
||||
$country = new Country('foo');
|
||||
|
||||
$this->_em->persist($country);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$this->assertCount(3, $repository->findAll());
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
|
||||
|
||||
$country = $repository->find($country->getId());
|
||||
|
||||
$this->_em->remove($country);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$this->assertCount(2, $repository->findAll());
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
|
||||
}
|
||||
|
||||
public function testRepositoryCacheFindBy()
|
||||
{
|
||||
$this->loadFixturesCountries();
|
||||
@ -91,7 +140,7 @@ class SecondLevelCacheRepositoryTest extends SecondLevelCacheAbstractTest
|
||||
$this->assertCount(1, $countries);
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $countries[0]);
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(2, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getMissCount());
|
||||
|
||||
$this->assertTrue($this->cache->containsEntity(Country::CLASSNAME, $this->countries[0]->getId()));
|
||||
@ -120,9 +169,82 @@ class SecondLevelCacheRepositoryTest extends SecondLevelCacheAbstractTest
|
||||
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $country);
|
||||
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(2, $this->secondLevelCacheLogger->getHitCount());
|
||||
$this->assertEquals(1, $this->secondLevelCacheLogger->getMissCount());
|
||||
|
||||
$this->assertTrue($this->cache->containsEntity(Country::CLASSNAME, $this->countries[0]->getId()));
|
||||
}
|
||||
|
||||
public function testRepositoryCacheFindAllToOneAssociation()
|
||||
{
|
||||
$this->loadFixturesCountries();
|
||||
$this->loadFixturesStates();
|
||||
|
||||
$this->evictRegions();
|
||||
|
||||
$this->secondLevelCacheLogger->clearStats();
|
||||
$this->_em->clear();
|
||||
|
||||
// load from database
|
||||
$repository = $this->_em->getRepository(State::CLASSNAME);
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
$entities = $repository->findAll();
|
||||
|
||||
$this->assertCount(4, $entities);
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
|
||||
|
||||
$this->assertInstanceOf(State::CLASSNAME, $entities[0]);
|
||||
$this->assertInstanceOf(State::CLASSNAME, $entities[1]);
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $entities[0]->getCountry());
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $entities[0]->getCountry());
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[0]->getCountry());
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[1]->getCountry());
|
||||
|
||||
// load from cache
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
$entities = $repository->findAll();
|
||||
|
||||
$this->assertCount(4, $entities);
|
||||
$this->assertEquals($queryCount, $this->getCurrentQueryCount());
|
||||
|
||||
$this->assertInstanceOf(State::CLASSNAME, $entities[0]);
|
||||
$this->assertInstanceOf(State::CLASSNAME, $entities[1]);
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $entities[0]->getCountry());
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $entities[1]->getCountry());
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[0]->getCountry());
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[1]->getCountry());
|
||||
|
||||
// invalidate cache
|
||||
$this->_em->persist(new State('foo', $this->_em->find(Country::CLASSNAME, $this->countries[0]->getId())));
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
// load from database
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
$entities = $repository->findAll();
|
||||
|
||||
$this->assertCount(5, $entities);
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
|
||||
|
||||
$this->assertInstanceOf(State::CLASSNAME, $entities[0]);
|
||||
$this->assertInstanceOf(State::CLASSNAME, $entities[1]);
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $entities[0]->getCountry());
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $entities[1]->getCountry());
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[0]->getCountry());
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[1]->getCountry());
|
||||
|
||||
// load from cache
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
$entities = $repository->findAll();
|
||||
|
||||
$this->assertCount(5, $entities);
|
||||
$this->assertEquals($queryCount, $this->getCurrentQueryCount());
|
||||
|
||||
$this->assertInstanceOf(State::CLASSNAME, $entities[0]);
|
||||
$this->assertInstanceOf(State::CLASSNAME, $entities[1]);
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $entities[0]->getCountry());
|
||||
$this->assertInstanceOf(Country::CLASSNAME, $entities[1]->getCountry());
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[0]->getCountry());
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[1]->getCountry());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user