11
11
use Magento \Framework \App \Config \Spi \PreProcessorInterface ;
12
12
use Magento \Framework \App \ObjectManager ;
13
13
use Magento \Config \App \Config \Type \System \Reader ;
14
+ use Magento \Framework \Lock \LockManagerInterface ;
14
15
use Magento \Framework \Serialize \Serializer \Sensitive as SensitiveSerializer ;
15
16
use Magento \Framework \Serialize \Serializer \SensitiveFactory as SensitiveSerializerFactory ;
16
17
use Magento \Framework \App \ScopeInterface ;
24
25
*
25
26
* @api
26
27
* @since 100.1.2
28
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
27
29
*/
28
30
class System implements ConfigTypeInterface
29
31
{
32
+ /**
33
+ * Config cache tag.
34
+ */
30
35
const CACHE_TAG = 'config_scopes ' ;
36
+
37
+ /**
38
+ * System config type.
39
+ */
31
40
const CONFIG_TYPE = 'system ' ;
32
41
42
+ /**
43
+ * @var string
44
+ */
45
+ private static $ lockName = 'SYSTEM_CONFIG ' ;
46
+
47
+ /**
48
+ * Timeout between retrieves to load the configuration from the cache.
49
+ *
50
+ * Value of the variable in microseconds.
51
+ *
52
+ * @var int
53
+ */
54
+ private static $ delayTimeout = 50000 ;
55
+
56
+ /**
57
+ * Lifetime of the lock for write in cache.
58
+ *
59
+ * Value of the variable in seconds.
60
+ *
61
+ * @var int
62
+ */
63
+ private static $ lockTimeout = 8 ;
64
+
33
65
/**
34
66
* @var array
35
67
*/
@@ -71,6 +103,11 @@ class System implements ConfigTypeInterface
71
103
*/
72
104
private $ availableDataScopes ;
73
105
106
+ /**
107
+ * @var LockManagerInterface
108
+ */
109
+ private $ locker ;
110
+
74
111
/**
75
112
* @param ConfigSourceInterface $source
76
113
* @param PostProcessorInterface $postProcessor
@@ -82,7 +119,7 @@ class System implements ConfigTypeInterface
82
119
* @param string $configType
83
120
* @param Reader $reader
84
121
* @param SensitiveSerializerFactory|null $sensitiveFactory
85
- *
122
+ * @param LockManagerInterface|null $locker
86
123
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
87
124
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
88
125
*/
@@ -96,7 +133,8 @@ public function __construct(
96
133
$ cachingNestedLevel = 1 ,
97
134
$ configType = self ::CONFIG_TYPE ,
98
135
Reader $ reader = null ,
99
- SensitiveSerializerFactory $ sensitiveFactory = null
136
+ SensitiveSerializerFactory $ sensitiveFactory = null ,
137
+ LockManagerInterface $ locker = null
100
138
) {
101
139
$ this ->postProcessor = $ postProcessor ;
102
140
$ this ->cache = $ cache ;
@@ -110,6 +148,7 @@ public function __construct(
110
148
$ this ->serializer = $ sensitiveFactory ->create (
111
149
['serializer ' => $ serializer ]
112
150
);
151
+ $ this ->locker = $ locker ?: ObjectManager::getInstance ()->get (LockManagerInterface::class);
113
152
}
114
153
115
154
/**
@@ -153,7 +192,7 @@ private function getWithParts($path)
153
192
154
193
if (count ($ pathParts ) === 1 && $ pathParts [0 ] !== ScopeInterface::SCOPE_DEFAULT ) {
155
194
if (!isset ($ this ->data [$ pathParts [0 ]])) {
156
- $ data = $ this ->readData ();
195
+ $ data = $ this ->loadAllData ();
157
196
$ this ->data = array_replace_recursive ($ data , $ this ->data );
158
197
}
159
198
@@ -186,21 +225,60 @@ private function getWithParts($path)
186
225
}
187
226
188
227
/**
189
- * Load configuration data for all scopes
228
+ * Make lock on data load.
190
229
*
230
+ * @param callable $dataLoader
231
+ * @param bool $flush
191
232
* @return array
192
233
*/
193
- private function loadAllData ()
234
+ private function lockedLoadData ( callable $ dataLoader , bool $ flush = false ): array
194
235
{
195
- $ cachedData = $ this -> cache -> load ( $ this -> configType );
236
+ $ cachedData = $ dataLoader (); //optimistic read
196
237
197
- if ($ cachedData === false ) {
198
- $ data = $ this ->readData ();
199
- } else {
200
- $ data = $ this ->serializer ->unserialize ($ cachedData );
238
+ while ($ cachedData === false && $ this ->locker ->isLocked (self ::$ lockName )) {
239
+ usleep (self ::$ delayTimeout );
240
+ $ cachedData = $ dataLoader ();
201
241
}
202
242
203
- 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
+ if ($ cachedData === false ) {
278
+ return $ cachedData ;
279
+ }
280
+ return $ this ->serializer ->unserialize ($ cachedData );
281
+ });
204
282
}
205
283
206
284
/**
@@ -211,16 +289,13 @@ private function loadAllData()
211
289
*/
212
290
private function loadDefaultScopeData ($ scopeType )
213
291
{
214
- $ cachedData = $ this ->cache ->load ($ this ->configType . '_ ' . $ scopeType );
215
-
216
- if ($ cachedData === false ) {
217
- $ data = $ this ->readData ();
218
- $ this ->cacheData ($ data );
219
- } else {
220
- $ data = [$ scopeType => $ this ->serializer ->unserialize ($ cachedData )];
221
- }
222
-
223
- return $ data ;
292
+ return $ this ->lockedLoadData (function () use ($ scopeType ) {
293
+ $ cachedData = $ this ->cache ->load ($ this ->configType . '_ ' . $ scopeType );
294
+ if ($ cachedData === false ) {
295
+ return $ cachedData ;
296
+ }
297
+ return [$ scopeType => $ this ->serializer ->unserialize ($ cachedData )];
298
+ });
224
299
}
225
300
226
301
/**
@@ -232,25 +307,22 @@ private function loadDefaultScopeData($scopeType)
232
307
*/
233
308
private function loadScopeData ($ scopeType , $ scopeId )
234
309
{
235
- $ cachedData = $ this ->cache ->load ($ this ->configType . '_ ' . $ scopeType . '_ ' . $ scopeId );
236
-
237
- if ($ cachedData === false ) {
238
- if ($ this ->availableDataScopes === null ) {
239
- $ cachedScopeData = $ this ->cache ->load ($ this ->configType . '_scopes ' );
240
- if ($ cachedScopeData !== false ) {
241
- $ this ->availableDataScopes = $ this ->serializer ->unserialize ($ cachedScopeData );
310
+ return $ this ->lockedLoadData (function () use ($ scopeType , $ scopeId ) {
311
+ $ cachedData = $ this ->cache ->load ($ this ->configType . '_ ' . $ scopeType . '_ ' . $ scopeId );
312
+ if ($ cachedData === false ) {
313
+ if ($ this ->availableDataScopes === null ) {
314
+ $ cachedScopeData = $ this ->cache ->load ($ this ->configType . '_scopes ' );
315
+ if ($ cachedScopeData !== false ) {
316
+ $ this ->availableDataScopes = $ this ->serializer ->unserialize ($ cachedScopeData );
317
+ }
242
318
}
319
+ if (is_array ($ this ->availableDataScopes ) && !isset ($ this ->availableDataScopes [$ scopeType ][$ scopeId ])) {
320
+ return [$ scopeType => [$ scopeId => []]];
321
+ }
322
+ return false ;
243
323
}
244
- if (is_array ($ this ->availableDataScopes ) && !isset ($ this ->availableDataScopes [$ scopeType ][$ scopeId ])) {
245
- return [$ scopeType => [$ scopeId => []]];
246
- }
247
- $ data = $ this ->readData ();
248
- $ this ->cacheData ($ data );
249
- } else {
250
- $ data = [$ scopeType => [$ scopeId => $ this ->serializer ->unserialize ($ cachedData )]];
251
- }
252
-
253
- return $ data ;
324
+ return [$ scopeType => [$ scopeId => $ this ->serializer ->unserialize ($ cachedData )]];
325
+ });
254
326
}
255
327
256
328
/**
@@ -340,6 +412,11 @@ private function readData(): array
340
412
public function clean ()
341
413
{
342
414
$ this ->data = [];
343
- $ this ->cache ->clean (\Zend_Cache::CLEANING_MODE_MATCHING_TAG , [self ::CACHE_TAG ]);
415
+ $ this ->lockedLoadData (
416
+ function () {
417
+ return false ;
418
+ },
419
+ true
420
+ );
344
421
}
345
422
}
0 commit comments