Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 928b3f1

Browse filesBrowse files
[Cache] Add LRU + max-lifetime capabilities to ArrayCache
1 parent b350c80 commit 928b3f1
Copy full SHA for 928b3f1

File tree

3 files changed

+106
-9
lines changed
Filter options

3 files changed

+106
-9
lines changed

‎src/Symfony/Component/Cache/Adapter/ArrayAdapter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
+69-9Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
use Symfony\Contracts\Cache\CacheInterface;
2020

2121
/**
22+
* An in-memory cache storage.
23+
*
24+
* Acts as a least-recently-used (LRU) storage when configured with a maximum number of items.
25+
*
2226
* @author Nicolas Grekas <p@tchwork.com>
2327
*/
2428
class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
@@ -29,13 +33,17 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
2933
private $values = [];
3034
private $expiries = [];
3135
private $createCacheItem;
36+
private $maxLifetime;
37+
private $maxItems;
3238

3339
/**
3440
* @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise
3541
*/
36-
public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true)
42+
public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true, int $maxLifetime = 0, int $maxItems = 0)
3743
{
3844
$this->storeSerialized = $storeSerialized;
45+
$this->maxLifetime = 0 < $maxLifetime ? $maxLifetime : 0;
46+
$this->maxItems = 0 < $maxItems ? $maxItems : 0;
3947
$this->createCacheItem = \Closure::bind(
4048
static function ($key, $value, $isHit) use ($defaultLifetime) {
4149
$item = new CacheItem();
@@ -84,6 +92,13 @@ public function delete(string $key): bool
8492
public function hasItem($key)
8593
{
8694
if (\is_string($key) && isset($this->expiries[$key]) && $this->expiries[$key] > microtime(true)) {
95+
if ($this->maxItems) {
96+
// Move the item last in the storage
97+
$value = $this->values[$key];
98+
unset($this->values[$key]);
99+
$this->values[$key] = $value;
100+
}
101+
87102
return true;
88103
}
89104
CacheItem::validateKey($key);
@@ -97,7 +112,12 @@ public function hasItem($key)
97112
public function getItem($key)
98113
{
99114
if (!$isHit = $this->hasItem($key)) {
100-
$this->values[$key] = $value = null;
115+
$value = null;
116+
117+
if (!$this->maxItems) {
118+
// Track misses in non-LRU mode only
119+
$this->values[$key] = null;
120+
}
101121
} else {
102122
$value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
103123
}
@@ -164,7 +184,9 @@ public function save(CacheItemInterface $item)
164184
$value = $item["\0*\0value"];
165185
$expiry = $item["\0*\0expiry"];
166186

167-
if (null !== $expiry && $expiry <= microtime(true)) {
187+
$now = microtime(true);
188+
189+
if (null !== $expiry && $expiry <= $now) {
168190
$this->deleteItem($key);
169191

170192
return true;
@@ -173,7 +195,23 @@ public function save(CacheItemInterface $item)
173195
return false;
174196
}
175197
if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) {
176-
$expiry = microtime(true) + $item["\0*\0defaultLifetime"];
198+
$expiry = $item["\0*\0defaultLifetime"];
199+
$expiry = $now + ($expiry > ($this->maxLifetime ?: $expiry) : $this->maxLifetime : $expiry);
200+
} elseif ($this->maxLifetime && (null === $expiry || $expiry > $now + $this->maxLifetime)) {
201+
$expiry = $now + $this->maxLifetime;
202+
}
203+
204+
if ($this->maxItems) {
205+
unset($this->values[$key]);
206+
207+
// Iterate items and vacuum expired ones while we are at it
208+
foreach ($this->values as $k => $v) {
209+
if ($this->expiries[$k] > $now && \count($this->values) < $this->maxItems) {
210+
break;
211+
}
212+
213+
unset($this->values[$k], $this->expiries[$k]);
214+
}
177215
}
178216

179217
$this->values[$key] = $value;
@@ -210,15 +248,21 @@ public function commit()
210248
public function clear(string $prefix = '')
211249
{
212250
if ('' !== $prefix) {
251+
$now = microtime(true);
252+
213253
foreach ($this->values as $key => $value) {
214-
if (0 === strpos($key, $prefix)) {
254+
if (!isset($this->expiries[$key]) || $this->expiries[$key] <= $now || 0 === strpos($key, $prefix)) {
215255
unset($this->values[$key], $this->expiries[$key]);
216256
}
217257
}
218-
} else {
219-
$this->values = $this->expiries = [];
258+
259+
if ($this->values) {
260+
return true;
261+
}
220262
}
221263

264+
$this->values = $this->expiries = [];
265+
222266
return true;
223267
}
224268

@@ -258,8 +302,20 @@ private function generateItems(array $keys, $now, $f)
258302
{
259303
foreach ($keys as $i => $key) {
260304
if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key))) {
261-
$this->values[$key] = $value = null;
305+
$value = null;
306+
307+
if (!$this->maxItems) {
308+
// Track misses in non-LRU mode only
309+
$this->values[$key] = null;
310+
}
262311
} else {
312+
if ($this->maxItems) {
313+
// Move the item last in the storage
314+
$value = $this->values[$key];
315+
unset($this->values[$key]);
316+
$this->values[$key] = $value;
317+
}
318+
263319
$value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
264320
}
265321
unset($keys[$i]);
@@ -314,8 +370,12 @@ private function unfreeze(string $key, bool &$isHit)
314370
$value = false;
315371
}
316372
if (false === $value) {
317-
$this->values[$key] = $value = null;
373+
$value = null;
318374
$isHit = false;
375+
376+
if (!$this->maxItems) {
377+
$this->values[$key] = null;
378+
}
319379
}
320380
}
321381

‎src/Symfony/Component/Cache/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/CHANGELOG.md
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
5.1.0
5+
-----
6+
7+
* added max-items + LRU + max-lifetime capabilities to `ArrayCache`
8+
49
5.0.0
510
-----
611

‎src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php
+32Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,36 @@ public function testGetValuesHitAndMiss()
5555
$this->assertArrayHasKey('bar', $values);
5656
$this->assertNull($values['bar']);
5757
}
58+
59+
public function testMaxLifetime()
60+
{
61+
$cache = new ArrayAdapter(0, false, 1);
62+
63+
$item = $cache->getItem('foo');
64+
$item->expiresAfter(2);
65+
$cache->save($item->set(123));
66+
67+
$this->assertTrue($cache->hasItem('foo'));
68+
sleep(1);
69+
$this->assertFalse($cache->hasItem('foo'));
70+
}
71+
72+
public function testMaxItems()
73+
{
74+
$cache = new ArrayAdapter(0, false, 0, 2);
75+
76+
$cache->save($cache->getItem('foo'));
77+
$cache->save($cache->getItem('bar'));
78+
$cache->save($cache->getItem('buz'));
79+
80+
$this->assertFalse($cache->hasItem('foo'));
81+
$this->assertTrue($cache->hasItem('bar'));
82+
$this->assertTrue($cache->hasItem('buz'));
83+
84+
$cache->save($cache->getItem('foo'));
85+
86+
$this->assertFalse($cache->hasItem('bar'));
87+
$this->assertTrue($cache->hasItem('buz'));
88+
$this->assertTrue($cache->hasItem('foo'));
89+
}
5890
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.