13
13
use Magento \Config \App \Config \Type \System \Reader ;
14
14
use Magento \Framework \App \ScopeInterface ;
15
15
use Magento \Framework \Cache \FrontendInterface ;
16
+ use Magento \Framework \Lock \LockManagerInterface ;
16
17
use Magento \Framework \Serialize \SerializerInterface ;
17
18
use Magento \Store \Model \Config \Processor \Fallback ;
18
19
use Magento \Store \Model \ScopeInterface as StoreScope ;
27
28
*/
28
29
class System implements ConfigTypeInterface
29
30
{
31
+ /**
32
+ * Config cache tag.
33
+ */
30
34
const CACHE_TAG = 'config_scopes ' ;
35
+
36
+ /**
37
+ * System config type.
38
+ */
31
39
const CONFIG_TYPE = 'system ' ;
32
40
41
+ /**
42
+ * @var string
43
+ */
44
+ private static $ lockName = 'SYSTEM_CONFIG ' ;
45
+ /**
46
+ * Timeout between retrieves to load the configuration from the cache.
47
+ *
48
+ * Value of the variable in microseconds.
49
+ *
50
+ * @var int
51
+ */
52
+ private static $ delayTimeout = 100000 ;
53
+ /**
54
+ * Lifetime of the lock for write in cache.
55
+ *
56
+ * Value of the variable in seconds.
57
+ *
58
+ * @var int
59
+ */
60
+ private static $ lockTimeout = 42 ;
61
+
33
62
/**
34
63
* @var array
35
64
*/
@@ -76,6 +105,11 @@ class System implements ConfigTypeInterface
76
105
*/
77
106
private $ encryptor ;
78
107
108
+ /**
109
+ * @var LockManagerInterface
110
+ */
111
+ private $ locker ;
112
+
79
113
/**
80
114
* @param ConfigSourceInterface $source
81
115
* @param PostProcessorInterface $postProcessor
@@ -87,7 +121,7 @@ class System implements ConfigTypeInterface
87
121
* @param string $configType
88
122
* @param Reader|null $reader
89
123
* @param Encryptor|null $encryptor
90
- *
124
+ * @param LockManagerInterface|null $locker
91
125
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
92
126
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
93
127
*/
@@ -101,14 +135,18 @@ public function __construct(
101
135
$ cachingNestedLevel = 1 ,
102
136
$ configType = self ::CONFIG_TYPE ,
103
137
Reader $ reader = null ,
104
- Encryptor $ encryptor = null
138
+ Encryptor $ encryptor = null ,
139
+ LockManagerInterface $ locker = null
105
140
) {
106
141
$ this ->postProcessor = $ postProcessor ;
107
142
$ this ->cache = $ cache ;
108
143
$ this ->serializer = $ serializer ;
109
144
$ this ->configType = $ configType ;
110
145
$ this ->reader = $ reader ?: ObjectManager::getInstance ()->get (Reader::class);
111
- $ this ->encryptor = $ encryptor ?: ObjectManager::getInstance ()->get (\Magento \Framework \Encryption \Encryptor::class);
146
+ $ this ->encryptor = $ encryptor
147
+ ?: ObjectManager::getInstance ()->get (Encryptor::class);
148
+ $ this ->locker = $ locker
149
+ ?: ObjectManager::getInstance ()->get (LockManagerInterface::class);
112
150
}
113
151
114
152
/**
@@ -187,21 +225,61 @@ private function getWithParts($path)
187
225
}
188
226
189
227
/**
190
- * Load configuration data for all scopes
228
+ * Make lock on data load.
191
229
*
230
+ * @param callable $dataLoader
231
+ * @param bool $flush
192
232
* @return array
193
233
*/
194
- private function loadAllData ()
234
+ private function lockedLoadData ( callable $ dataLoader , bool $ flush = false ): array
195
235
{
196
- $ cachedData = $ this -> cache -> load ( $ this -> configType );
236
+ $ cachedData = $ dataLoader (); //optimistic read
197
237
198
- if ($ cachedData === false ) {
199
- $ data = $ this ->readData ();
200
- } else {
201
- $ data = $ this ->serializer ->unserialize ($ this ->encryptor ->decrypt ($ cachedData ));
238
+ while ($ cachedData === false && $ this ->locker ->isLocked (self ::$ lockName )) {
239
+ usleep (self ::$ delayTimeout );
240
+ $ cachedData = $ dataLoader ();
202
241
}
203
242
204
- return $ data ;
243
+ while ($ cachedData === false ) {
244
+ try {
245
+ if ($ this ->locker ->lock (self ::$ lockName , self ::$ lockTimeout )) {
246
+ if (!$ flush ) {
247
+ $ data = $ this ->readData ();
248
+ $ this ->cacheData ($ data );
249
+ $ cachedData = $ data ;
250
+ } else {
251
+ $ this ->cache ->clean (\Zend_Cache::CLEANING_MODE_MATCHING_TAG , [self ::CACHE_TAG ]);
252
+ $ cachedData = [];
253
+ }
254
+ }
255
+ } finally {
256
+ $ this ->locker ->unlock (self ::$ lockName );
257
+ }
258
+
259
+ if ($ cachedData === false ) {
260
+ usleep (self ::$ delayTimeout );
261
+ $ cachedData = $ dataLoader ();
262
+ }
263
+ }
264
+
265
+ return $ cachedData ;
266
+ }
267
+
268
+ /**
269
+ * Load configuration data for all scopes
270
+ *
271
+ * @return array
272
+ */
273
+ private function loadAllData ()
274
+ {
275
+ return $ this ->lockedLoadData (function () {
276
+ $ cachedData = $ this ->cache ->load ($ this ->configType );
277
+ $ data = false ;
278
+ if ($ cachedData !== false ) {
279
+ $ data = $ this ->serializer ->unserialize ($ this ->encryptor ->decrypt ($ cachedData ));
280
+ }
281
+ return $ data ;
282
+ });
205
283
}
206
284
207
285
/**
@@ -212,16 +290,14 @@ private function loadAllData()
212
290
*/
213
291
private function loadDefaultScopeData ($ scopeType )
214
292
{
215
- $ cachedData = $ this ->cache ->load ($ this ->configType . '_ ' . $ scopeType );
216
-
217
- if ($ cachedData === false ) {
218
- $ data = $ this ->readData ();
219
- $ this ->cacheData ($ data );
220
- } else {
221
- $ data = [$ scopeType => $ this ->serializer ->unserialize ($ this ->encryptor ->decrypt ($ cachedData ))];
222
- }
223
-
224
- return $ data ;
293
+ return $ this ->lockedLoadData (function () use ($ scopeType ) {
294
+ $ cachedData = $ this ->cache ->load ($ this ->configType . '_ ' . $ scopeType );
295
+ $ scopeData = false ;
296
+ if ($ cachedData !== false ) {
297
+ $ scopeData = [$ scopeType => $ this ->serializer ->unserialize ($ this ->encryptor ->decrypt ($ cachedData ))];
298
+ }
299
+ return $ scopeData ;
300
+ });
225
301
}
226
302
227
303
/**
@@ -233,31 +309,31 @@ private function loadDefaultScopeData($scopeType)
233
309
*/
234
310
private function loadScopeData ($ scopeType , $ scopeId )
235
311
{
236
- $ cachedData = $ this ->cache ->load ($ this ->configType . '_ ' . $ scopeType . '_ ' . $ scopeId );
237
-
238
- if ($ cachedData === false ) {
239
- if ($ this ->availableDataScopes === null ) {
240
- $ cachedScopeData = $ this ->cache ->load ($ this ->configType . '_scopes ' );
241
- if ($ cachedScopeData !== false ) {
242
- $ serializedCachedData = $ this ->encryptor ->decrypt ($ cachedScopeData );
243
- $ this ->availableDataScopes = $ this ->serializer ->unserialize ($ serializedCachedData );
312
+ return $ this ->lockedLoadData (function () use ($ scopeType , $ scopeId ) {
313
+ $ cachedData = $ this ->cache ->load ($ this ->configType . '_ ' . $ scopeType . '_ ' . $ scopeId );
314
+ $ scopeData = false ;
315
+ if ($ cachedData === false ) {
316
+ if ($ this ->availableDataScopes === null ) {
317
+ $ cachedScopeData = $ this ->cache ->load ($ this ->configType . '_scopes ' );
318
+ if ($ cachedScopeData !== false ) {
319
+ $ serializedCachedData = $ this ->encryptor ->decrypt ($ cachedScopeData );
320
+ $ this ->availableDataScopes = $ this ->serializer ->unserialize ($ serializedCachedData );
321
+ }
244
322
}
323
+ if (is_array ($ this ->availableDataScopes ) && !isset ($ this ->availableDataScopes [$ scopeType ][$ scopeId ])) {
324
+ $ scopeData = [$ scopeType => [$ scopeId => []]];
325
+ }
326
+ } else {
327
+ $ serializedCachedData = $ this ->encryptor ->decrypt ($ cachedData );
328
+ $ scopeData = [$ scopeType => [$ scopeId => $ this ->serializer ->unserialize ($ serializedCachedData )]];
245
329
}
246
- if (is_array ($ this ->availableDataScopes ) && !isset ($ this ->availableDataScopes [$ scopeType ][$ scopeId ])) {
247
- return [$ scopeType => [$ scopeId => []]];
248
- }
249
- $ data = $ this ->readData ();
250
- $ this ->cacheData ($ data );
251
- } else {
252
- $ serializedCachedData = $ this ->encryptor ->decrypt ($ cachedData );
253
- $ data = [$ scopeType => [$ scopeId => $ this ->serializer ->unserialize ($ serializedCachedData )]];
254
- }
255
330
256
- return $ data ;
331
+ return $ scopeData ;
332
+ });
257
333
}
258
334
259
335
/**
260
- * Cache configuration data
336
+ * Cache configuration data.
261
337
*
262
338
* Caches data per scope to avoid reading data for all scopes on every request
263
339
*
@@ -344,6 +420,11 @@ private function readData(): array
344
420
public function clean ()
345
421
{
346
422
$ this ->data = [];
347
- $ this ->cache ->clean (\Zend_Cache::CLEANING_MODE_MATCHING_TAG , [self ::CACHE_TAG ]);
423
+ $ this ->lockedLoadData (
424
+ function () {
425
+ return false ;
426
+ },
427
+ true
428
+ );
348
429
}
349
430
}
0 commit comments