11
11
12
12
namespace Symfony \Component \Cache \Traits ;
13
13
14
- use Psr \ Cache \CacheItemPoolInterface ;
14
+ use Symfony \ Component \ Cache \Adapter \ AdapterInterface ;
15
15
use Symfony \Component \Cache \CacheItem ;
16
16
use Symfony \Component \Cache \Exception \InvalidArgumentException ;
17
17
use Symfony \Component \Cache \LockRegistry ;
18
+ use Symfony \Contracts \Cache \CacheInterface ;
19
+ use Symfony \Contracts \Cache \ItemInterface ;
18
20
19
21
/**
20
- * An implementation for CacheInterface that provides stampede protection via probabilistic early expiration.
21
- *
22
- * @see https://en.wikipedia.org/wiki/Cache_stampede
23
- *
24
22
* @author Nicolas Grekas <p@tchwork.com>
25
23
*
26
24
* @internal
27
25
*/
28
26
trait GetTrait
29
27
{
28
+ private $ callbackWrapper = array (LockRegistry::class, 'compute ' );
29
+
30
+ /**
31
+ * Wraps the callback passed to ->get() in a callable.
32
+ *
33
+ * @param callable(ItemInterface, callable, CacheInterface):mixed $callbackWrapper
34
+ *
35
+ * @return callable the previous callback wrapper
36
+ */
37
+ public function setCallbackWrapper (callable $ callbackWrapper ): callable
38
+ {
39
+ $ previousWrapper = $ this ->callbackWrapper ;
40
+ $ this ->callbackWrapper = $ callbackWrapper ;
41
+
42
+ return $ previousWrapper ;
43
+ }
44
+
30
45
/**
31
46
* {@inheritdoc}
32
47
*/
33
48
public function get (string $ key , callable $ callback , float $ beta = null )
34
49
{
35
- if (0 > $ beta ) {
36
- throw new InvalidArgumentException (sprintf ('Argument "$beta" provided to "%s::get()" must be a positive number, %f given. ' , \get_class ($ this ), $ beta ));
37
- }
38
-
39
- return $ this ->doGet ($ this , $ key , $ callback , $ beta ?? 1.0 );
50
+ return $ this ->doGet ($ this , $ key , $ callback , $ beta );
40
51
}
41
52
42
- private function doGet (CacheItemPoolInterface $ pool , string $ key , callable $ callback , float $ beta )
53
+ private function doGet (AdapterInterface $ pool , string $ key , callable $ callback , ? float $ beta )
43
54
{
55
+ if (0 > $ beta = $ beta ?? 1.0 ) {
56
+ throw new InvalidArgumentException (sprintf ('Argument "$beta" provided to "%s::get()" must be a positive number, %f given. ' , \get_class ($ this ), $ beta ));
57
+ }
58
+
44
59
retry:
45
60
$ t = 0 ;
46
61
$ item = $ pool ->getItem ($ key );
47
62
$ recompute = !$ item ->isHit () || INF === $ beta ;
48
63
49
- if ($ item instanceof CacheItem && 0 < $ beta ) {
64
+ if (0 < $ beta ) {
50
65
if ($ recompute ) {
51
66
$ t = microtime (true );
52
67
} else {
53
68
$ metadata = $ item ->getMetadata ();
54
- $ expiry = $ metadata [CacheItem ::METADATA_EXPIRY ] ?? false ;
55
- $ ctime = $ metadata [CacheItem ::METADATA_CTIME ] ?? false ;
69
+ $ expiry = $ metadata [ItemInterface ::METADATA_EXPIRY ] ?? false ;
70
+ $ ctime = $ metadata [ItemInterface ::METADATA_CTIME ] ?? false ;
56
71
57
72
if ($ ctime && $ expiry ) {
58
73
$ t = microtime (true );
@@ -69,11 +84,32 @@ private function doGet(CacheItemPoolInterface $pool, string $key, callable $call
69
84
return $ item ->get ();
70
85
}
71
86
72
- if (!LockRegistry::save ($ key , $ pool , $ item , $ callback , $ t , $ value )) {
73
- $ beta = 0 ;
74
- goto retry;
87
+ static $ save ;
88
+
89
+ $ save = $ save ?? \Closure::bind (
90
+ function (AdapterInterface $ pool , ItemInterface $ item , $ value , float $ startTime ) {
91
+ if ($ startTime && $ item ->expiry > $ endTime = microtime (true )) {
92
+ $ item ->newMetadata [ItemInterface::METADATA_EXPIRY ] = $ item ->expiry ;
93
+ $ item ->newMetadata [ItemInterface::METADATA_CTIME ] = 1000 * (int ) ($ endTime - $ startTime );
94
+ }
95
+ $ pool ->save ($ item ->set ($ value ));
96
+
97
+ return $ value ;
98
+ },
99
+ null ,
100
+ CacheItem::class
101
+ );
102
+
103
+ // don't wrap nor save recursive calls
104
+ if (null === $ callbackWrapper = $ this ->callbackWrapper ) {
105
+ return $ callback ($ item );
75
106
}
107
+ $ this ->callbackWrapper = null ;
76
108
77
- return $ value ;
109
+ try {
110
+ return $ save ($ pool , $ item , ($ callbackWrapper )($ item , $ callback , $ pool ), $ t );
111
+ } finally {
112
+ $ this ->callbackWrapper = $ callbackWrapper ;
113
+ }
78
114
}
79
115
}
0 commit comments