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

Skip to content

Commit c7b3b32

Browse files
[Cache] allow to skip saving the computed value when using CacheInterface::get()
1 parent c2e55ff commit c7b3b32

File tree

6 files changed

+88
-40
lines changed

6 files changed

+88
-40
lines changed

src/Symfony/Component/Cache/LockRegistry.php

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
*/
2626
class LockRegistry
2727
{
28-
private static $save;
2928
private static $openedFiles = array();
3029
private static $lockedFiles = array();
3130

@@ -75,29 +74,43 @@ public static function setFiles(array $files): array
7574
return $previousFiles;
7675
}
7776

78-
public static function compute(ItemInterface $item, callable $callback, CacheInterface $pool)
77+
public static function compute(ItemInterface $item, bool &$save, callable $callback, CacheInterface $pool)
7978
{
8079
$key = self::$files ? crc32($item->getKey()) % \count(self::$files) : -1;
8180

8281
if ($key < 0 || (self::$lockedFiles[$key] ?? false) || !$lock = self::open($key)) {
83-
return $callback($item);
82+
return $callback($item, $save);
8483
}
8584

86-
try {
87-
// race to get the lock in non-blocking mode
88-
if (flock($lock, LOCK_EX | LOCK_NB)) {
89-
self::$lockedFiles[$key] = true;
85+
while (true) {
86+
try {
87+
// race to get the lock in non-blocking mode
88+
if (flock($lock, LOCK_EX | LOCK_NB)) {
89+
self::$lockedFiles[$key] = true;
9090

91-
return $callback($item);
91+
return $callback($item, $save);
92+
}
93+
// if we failed the race, retry locking in blocking mode to wait for the winner
94+
flock($lock, LOCK_SH);
95+
} finally {
96+
flock($lock, LOCK_UN);
97+
unset(self::$lockedFiles[$key]);
9298
}
93-
// if we failed the race, retry locking in blocking mode to wait for the winner
94-
flock($lock, LOCK_SH);
95-
} finally {
96-
flock($lock, LOCK_UN);
97-
unset(self::$lockedFiles[$key]);
98-
}
99+
static $signalingException, $signalingCallback;
100+
$signalingException = $signalingException ?? unserialize("O:9:\"Exception\":1:{s:16:\"\0Exception\0trace\";a:0:{}}");
101+
$signalingCallback = $signalingCallback ?? function () use ($signalingException) { throw $signalingException; };
99102

100-
return $pool->get($item->getKey(), $callback, 0);
103+
try {
104+
$value = $pool->get($item->getKey(), $signalingCallback, 0);
105+
$save = false;
106+
107+
return $value;
108+
} catch (\Exception $e) {
109+
if ($signalingException !== $e) {
110+
throw $e;
111+
}
112+
}
113+
}
101114
}
102115

103116
private static function open(int $key)

src/Symfony/Component/Cache/Traits/ContractsTrait.php

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ trait ContractsTrait
3535
/**
3636
* Wraps the callback passed to ->get() in a callable.
3737
*
38-
* @param callable(ItemInterface, callable, CacheInterface):mixed $callbackWrapper
39-
*
4038
* @return callable the previous callback wrapper
4139
*/
42-
public function setCallbackWrapper(callable $callbackWrapper): callable
40+
public function setCallbackWrapper(?callable $callbackWrapper): callable
4341
{
4442
$previousWrapper = $this->callbackWrapper;
45-
$this->callbackWrapper = $callbackWrapper;
43+
$this->callbackWrapper = $callbackWrapper ?? function (ItemInterface $item, bool &$save, callable $callback, CacheInterface $pool) {
44+
return $callback($item, $save);
45+
};
4646

4747
return $previousWrapper;
4848
}
@@ -53,32 +53,35 @@ private function doGet(AdapterInterface $pool, string $key, callable $callback,
5353
throw new InvalidArgumentException(sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', \get_class($this), $beta));
5454
}
5555

56-
static $save;
56+
static $setMetadata;
5757

58-
$save = $save ?? \Closure::bind(
59-
function (AdapterInterface $pool, ItemInterface $item, $value, float $startTime) {
60-
if ($startTime && $item->expiry > $endTime = microtime(true)) {
58+
$setMetadata = $setMetadata ?? \Closure::bind(
59+
function (AdapterInterface $pool, ItemInterface $item, float $startTime) {
60+
if ($item->expiry > $endTime = microtime(true)) {
6161
$item->newMetadata[ItemInterface::METADATA_EXPIRY] = $item->expiry;
6262
$item->newMetadata[ItemInterface::METADATA_CTIME] = 1000 * (int) ($endTime - $startTime);
6363
}
64-
$pool->save($item->set($value));
65-
66-
return $value;
6764
},
6865
null,
6966
CacheItem::class
7067
);
7168

72-
return $this->contractsGet($pool, $key, function (CacheItem $item) use ($pool, $callback, $save) {
69+
return $this->contractsGet($pool, $key, function (CacheItem $item, bool &$save) use ($pool, $callback, $setMetadata) {
7370
// don't wrap nor save recursive calls
7471
if (null === $callbackWrapper = $this->callbackWrapper) {
75-
return $callback($item);
72+
$value = $callback($item, $save);
73+
$save = false;
74+
75+
return $value;
7676
}
7777
$this->callbackWrapper = null;
78-
$t = microtime(true);
78+
$startTime = microtime(true);
7979

8080
try {
81-
return $save($pool, $item, $callbackWrapper($item, $callback, $pool), $t);
81+
$value = $callbackWrapper($item, $save, $callback, $pool);
82+
$setMetadata($pool, $item, $startTime);
83+
84+
return $value;
8285
} finally {
8386
$this->callbackWrapper = $callbackWrapper;
8487
}

src/Symfony/Contracts/Cache/CacheInterface.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ interface CacheInterface
2929
* requested key, that could be used e.g. for expiration control. It could also
3030
* be an ItemInterface instance when its additional features are needed.
3131
*
32-
* @param string $key The key of the item to retrieve from the cache
33-
* @param callable(CacheItemInterface):mixed $callback Should return the computed value for the given key/item
34-
* @param float|null $beta A float that, as it grows, controls the likeliness of triggering
35-
* early expiration. 0 disables it, INF forces immediate expiration.
36-
* The default (or providing null) is implementation dependent but should
37-
* typically be 1.0, which should provide optimal stampede protection.
38-
* See https://en.wikipedia.org/wiki/Cache_stampede#Probabilistic_early_expiration
32+
* @param string $key The key of the item to retrieve from the cache
33+
* @param callable|CallbackInterface $callback Should return the computed value for the given key/item
34+
* @param float|null $beta A float that, as it grows, controls the likeliness of triggering
35+
* early expiration. 0 disables it, INF forces immediate expiration.
36+
* The default (or providing null) is implementation dependent but should
37+
* typically be 1.0, which should provide optimal stampede protection.
38+
* See https://en.wikipedia.org/wiki/Cache_stampede#Probabilistic_early_expiration
3939
*
4040
* @return mixed The value corresponding to the provided key
4141
*

src/Symfony/Contracts/Cache/CacheTrait.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ private function doGet(CacheItemPoolInterface $pool, string $key, callable $call
5959
}
6060

6161
if ($recompute) {
62-
$pool->save($item->set($callback($item)));
62+
$save = true;
63+
$item->set($callback($item, $save));
64+
if ($save) {
65+
$pool->save($item);
66+
}
6367
}
6468

6569
return $item->get();
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Contracts\Cache;
13+
14+
use Psr\Cache\CacheItemInterface;
15+
16+
/**
17+
* Computes and returns the cached value of an item.
18+
*
19+
* @author Nicolas Grekas <[email protected]>
20+
*/
21+
interface CallbackInterface
22+
{
23+
/**
24+
* @param CacheIntemInterface|ItemInterface $item The item to compute the value for
25+
* @param bool &$save Should be set to false when the value should not be saved in the pool
26+
*
27+
* @return mixed The computed value for the passed item
28+
*/
29+
public function __invoke(CacheItemInterface $item, bool &$save);
30+
}

src/Symfony/Contracts/Cache/TagAwareCacheInterface.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ interface TagAwareCacheInterface extends CacheInterface
2222
{
2323
/**
2424
* {@inheritdoc}
25-
*
26-
* @param callable(ItemInterface):mixed $callback Should return the computed value for the given key/item
2725
*/
2826
public function get(string $key, callable $callback, float $beta = null);
2927

0 commit comments

Comments
 (0)