Movatterモバイル変換


[0]ホーム

URL:


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

Sign up
Appearance settings

Commit472b593

Browse files
[Cache] Add stampede protection via probabilistic early expiration
1 parent0bf5a3a commit472b593

22 files changed

+238
-45
lines changed

‎UPGRADE-4.2.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
UPGRADE FROM 4.1 to 4.2
2+
=======================
3+
4+
Cache
5+
-----
6+
7+
* Deprecated`CacheItem::getPreviousTags()`, use`CacheItem::getStats()` instead.

‎UPGRADE-5.0.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
UPGRADE FROM 4.x to 5.0
22
=======================
33

4+
Cache
5+
-----
6+
7+
* Removed`CacheItem::getPreviousTags()`, use`CacheItem::getStats()` instead.
8+
49
Config
510
------
611

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,12 @@ protected function __construct(string $namespace = '', int $defaultLifetime = 0)
4646
function ($key,$value,$isHit)use ($defaultLifetime) {
4747
$item =newCacheItem();
4848
$item->key =$key;
49-
$item->value =$value;
49+
$item->value =$v =$value;
5050
$item->isHit =$isHit;
5151
$item->defaultLifetime =$defaultLifetime;
52+
if (\is_array($v) &&1 ===\count($v) &&\is_array($v =$v[CacheItem::STATS_KEY] ??null) &&array(0,1) ===\array_keys($v)) {
53+
list($item->value,$item->stats) =$v;
54+
}
5255

5356
return$item;
5457
},
@@ -64,12 +67,14 @@ function ($deferred, $namespace, &$expiredIds) use ($getId) {
6467

6568
foreach ($deferredas$key =>$item) {
6669
if (null ===$item->expiry) {
67-
$byLifetime[0 <$item->defaultLifetime ?$item->defaultLifetime :0][$getId($key)] =$item->value;
70+
$ttl =0 <$item->defaultLifetime ?$item->defaultLifetime :0;
6871
}elseif ($item->expiry >$now) {
69-
$byLifetime[$item->expiry -$now][$getId($key)] =$item->value;
72+
$ttl =$item->expiry -$now;
7073
}else {
7174
$expiredIds[] =$getId($key);
75+
continue;
7276
}
77+
$byLifetime[$ttl][$getId($key)] =$item->newStats ?array(CacheItem::STATS_KEY =>array($item->value,$item->newStats)) :$item->value;
7378
}
7479

7580
return$byLifetime;

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ function ($sourceItem, $item) use ($defaultLifetime) {
6464
$item->value =$sourceItem->value;
6565
$item->expiry =$sourceItem->expiry;
6666
$item->isHit =$sourceItem->isHit;
67+
$item->stats =$sourceItem->stats;
6768

6869
$sourceItem->isTaggable =false;
70+
unset($sourceItem->stats[CacheItem::STATS_TAGS]);
6971

7072
if (0 <$sourceItem->defaultLifetime &&$sourceItem->defaultLifetime <$defaultLifetime) {
7173
$defaultLifetime =$sourceItem->defaultLifetime;
@@ -84,19 +86,20 @@ function ($sourceItem, $item) use ($defaultLifetime) {
8486
/**
8587
* {@inheritdoc}
8688
*/
87-
publicfunctionget(string$key,callable$callback)
89+
publicfunctionget(string$key,callable$callback,float$beta =null)
8890
{
8991
$lastItem =null;
9092
$i =0;
91-
$wrap =function (CacheItem$item =null)use ($key,$callback, &$wrap, &$i, &$lastItem) {
93+
$wrap =function (CacheItem$item =null)use ($key,$callback,$beta,&$wrap, &$i, &$lastItem) {
9294
$adapter =$this->adapters[$i];
9395
if (isset($this->adapters[++$i])) {
9496
$callback =$wrap;
97+
$beta =INF ===$beta ?INF :0;
9598
}
9699
if ($adapterinstanceof CacheInterface) {
97-
$value =$adapter->get($key,$callback);
100+
$value =$adapter->get($key,$callback,$beta);
98101
}else {
99-
$value =$this->doGet($adapter,$key,$callback);
102+
$value =$this->doGet($adapter,$key,$callback,$beta ??1.0);
100103
}
101104
if (null !==$item) {
102105
($this->syncItem)($lastItem =$lastItem ??$item,$item);

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,17 @@ public static function create($file, CacheItemPoolInterface $fallbackPool)
8383
/**
8484
* {@inheritdoc}
8585
*/
86-
publicfunctionget(string$key,callable$callback)
86+
publicfunctionget(string$key,callable$callback,float$beta =null)
8787
{
8888
if (null ===$this->values) {
8989
$this->initialize();
9090
}
9191
if (null ===$value =$this->values[$key] ??null) {
9292
if ($this->poolinstanceof CacheInterface) {
93-
return$this->pool->get($key,$callback);
93+
return$this->pool->get($key,$callback,$beta);
9494
}
9595

96-
return$this->doGet($this->pool,$key,$callback);
96+
return$this->doGet($this->pool,$key,$callback,$beta ??1.0);
9797
}
9898
if ('N;' ===$value) {
9999
returnnull;

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

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class ProxyAdapter implements AdapterInterface, CacheInterface, PruneableInterfa
3131
private$namespace;
3232
private$namespaceLen;
3333
private$createCacheItem;
34+
private$setInnerItem;
3435
private$poolHash;
3536

3637
publicfunction__construct(CacheItemPoolInterface$pool,string$namespace ='',int$defaultLifetime =0)
@@ -43,32 +44,52 @@ public function __construct(CacheItemPoolInterface $pool, string $namespace = ''
4344
function ($key,$innerItem)use ($defaultLifetime,$poolHash) {
4445
$item =newCacheItem();
4546
$item->key =$key;
46-
$item->value =$innerItem->get();
47+
$item->value =$v =$innerItem->get();
4748
$item->isHit =$innerItem->isHit();
4849
$item->defaultLifetime =$defaultLifetime;
4950
$item->innerItem =$innerItem;
5051
$item->poolHash =$poolHash;
52+
if (\is_array($v) &&1 ===\count($v) &&\is_array($v =$v[CacheItem::STATS_KEY] ??null) &&array(0,1) ===\array_keys($v)) {
53+
list($item->value,$item->stats) =$v;
54+
}elseif ($innerIteminstanceof CacheItem) {
55+
$item->stats =$innerItem->stats;
56+
}
5157
$innerItem->set(null);
5258

5359
return$item;
5460
},
5561
null,
5662
CacheItem::class
5763
);
64+
$this->setInnerItem = \Closure::bind(
65+
function (CacheItemInterface$innerItem,array$item) {
66+
if ($stats =$item["\0*\0newStats"]) {
67+
$item["\0*\0value"] =array(CacheItem::STATS_KEY =>array($item["\0*\0value"],$stats));
68+
}
69+
$innerItem->set($item["\0*\0value"]);
70+
$innerItem->expiresAt(null !==$item["\0*\0expiry"] ? \DateTime::createFromFormat('U',$item["\0*\0expiry"]) :null);
71+
},
72+
null,
73+
CacheItem::class
74+
);
5875
}
5976

6077
/**
6178
* {@inheritdoc}
6279
*/
63-
publicfunctionget(string$key,callable$callback)
80+
publicfunctionget(string$key,callable$callback,float$beta =null)
6481
{
6582
if (!$this->poolinstanceof CacheInterface) {
66-
return$this->doGet($this->pool,$key,$callback);
83+
return$this->doGet($this,$key,$callback,$beta ??1.0);
6784
}
6885

6986
return$this->pool->get($this->getId($key),function ($innerItem)use ($key,$callback) {
70-
return$callback(($this->createCacheItem)($key,$innerItem));
71-
});
87+
$item = ($this->createCacheItem)($key,$innerItem);
88+
$item->set($value =$callback($item));
89+
($this->setInnerItem)($innerItem, (array)$item);
90+
91+
return$value;
92+
},$beta);
7293
}
7394

7495
/**
@@ -164,13 +185,11 @@ private function doSave(CacheItemInterface $item, $method)
164185
returnfalse;
165186
}
166187
$item = (array)$item;
167-
$expiry =$item["\0*\0expiry"];
168-
if (null ===$expiry &&0 <$item["\0*\0defaultLifetime"]) {
169-
$expiry =time() +$item["\0*\0defaultLifetime"];
188+
if (null ===$item["\0*\0expiry"] &&0 <$item["\0*\0defaultLifetime"]) {
189+
$item["\0*\0expiry"] =time() +$item["\0*\0defaultLifetime"];
170190
}
171191
$innerItem =$item["\0*\0poolHash"] ===$this->poolHash ?$item["\0*\0innerItem"] :$this->pool->getItem($this->namespace.$item["\0*\0key"]);
172-
$innerItem->set($item["\0*\0value"]);
173-
$innerItem->expiresAt(null !==$expiry ? \DateTime::createFromFormat('U',$expiry) :null);
192+
($this->setInnerItem)($innerItem,$item);
174193

175194
return$this->pool->$method($innerItem);
176195
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function (CacheItem $item, $key, array &$itemTags) {
6464
}
6565
if (isset($itemTags[$key])) {
6666
foreach ($itemTags[$key]as$tag =>$version) {
67-
$item->prevTags[$tag] =$tag;
67+
$item->stats[CacheItem::STATS_TAGS][$tag] =$tag;
6868
}
6969
unset($itemTags[$key]);
7070
}else {
@@ -81,7 +81,7 @@ function (CacheItem $item, $key, array &$itemTags) {
8181
function ($deferred) {
8282
$tagsByKey =array();
8383
foreach ($deferredas$key =>$item) {
84-
$tagsByKey[$key] =$item->tags;
84+
$tagsByKey[$key] =$item->newStats[CacheItem::STATS_TAGS] ??array();
8585
}
8686

8787
return$tagsByKey;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function __construct(AdapterInterface $pool)
3737
/**
3838
* {@inheritdoc}
3939
*/
40-
publicfunctionget(string$key,callable$callback)
40+
publicfunctionget(string$key,callable$callback,float$beta =null)
4141
{
4242
if (!$this->poolinstanceof CacheInterface) {
4343
thrownew \BadMethodCallException(sprintf('Cannot call "%s::get()": this class doesn\'t implement "%s".',get_class($this->pool), CacheInterface::class));
@@ -52,7 +52,7 @@ public function get(string $key, callable $callback)
5252

5353
$event =$this->start(__FUNCTION__);
5454
try {
55-
$value =$this->pool->get($key,$callback);
55+
$value =$this->pool->get($key,$callback,$beta);
5656
$event->result[$key] =\is_object($value) ?\get_class($value) :gettype($value);
5757
}finally {
5858
$event->end =microtime(true);

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ CHANGELOG
44
4.2.0
55
-----
66

7-
* added`CacheInterface` and`TaggableCacheInterface`
7+
* added`CacheInterface` and`TaggableCacheInterface`, providing stampede protection via probabilistic early expiration
88
* throw`LogicException` when`CacheItem::tag()` is called on an item coming from a non tag-aware pool
9+
* deprecated`CacheItem::getPreviousTags()`, use`CacheItem::getStats()` instead
910

1011
3.4.0
1112
-----
@@ -19,7 +20,7 @@ CHANGELOG
1920
3.3.0
2021
-----
2122

22-
*[EXPERIMENTAL]added CacheItem::getPreviousTags() to get bound tags coming from the pool storage if any
23+
* added CacheItem::getPreviousTags() to get bound tags coming from the pool storage if any
2324
* added PSR-16 "Simple Cache" implementations for all existing PSR-6 adapters
2425
* added Psr6Cache and SimpleCacheAdapter for bidirectional interoperability between PSR-6 and PSR-16
2526
* added MemcachedAdapter (PSR-6) and MemcachedCache (PSR-16)

‎src/Symfony/Component/Cache/CacheInterface.php‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ interface CacheInterface
3030
{
3131
/**
3232
* @param callable(CacheItemInterface $item):mixed $callback Should return the computed value for the given key/item
33+
* @param float|null $beta A float that controls the likeness of triggering early expiration.
34+
* 0 disables it, INF forces immediate expiration.
35+
* The default (or providing null) is implementation dependent but should
36+
* typically be 1.0, which should provide optimal stampede protection.
3337
*
3438
* @return mixed The value corresponding to the provided key
3539
*/
36-
publicfunctionget(string$key,callable$callback);
40+
publicfunctionget(string$key,callable$callback,float$beta =null);
3741
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp