Skip to content

Commit

Permalink
Merge pull request #6417 from lcobucci/fix-expire-result-cache
Browse files Browse the repository at this point in the history
Fix expire result cache
  • Loading branch information
Ocramius authored May 3, 2017
2 parents 2c1ebc4 + e71272e commit 7bb02d0
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 0 deletions.
19 changes: 19 additions & 0 deletions lib/Doctrine/ORM/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
namespace Doctrine\ORM;

use Doctrine\DBAL\LockMode;
use Doctrine\ORM\Query\Exec\AbstractSqlExecutor;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\ParserResult;
use Doctrine\ORM\Query\QueryException;
Expand Down Expand Up @@ -322,9 +323,27 @@ protected function _doExecute()

list($sqlParams, $types) = $this->processParameterMappings($paramMappings);

$this->evictResultSetCache($executor, $sqlParams, $types);

return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
}

private function evictResultSetCache(AbstractSqlExecutor $executor, array $sqlParams, array $types)
{
if (null === $this->_queryCacheProfile || ! $this->getExpireResultCache()) {
return;
}

$cacheDriver = $this->_queryCacheProfile->getResultCacheDriver();
$statements = (array) $executor->getSqlStatements(); // Type casted since it can either be a string or an array

foreach ($statements as $statement) {
$cacheKeys = $this->_queryCacheProfile->generateCacheKeys($statement, $sqlParams, $types);

$cacheDriver->delete(reset($cacheKeys));
}
}

/**
* Evict entity cache region
*/
Expand Down
95 changes: 95 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/Ticket/GH2947Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace Doctrine\Tests\ORM\Functional\Ticket;

use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Tests\OrmFunctionalTestCase;

/**
* @group 2947
*/
class GH2947Test extends OrmFunctionalTestCase
{
protected function setUp()
{
$this->resultCacheImpl = new ArrayCache();

parent::setUp();

$this->_schemaTool->createSchema([$this->_em->getClassMetadata(GH2947Car::class)]);
}

public function testIssue()
{
$this->createData();
$initialQueryCount = $this->getCurrentQueryCount();

$query = $this->createQuery();
self::assertEquals('BMW', (string) $query->getSingleResult());
self::assertEquals($initialQueryCount + 1, $this->getCurrentQueryCount());

$this->updateData();
self::assertEquals('BMW', (string) $query->getSingleResult());
self::assertEquals($initialQueryCount + 2, $this->getCurrentQueryCount());

$query->expireResultCache(true);
self::assertEquals('Dacia', (string) $query->getSingleResult());
self::assertEquals($initialQueryCount + 3, $this->getCurrentQueryCount());

$query->expireResultCache(false);
self::assertEquals('Dacia', (string) $query->getSingleResult());
self::assertEquals($initialQueryCount + 3, $this->getCurrentQueryCount());
}

private function createQuery()
{
return $this->_em->createQueryBuilder()
->select('car')
->from(GH2947Car::class, 'car')
->getQuery()
->useResultCache(true, 3600, 'foo-cache-id');
}

private function createData()
{
$this->_em->persist(new GH2947Car('BMW'));
$this->_em->flush();
$this->_em->clear();
}

private function updateData()
{
$this->_em->createQueryBuilder()
->update(GH2947Car::class, 'car')
->set('car.brand', ':newBrand')
->where('car.brand = :oldBrand')
->setParameter('newBrand', 'Dacia')
->setParameter('oldBrand', 'BMW')
->getQuery()
->execute();
}
}

/**
* @Entity
* @Table(name="GH2947_car")
*/
class GH2947Car
{
/**
* @Id
* @Column(type="string", length=25)
* @GeneratedValue(strategy="NONE")
*/
public $brand;

public function __construct(string $brand)
{
$this->brand = $brand;
}

public function __toString(): string
{
return $this->brand;
}
}
33 changes: 33 additions & 0 deletions tests/Doctrine/Tests/ORM/Query/QueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,37 @@ public function testSetHydrationCacheProfileNull()
$query->setHydrationCacheProfile(null);
$this->assertNull($query->getHydrationCacheProfile());
}

/**
* @group 2947
*/
public function testResultCacheEviction()
{
$this->_em->getConfiguration()->setResultCacheImpl(new ArrayCache());

$query = $this->_em->createQuery("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u")
->useResultCache(true);

/** @var DriverConnectionMock $driverConnectionMock */
$driverConnectionMock = $this->_em->getConnection()
->getWrappedConnection();

$driverConnectionMock->setStatementMock(new StatementArrayMock([['id_0' => 1]]));

// Performs the query and sets up the initial cache
self::assertCount(1, $query->getResult());

$driverConnectionMock->setStatementMock(new StatementArrayMock([['id_0' => 1], ['id_0' => 2]]));

// Retrieves cached data since expire flag is false and we have a cached result set
self::assertCount(1, $query->getResult());

// Performs the query and caches the result set since expire flag is true
self::assertCount(2, $query->expireResultCache(true)->getResult());

$driverConnectionMock->setStatementMock(new StatementArrayMock([['id_0' => 1]]));

// Retrieves cached data since expire flag is false and we have a cached result set
self::assertCount(2, $query->expireResultCache(false)->getResult());
}
}
11 changes: 11 additions & 0 deletions tests/Doctrine/Tests/OrmFunctionalTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
*/
protected $_usedModelSets = [];

/**
* To be configured by the test that uses result set cache
*
* @var \Doctrine\Common\Cache\Cache|null
*/
protected $resultCacheImpl;

/**
* Whether the database schema has already been created.
*
Expand Down Expand Up @@ -699,6 +706,10 @@ protected function _getEntityManager($config = null, $eventManager = null) {
$config->setProxyDir(__DIR__ . '/Proxies');
$config->setProxyNamespace('Doctrine\Tests\Proxies');

if (null !== $this->resultCacheImpl) {
$config->setResultCacheImpl($this->resultCacheImpl);
}

$enableSecondLevelCache = getenv('ENABLE_SECOND_LEVEL_CACHE');

if ($this->isSecondLevelCacheEnabled || $enableSecondLevelCache) {
Expand Down

0 comments on commit 7bb02d0

Please sign in to comment.