6
6
7
7
namespace Magento \Framework \Cache \Backend ;
8
8
9
- use Magento \Framework \App \ObjectManager ;
10
- use Magento \Framework \Lock \LockManagerInterface ;
11
-
12
9
/**
13
10
* Remote synchronized cache
14
11
*
@@ -45,10 +42,6 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
45
42
*/
46
43
private const HASH_SUFFIX = ':hash ' ;
47
44
48
- /**
49
- * @var LockManagerInterface
50
- */
51
- private $ lockManager ;
52
45
53
46
/**
54
47
* @inheritdoc
@@ -61,9 +54,24 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
61
54
'local_backend ' => '' ,
62
55
'local_backend_options ' => [],
63
56
'local_backend_custom_naming ' => true ,
64
- 'local_backend_autoload ' => true
57
+ 'local_backend_autoload ' => true ,
58
+ 'use_stale_cache ' => false ,
65
59
];
66
60
61
+ /**
62
+ * In memory state for locks.
63
+ *
64
+ * @var array
65
+ */
66
+ private $ lockArray = [];
67
+
68
+ /**
69
+ * Sign for locks, helps to avoid removing a lock that was created by another client
70
+ *
71
+ * @string
72
+ */
73
+ private $ lockSign ;
74
+
67
75
/**
68
76
* @param array $options
69
77
* @throws \Zend_Cache_Exception
@@ -110,7 +118,7 @@ public function __construct(array $options = [])
110
118
}
111
119
}
112
120
113
- $ this ->lockManager = ObjectManager:: getInstance ()-> get (LockManagerInterface::class );
121
+ $ this ->lockSign = $ this -> generateLockSign ( );
114
122
}
115
123
116
124
/**
@@ -186,14 +194,21 @@ public function load($id, $doNotTestCacheValidity = false)
186
194
}
187
195
} else {
188
196
if ($ this ->getDataVersion ($ localData ) !== $ this ->loadRemoteDataVersion ($ id )) {
189
- $ localData = false ;
190
197
$ remoteData = $ this ->remote ->load ($ id );
198
+
199
+ if (!$ this ->_options ['use_stale_cache ' ]) {
200
+ $ localData = false ;
201
+ }
191
202
}
192
203
}
193
204
194
205
if ($ remoteData !== false ) {
195
206
$ this ->local ->save ($ remoteData , $ id );
196
207
$ localData = $ remoteData ;
208
+ } elseif ($ this ->_options ['use_stale_cache ' ] && $ localData !== false ) {
209
+ if ($ this ->lock ($ id )) {
210
+ return false ;
211
+ }
197
212
}
198
213
199
214
return $ localData ;
@@ -222,6 +237,10 @@ public function save($data, $id, $tags = [], $specificLifetime = false)
222
237
$ this ->saveRemoteDataVersion ($ data , $ id , $ tags , $ specificLifetime );
223
238
}
224
239
240
+ if ($ this ->_options ['use_stale_cache ' ]) {
241
+ $ this ->unlock ($ id );
242
+ }
243
+
225
244
return $ this ->local ->save ($ dataToSave , $ id , [], $ specificLifetime );
226
245
}
227
246
@@ -314,4 +333,114 @@ public function getCapabilities()
314
333
{
315
334
return $ this ->local ->getCapabilities ();
316
335
}
336
+
337
+ /**
338
+ * Sets a lock
339
+ *
340
+ * @param string $id
341
+ * @return bool
342
+ */
343
+ private function lock (string $ id ): bool
344
+ {
345
+ $ timeout = 10 ;
346
+ $ this ->lockArray [$ id ] = microtime (true );
347
+
348
+ $ data = $ this ->remote ->load ($ this ->getLockName ($ id ));
349
+
350
+ if (false !== $ data ) {
351
+ return false ;
352
+ }
353
+
354
+ $ this ->remote ->save ($ this ->lockSign , $ this ->getLockName ($ id ), [], $ timeout * 100 );
355
+
356
+ $ data = $ this ->remote ->load ($ this ->getLockName ($ id ));
357
+
358
+ if ($ data === $ this ->lockSign ) {
359
+ return true ;
360
+ }
361
+
362
+ return false ;
363
+ }
364
+
365
+ /**
366
+ * Release a lock.
367
+ *
368
+ * @param string $id
369
+ * @return bool
370
+ */
371
+ private function unlock (string $ id ): bool
372
+ {
373
+ if (isset ($ this ->lockArray [$ id ])) {
374
+ unset($ this ->lockArray [$ id ]);
375
+ }
376
+
377
+ $ data = $ this ->remote ->load ($ this ->getLockName ($ id ));
378
+
379
+ if (false === $ data ) {
380
+ return false ;
381
+ }
382
+
383
+ $ removeResult = false ;
384
+ if ($ data === $ this ->lockSign ) {
385
+ $ removeResult = (bool )$ this ->remote ->remove ($ this ->getLockName ($ id ));
386
+ }
387
+
388
+ return $ removeResult ;
389
+ }
390
+
391
+ /**
392
+ * Calculate lock name.
393
+ *
394
+ * @param $id
395
+ * @return string
396
+ */
397
+ private function getLockName ($ id ): string
398
+ {
399
+ return 'REMOTE_SYNC_LOCK_ ' . $ id ;
400
+ }
401
+
402
+ /**
403
+ * Release all locks.
404
+ *
405
+ * @return void
406
+ */
407
+ private function unlockAll ()
408
+ {
409
+ foreach ($ this ->lockArray as $ id => $ ttl ) {
410
+ $ this ->unlock ($ id );
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Release all locks on destruct.
416
+ *
417
+ * @return void
418
+ */
419
+ public function __destruct ()
420
+ {
421
+ $ this ->unlockAll ();
422
+ }
423
+
424
+ /**
425
+ * Function that generates lock sign that helps to avoid removing a lock that was created by another client.
426
+ *
427
+ * @return string
428
+ */
429
+ private function generateLockSign ()
430
+ {
431
+ $ sign = implode (
432
+ '- ' ,
433
+ [
434
+ \getmypid (), \crc32 (\gethostname ())
435
+ ]
436
+ );
437
+
438
+ try {
439
+ $ sign .= '- ' . \bin2hex (\random_bytes (4 ));
440
+ } catch (\Exception $ e ) {
441
+ $ sign .= '- ' . \uniqid ('-uniqid- ' );
442
+ }
443
+
444
+ return $ sign ;
445
+ }
317
446
}
0 commit comments