diff --git a/docs/en/reference/second-level-cache.rst b/docs/en/reference/second-level-cache.rst index ea7c20ac3..ecf682b08 100644 --- a/docs/en/reference/second-level-cache.rst +++ b/docs/en/reference/second-level-cache.rst @@ -2,7 +2,7 @@ The Second Level Cache ====================== The Second Level Cache is designed to reduce the amount of necessary database access. -It sits between your application and the database to avoid the number of database hits as many as possible. +It sits between your application and the database to avoid the number of database hits as much as possible. When turned on, entities will be first searched in cache and if they are not found, a database query will be fired an then the entity result will be stored in a cache provider. @@ -22,6 +22,9 @@ Each entity class, collection association and query has its region, where values Caching Regions are specific region into the cache provider that might store entities, collection or queries. Each cache region resides in a specific cache namespace and has its own lifetime configuration. +Notice that when caching collection and queries only identifiers are stored. +The entity values will be stored in its own region + Something like below for an entity region : .. code-block:: php @@ -60,8 +63,7 @@ A query region might be something like : .. note:: - Notice that when caching collection and queries only identifiers are stored. - The entity values will be stored in its own region + The following data structures represents now the cache will looks like, this is not actual cached data. .. _reference-second-level-cache-regions: @@ -80,6 +82,11 @@ It allows you to provide your own cache implementation that might take advantage If you want to support locking for ``READ_WRITE`` strategies you should implement ``ConcurrentRegion``; ``CacheRegion`` otherwise. +Cache region +~~~~~~~~~~~~ + +Defines a contract for accessing a particular region. + ``Doctrine\ORM\Cache\Region`` Defines a contract for accessing a particular cache region. @@ -111,7 +118,7 @@ Defines a contract for accessing a particular cache region. * * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to be retrieved. * - * @return \Doctrine\ORM\Cache\CacheEntry The cached entry or NULL + * @return \Doctrine\ORM\Cache\CacheEntry|null The cached entry or NULL */ public function get(CacheKey $key); @@ -138,6 +145,14 @@ Defines a contract for accessing a particular cache region. } +Concurrent cache region +~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Doctrine\ORM\Cache\ConcurrentRegion`` is designed to store concurrently managed data region. +By default, Doctrine provides a very simple implementation based on file locks ``Doctrine\ORM\Cache\Region\FileLockRegion``. + +If you want to use an ``READ_WRITE`` cache, you should consider providing your own cache region. + ``Doctrine\ORM\Cache\ConcurrentRegion`` Defines contract for concurrently managed data region. @@ -166,6 +181,9 @@ Defines contract for concurrently managed data region. public function unlock(CacheKey $key, Lock $lock); } +Cache region +~~~~~~~~~~~~ + ``Doctrine\ORM\Cache\TimestampRegion`` Tracks the timestamps of the most recent updates to particular entity. @@ -179,7 +197,7 @@ Tracks the timestamps of the most recent updates to particular entity. /** * Update an specific key into the cache region. * - * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to lock. + * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to update the timestamp. * * @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region. */ @@ -364,8 +382,8 @@ To specify a default lifetime for all regions or specify a different lifetime fo $regionConfig = $cacheConfig->getRegionsConfiguration(); //Cache Region lifetime - $regionConfig->setLifetime('my_entity_region', 3600); - $regionConfig->setDefaultLifetime(7200); + $regionConfig->setLifetime('my_entity_region', 3600); // Time to live for a specific region; In seconds + $regionConfig->setDefaultLifetime(7200); // Default time to live; In seconds Cache Log @@ -672,7 +690,7 @@ Basic entity cache $em->clear(); // Clear entity manager - $country = $em->find('Country', 1); // Retrieve item from cache + $country1 = $em->find('Country', 1); // Retrieve item from cache $country->setName("New Name"); $em->persist($state); @@ -680,7 +698,8 @@ Basic entity cache $em->clear(); // Clear entity manager - $country = $em->find('Country', 1); // Retrieve item from cache + $country2 = $em->find('Country', 1); // Retrieve item from cache + // Notice that $country1 and $country2 are not the same instance. Association cache @@ -759,6 +778,8 @@ The query cache stores the results of the query but as identifiers, entity value ->setCacheable(true) ->getResult(); + $em->clear() + // Check if query result is valid and load entities from cache $result2 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name') ->setCacheable(true) @@ -806,15 +827,16 @@ Using the last update timestamps as part of the query key invalidate the cache k $entities = $em->getRepository('Entity\Country')->findAll(); // load from query and entities from cache.. - $entities = $em->getRepository('Country')->findAll(); + $entities = $em->getRepository('Entity\Country')->findAll(); // update the timestamp cache region for Country $em->persist(new Country('zombieland')); $em->flush(); $em->clear(); - // Reload the query from database. - $entities = $em->getRepository('Country')->findAll(); + // Reload from database. + // At this point the query cache key if not logger valid, the select goes straight + $entities = $em->getRepository('Entity\Country')->findAll(); Cache API --------- @@ -843,8 +865,9 @@ Limitations Composite primary key ~~~~~~~~~~~~~~~~~~~~~ -Composite primary key are supported by second level cache, however when one of the keys is an association -the cached entity should always be retrieved using the association identifier. +Composite primary key are supported by second level cache, +however when one of the keys is an association the cached entity should always be retrieved using the association identifier. +For performance reasons the cache API does not extract from composite primary key. .. code-block:: php @@ -871,29 +894,19 @@ the cached entity should always be retrieved using the association identifier. // Supported /** @var $article Article */ - $article = $this->_em->find("Article", 1); + $article = $em->find('Article', 1); // Supported /** @var $article Article */ - $article = $this->_em->find("Article", $article); + $article = $em->find('Article', $article); // Supported $id = array('source' => 1, 'target' => 2); - $reference = $this->_em->find("Reference", $id); + $reference = $em->find('Reference', $id); // NOT Supported $id = array('source' => new Article(1), 'target' => new Article(2)); - $reference = $this->_em->find("Reference", $id); - - -Concurrent cache region -~~~~~~~~~~~~~~~~~~~~~~~ - -A ``Doctrine\\ORM\\Cache\\ConcurrentRegion`` is designed to store concurrently managed data region. -By default, Doctrine provides a very simple implementation based on file locks ``Doctrine\\ORM\\Cache\\Region\\FileLockRegion``. - -If you want to use an ``READ_WRITE`` cache, you should consider providing your own cache region. -for more details about how to implement a cache region please see :ref:`reference-second-level-cache-regions` + $reference = $em->find('Reference', $id); DELETE / UPDATE queries diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index d7ed6fd17..5d77b4579 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -137,19 +137,19 @@ abstract class AbstractQuery /** * Second level cache region name. * - * @var string + * @var string|null */ protected $cacheRegion; /** * Second level query cache mode. * - * @var integer + * @var integer|null */ protected $cacheMode; /** - * @var \Doctrine\ORM\Cache\Logging\CacheLogger + * @var \Doctrine\ORM\Cache\Logging\CacheLogger|null */ protected $cacheLogger; @@ -206,7 +206,7 @@ abstract class AbstractQuery */ public function setCacheRegion($cacheRegion) { - $this->cacheRegion = $cacheRegion; + $this->cacheRegion = (string) $cacheRegion; return $this; } @@ -245,7 +245,7 @@ abstract class AbstractQuery */ public function setLifetime($lifetime) { - $this->lifetime = $lifetime; + $this->lifetime = (integer) $lifetime; return $this; } @@ -264,7 +264,7 @@ abstract class AbstractQuery */ public function setCacheMode($cacheMode) { - $this->cacheMode = $cacheMode; + $this->cacheMode = (integer) $cacheMode; return $this; } diff --git a/lib/Doctrine/ORM/Cache.php b/lib/Doctrine/ORM/Cache.php index 6cd8b4814..37cc9a075 100644 --- a/lib/Doctrine/ORM/Cache.php +++ b/lib/Doctrine/ORM/Cache.php @@ -165,7 +165,7 @@ interface Cache /** * Evicts all cached query results under the given name, or default query cache if the region name is NULL. * - * @param string $regionName The cache name associated to the queries being cached. + * @param string|null $regionName The cache name associated to the queries being cached. */ public function evictQueryRegion($regionName = null); @@ -179,9 +179,9 @@ interface Cache /** * Get query cache by region name or create a new one if none exist. * - * @param regionName Query cache region name, or default query cache if the region name is NULL. + * @param string|null $regionName Query cache region name, or default query cache if the region name is NULL. * * @return \Doctrine\ORM\Cache\QueryCache The Query Cache associated with the region name. */ public function getQueryCache($regionName = null); -} \ No newline at end of file +} diff --git a/lib/Doctrine/ORM/Cache/CacheConfiguration.php b/lib/Doctrine/ORM/Cache/CacheConfiguration.php index 42a1ba3ba..9d3654a04 100644 --- a/lib/Doctrine/ORM/Cache/CacheConfiguration.php +++ b/lib/Doctrine/ORM/Cache/CacheConfiguration.php @@ -35,22 +35,22 @@ use Doctrine\ORM\Cache\TimestampQueryCacheValidator; class CacheConfiguration { /** - * @var \Doctrine\ORM\Cache\CacheFactory + * @var \Doctrine\ORM\Cache\CacheFactory|null */ private $cacheFactory; /** - * @var \Doctrine\ORM\Cache\RegionsConfiguration + * @var \Doctrine\ORM\Cache\RegionsConfiguration|null */ private $regionsConfig; /** - * @var \Doctrine\ORM\Cache\Logging\CacheLogger + * @var \Doctrine\ORM\Cache\Logging\CacheLogger|null */ private $cacheLogger; /** - * @var \Doctrine\ORM\Cache\QueryCacheValidator + * @var \Doctrine\ORM\Cache\QueryCacheValidator|null */ private $queryValidator; diff --git a/lib/Doctrine/ORM/Cache/DefaultCache.php b/lib/Doctrine/ORM/Cache/DefaultCache.php index cd4c52ca0..78a22a80f 100644 --- a/lib/Doctrine/ORM/Cache/DefaultCache.php +++ b/lib/Doctrine/ORM/Cache/DefaultCache.php @@ -51,7 +51,7 @@ class DefaultCache implements Cache private $cacheFactory; /** - * @var array<\Doctrine\ORM\Cache\QueryCache> + * @var \Doctrine\ORM\Cache\QueryCache[] */ private $queryCaches = array(); diff --git a/lib/Doctrine/ORM/Cache/DefaultCacheFactory.php b/lib/Doctrine/ORM/Cache/DefaultCacheFactory.php index 5781f3936..1add374eb 100644 --- a/lib/Doctrine/ORM/Cache/DefaultCacheFactory.php +++ b/lib/Doctrine/ORM/Cache/DefaultCacheFactory.php @@ -57,17 +57,17 @@ class DefaultCacheFactory implements CacheFactory private $regionsConfig; /** - * @var \Doctrine\ORM\Cache\TimestampRegion + * @var \Doctrine\ORM\Cache\TimestampRegion|null */ private $timestampRegion; /** - * @var array + * @var \Doctrine\ORM\Cache\Region[] */ - private $regions; + private $regions = array(); /** - * @var string + * @var string|null */ private $fileLockRegionDirectory; @@ -86,7 +86,7 @@ class DefaultCacheFactory implements CacheFactory */ public function setFileLockRegionDirectory($fileLockRegionDirectory) { - $this->fileLockRegionDirectory = $fileLockRegionDirectory; + $this->fileLockRegionDirectory = (string) $fileLockRegionDirectory; } /** @@ -200,7 +200,7 @@ class DefaultCacheFactory implements CacheFactory if ($cache['usage'] === ClassMetadata::CACHE_USAGE_READ_WRITE) { if ( ! $this->fileLockRegionDirectory) { - throw new \RuntimeException( + throw new \LogicException( 'If you want to use a "READ_WRITE" cache an implementation of "Doctrine\ORM\Cache\ConcurrentRegion" is required, ' . 'The default implementation provided by doctrine is "Doctrine\ORM\Cache\Region\FileLockRegion" if you what to use it please provide a valid directory, DefaultCacheFactory#setFileLockRegionDirectory(). ' ); diff --git a/lib/Doctrine/ORM/Cache/Region.php b/lib/Doctrine/ORM/Cache/Region.php index 16609400f..91879f45a 100644 --- a/lib/Doctrine/ORM/Cache/Region.php +++ b/lib/Doctrine/ORM/Cache/Region.php @@ -51,7 +51,7 @@ interface Region * * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to be retrieved. * - * @return \Doctrine\ORM\Cache\CacheEntry The cached entry or NULL + * @return \Doctrine\ORM\Cache\CacheEntry|null The cached entry or NULL * * @throws \Doctrine\ORM\Cache\CacheException Indicates a problem accessing the item or region. */ diff --git a/lib/Doctrine/ORM/Cache/RegionsConfiguration.php b/lib/Doctrine/ORM/Cache/RegionsConfiguration.php index 1a847943b..0d060636c 100644 --- a/lib/Doctrine/ORM/Cache/RegionsConfiguration.php +++ b/lib/Doctrine/ORM/Cache/RegionsConfiguration.php @@ -31,12 +31,12 @@ class RegionsConfiguration /** * @var array */ - private $lifetimes; + private $lifetimes = array(); /** * @var array */ - private $lockLifetimes; + private $lockLifetimes = array(); /** * @var integer @@ -54,8 +54,8 @@ class RegionsConfiguration */ public function __construct($defaultLifetime = 3600, $defaultLockLifetime = 60) { - $this->defaultLifetime = $defaultLifetime; - $this->defaultLockLifetime = $defaultLockLifetime; + $this->defaultLifetime = (integer) $defaultLifetime; + $this->defaultLockLifetime = (integer) $defaultLockLifetime; } /** @@ -71,7 +71,7 @@ class RegionsConfiguration */ public function setDefaultLifetime($defaultLifetime) { - $this->defaultLifetime = $defaultLifetime; + $this->defaultLifetime = (integer) $defaultLifetime; } /** @@ -87,7 +87,7 @@ class RegionsConfiguration */ public function setDefaultLockLifetime($defaultLockLifetime) { - $this->defaultLockLifetime = $defaultLockLifetime; + $this->defaultLockLifetime = (integer) $defaultLockLifetime; } /** diff --git a/lib/Doctrine/ORM/Cache/TimestampRegion.php b/lib/Doctrine/ORM/Cache/TimestampRegion.php index 8065a9411..9e0c25ca6 100644 --- a/lib/Doctrine/ORM/Cache/TimestampRegion.php +++ b/lib/Doctrine/ORM/Cache/TimestampRegion.php @@ -31,7 +31,7 @@ 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. + * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to update the timestamp. * * @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region. */ diff --git a/lib/Doctrine/ORM/Mapping/Cache.php b/lib/Doctrine/ORM/Mapping/Cache.php index 2bb8bb321..3226b6031 100644 --- a/lib/Doctrine/ORM/Mapping/Cache.php +++ b/lib/Doctrine/ORM/Mapping/Cache.php @@ -41,4 +41,4 @@ final class Cache implements Annotation * @var string Cache region name. */ public $region; -} \ No newline at end of file +} diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 6337326d4..057c3857f 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -594,7 +594,7 @@ class ClassMetadataInfo implements ClassMetadata /** * @var array */ - public $cache; + public $cache = null; /** * The ReflectionClass instance of the mapped class. @@ -875,7 +875,7 @@ class ClassMetadataInfo implements ClassMetadata } if ($this->cache) { - $serialized[] = "cache"; + $serialized[] = 'cache'; } return $serialized; diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index c261723f7..2c8f1afcd 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -727,6 +727,8 @@ class XmlDriver extends FileDriver } /** + * Parse / Normalize the cache configuration + * * @param SimpleXMLElement $cacheMapping * * @return array diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index e5eac7e4f..a6880b018 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -732,6 +732,8 @@ class YamlDriver extends FileDriver } /** + * Parse / Normalize the cache configuration + * * @param array $cacheMapping * * @return array diff --git a/tests/Doctrine/Tests/EventListener/CacheMetadataListener.php b/tests/Doctrine/Tests/EventListener/CacheMetadataListener.php index 243f9b63e..f9e2616c6 100644 --- a/tests/Doctrine/Tests/EventListener/CacheMetadataListener.php +++ b/tests/Doctrine/Tests/EventListener/CacheMetadataListener.php @@ -32,4 +32,4 @@ class CacheMetadataListener $metadata->enableAssociationCache($mapping['fieldName'], $cache); } } -} \ No newline at end of file +} diff --git a/tests/Doctrine/Tests/Mocks/CacheEntryMock.php b/tests/Doctrine/Tests/Mocks/CacheEntryMock.php index 702295cda..902f092e0 100644 --- a/tests/Doctrine/Tests/Mocks/CacheEntryMock.php +++ b/tests/Doctrine/Tests/Mocks/CacheEntryMock.php @@ -7,4 +7,4 @@ use Doctrine\ORM\Cache\CacheEntry; class CacheEntryMock extends \ArrayObject implements CacheEntry { -} \ No newline at end of file +} diff --git a/tests/Doctrine/Tests/Mocks/CacheKeyMock.php b/tests/Doctrine/Tests/Mocks/CacheKeyMock.php index 078cd15eb..e427c2f41 100644 --- a/tests/Doctrine/Tests/Mocks/CacheKeyMock.php +++ b/tests/Doctrine/Tests/Mocks/CacheKeyMock.php @@ -6,9 +6,8 @@ use Doctrine\ORM\Cache\CacheKey; class CacheKeyMock extends CacheKey { - function __construct($hash) { $this->hash = $hash; } -} \ No newline at end of file +} diff --git a/tests/Doctrine/Tests/ORM/Cache/DefaultCacheFactoryTest.php b/tests/Doctrine/Tests/ORM/Cache/DefaultCacheFactoryTest.php index f597a5134..30cc3ca76 100644 --- a/tests/Doctrine/Tests/ORM/Cache/DefaultCacheFactoryTest.php +++ b/tests/Doctrine/Tests/ORM/Cache/DefaultCacheFactoryTest.php @@ -252,7 +252,7 @@ class DefaultCacheFactoryTest extends OrmTestCase } /** - * @expectedException RuntimeException + * @expectedException LogicException * @expectedExceptionMessage If you want to use a "READ_WRITE" cache an implementation of "Doctrine\ORM\Cache\ConcurrentRegion" is required, The default implementation provided by doctrine is "Doctrine\ORM\Cache\Region\FileLockRegion" if you what to use it please provide a valid directory */ public function testInvalidFileLockRegionDirectoryException()