Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a957b41

Browse files
[Cache] Add LRU + max-lifetime capabilities to ArrayCache
1 parent b350c80 commit a957b41

File tree

3 files changed

+106
-9
lines changed

3 files changed

+106
-9
lines changed

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

Lines 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 <[email protected]>
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

Lines 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

Lines 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)