Skip to content

Commit 660197e

Browse files
committedFeb 8, 2023
Avoid unnecessary information in query hints to improve query cache hit ratio
I've noticed that over time my query caches fill up with redundant queries, i. e. different cache entries for the DQL -> SQL translation that are exactly the same. For me, it's an issue because the cache entries fill up precious OPcache memory. Further investigation revealed that the queries themselves do not differ, but only the query hints – that are part of the computed cache key – do. In particular, only the value for the `WhereInWalker::HINT_PAGINATOR_ID_COUNT` query hint are different. Since `WhereInWalker` only needs to know _if_ there are matching IDs but not _how many_, we could avoid such cache misses by using just a boolean value as cache hint.
1 parent ab06e07 commit 660197e

File tree

5 files changed

+29
-9
lines changed

5 files changed

+29
-9
lines changed
 

‎lib/Doctrine/ORM/Tools/Pagination/Paginator.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
use function array_map;
2323
use function array_sum;
2424
use function assert;
25-
use function count;
2625
use function is_string;
2726

2827
/**
@@ -160,7 +159,7 @@ public function getIterator()
160159
$ids = array_map('current', $foundIdRows);
161160

162161
$this->appendTreeWalker($whereInQuery, WhereInWalker::class);
163-
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids));
162+
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_HAS_IDS, true);
164163
$whereInQuery->setFirstResult(0)->setMaxResults(null);
165164
$whereInQuery->setCacheable($this->query->isCacheable());
166165

‎lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@
2828
* The parameter namespace (dpid) is defined by
2929
* the PAGINATOR_ID_ALIAS
3030
*
31-
* The total number of parameters is retrieved from
32-
* the HINT_PAGINATOR_ID_COUNT query hint.
31+
* The HINT_PAGINATOR_HAS_IDS query hint indicates whether there are
32+
* any ids in the parameter at all.
3333
*/
3434
class WhereInWalker extends TreeWalkerAdapter
3535
{
3636
/**
3737
* ID Count hint name.
3838
*/
39-
public const HINT_PAGINATOR_ID_COUNT = 'doctrine.id.count';
39+
public const HINT_PAGINATOR_HAS_IDS = 'doctrine.paginator_has_ids';
4040

4141
/**
4242
* Primary key alias for query.
@@ -65,9 +65,9 @@ public function walkSelectStatement(SelectStatement $AST)
6565
$pathExpression = new PathExpression(PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, $identifierFieldName);
6666
$pathExpression->type = $pathType;
6767

68-
$count = $this->_getQuery()->getHint(self::HINT_PAGINATOR_ID_COUNT);
68+
$hasIds = $this->_getQuery()->getHint(self::HINT_PAGINATOR_HAS_IDS);
6969

70-
if ($count > 0) {
70+
if ($hasIds) {
7171
$arithmeticExpression = new ArithmeticExpression();
7272
$arithmeticExpression->simpleArithmeticExpression = new SimpleArithmeticExpression(
7373
[$pathExpression]

‎tests/Doctrine/Tests/ORM/Functional/PaginationTest.php

+21
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,27 @@ public function testPaginationWithSubSelectOrderByExpression($useOutputWalker, $
667667
self::assertCount(9, $paginator->getIterator());
668668
}
669669

670+
public function testDifferentResultLengthsDoNotRequireExtraQueryCacheEntries(): void
671+
{
672+
$dql = 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id >= :id';
673+
$query = $this->_em->createQuery($dql);
674+
$query->setMaxResults(10);
675+
676+
$query->setParameter('id', 1);
677+
$paginator = new Paginator($query);
678+
$paginator->getIterator(); // exercise the Paginator
679+
680+
$initialCount = count(self::$queryCache->getValues());
681+
682+
$query->setParameter('id', 2);
683+
$paginator = new Paginator($query);
684+
$paginator->getIterator(); // exercise the Paginator again, with a smaller result set
685+
686+
$newCount = count(self::$queryCache->getValues());
687+
688+
self::assertSame($initialCount, $newCount);
689+
}
690+
670691
public function populate(): void
671692
{
672693
$groups = [];

‎tests/Doctrine/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public function testDqlQueryTransformation(string $dql, string $expectedSql): vo
1919
{
2020
$query = $this->entityManager->createQuery($dql);
2121
$query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [WhereInWalker::class]);
22-
$query->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, 10);
22+
$query->setHint(WhereInWalker::HINT_PAGINATOR_HAS_IDS, true);
2323

2424
$result = (new Parser($query))->parse();
2525

‎tests/Doctrine/Tests/OrmFunctionalTestCase.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
7474
*
7575
* @var CacheItemPoolInterface|null
7676
*/
77-
private static $queryCache = null;
77+
protected static $queryCache = null;
7878

7979
/**
8080
* Shared connection when a TestCase is run alone (outside of its functional suite).

0 commit comments

Comments
 (0)
Please sign in to comment.