Skip to content

Commit 61df34a

Browse files
author
Joan He
committed
Merge remote-tracking branch 'trigger/MC-6275' into pr
2 parents a7aadd4 + f032c76 commit 61df34a

File tree

21 files changed

+801
-249
lines changed

21 files changed

+801
-249
lines changed

app/code/Magento/Config/App/Config/Type/System.php

Lines changed: 122 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Magento\Config\App\Config\Type\System\Reader;
1414
use Magento\Framework\App\ScopeInterface;
1515
use Magento\Framework\Cache\FrontendInterface;
16+
use Magento\Framework\Lock\LockManagerInterface;
1617
use Magento\Framework\Serialize\SerializerInterface;
1718
use Magento\Store\Model\Config\Processor\Fallback;
1819
use Magento\Store\Model\ScopeInterface as StoreScope;
@@ -27,9 +28,37 @@
2728
*/
2829
class System implements ConfigTypeInterface
2930
{
31+
/**
32+
* Config cache tag.
33+
*/
3034
const CACHE_TAG = 'config_scopes';
35+
36+
/**
37+
* System config type.
38+
*/
3139
const CONFIG_TYPE = 'system';
3240

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+
3362
/**
3463
* @var array
3564
*/
@@ -76,6 +105,11 @@ class System implements ConfigTypeInterface
76105
*/
77106
private $encryptor;
78107

108+
/**
109+
* @var LockManagerInterface
110+
*/
111+
private $locker;
112+
79113
/**
80114
* @param ConfigSourceInterface $source
81115
* @param PostProcessorInterface $postProcessor
@@ -87,7 +121,7 @@ class System implements ConfigTypeInterface
87121
* @param string $configType
88122
* @param Reader|null $reader
89123
* @param Encryptor|null $encryptor
90-
*
124+
* @param LockManagerInterface|null $locker
91125
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
92126
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
93127
*/
@@ -101,14 +135,18 @@ public function __construct(
101135
$cachingNestedLevel = 1,
102136
$configType = self::CONFIG_TYPE,
103137
Reader $reader = null,
104-
Encryptor $encryptor = null
138+
Encryptor $encryptor = null,
139+
LockManagerInterface $locker = null
105140
) {
106141
$this->postProcessor = $postProcessor;
107142
$this->cache = $cache;
108143
$this->serializer = $serializer;
109144
$this->configType = $configType;
110145
$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);
112150
}
113151

114152
/**
@@ -187,21 +225,61 @@ private function getWithParts($path)
187225
}
188226

189227
/**
190-
* Load configuration data for all scopes
228+
* Make lock on data load.
191229
*
230+
* @param callable $dataLoader
231+
* @param bool $flush
192232
* @return array
193233
*/
194-
private function loadAllData()
234+
private function lockedLoadData(callable $dataLoader, bool $flush = false): array
195235
{
196-
$cachedData = $this->cache->load($this->configType);
236+
$cachedData = $dataLoader(); //optimistic read
197237

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();
202241
}
203242

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+
});
205283
}
206284

207285
/**
@@ -212,16 +290,14 @@ private function loadAllData()
212290
*/
213291
private function loadDefaultScopeData($scopeType)
214292
{
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+
});
225301
}
226302

227303
/**
@@ -233,31 +309,31 @@ private function loadDefaultScopeData($scopeType)
233309
*/
234310
private function loadScopeData($scopeType, $scopeId)
235311
{
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+
}
244322
}
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)]];
245329
}
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-
}
255330

256-
return $data;
331+
return $scopeData;
332+
});
257333
}
258334

259335
/**
260-
* Cache configuration data
336+
* Cache configuration data.
261337
*
262338
* Caches data per scope to avoid reading data for all scopes on every request
263339
*
@@ -344,6 +420,11 @@ private function readData(): array
344420
public function clean()
345421
{
346422
$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+
);
348429
}
349430
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Config\Setup;
9+
10+
use Magento\Framework\App\DeploymentConfig;
11+
use Magento\Framework\Config\Data\ConfigData;
12+
use Magento\Framework\Config\Data\ConfigDataFactory;
13+
use Magento\Framework\Config\File\ConfigFilePool;
14+
use Magento\Framework\Setup\ConfigOptionsListInterface;
15+
use Magento\Framework\Setup\Option\SelectConfigOption;
16+
17+
/**
18+
* Deployment configuration options required for the Config module.
19+
*/
20+
class ConfigOptionsList implements ConfigOptionsListInterface
21+
{
22+
/**
23+
* Input key for the debug_logging option.
24+
*/
25+
const INPUT_KEY_DEBUG_LOGGING = 'enable-debug-logging';
26+
27+
/**
28+
* Path to the debug_logging value in the deployment config.
29+
*/
30+
const CONFIG_PATH_DEBUG_LOGGING = 'dev/debug/debug_logging';
31+
32+
/**
33+
* Input key for the syslog_logging option.
34+
*/
35+
const INPUT_KEY_SYSLOG_LOGGING = 'enable-syslog-logging';
36+
37+
/**
38+
* Path to the syslog_logging value in the deployment config.
39+
*/
40+
const CONFIG_PATH_SYSLOG_LOGGING = 'dev/syslog/syslog_logging';
41+
42+
/**
43+
* @var ConfigDataFactory
44+
*/
45+
private $configDataFactory;
46+
47+
/**
48+
* @param ConfigDataFactory $configDataFactory
49+
*/
50+
public function __construct(ConfigDataFactory $configDataFactory)
51+
{
52+
$this->configDataFactory = $configDataFactory;
53+
}
54+
55+
/**
56+
* @inheritdoc
57+
*/
58+
public function getOptions()
59+
{
60+
return [
61+
new SelectConfigOption(
62+
self::INPUT_KEY_DEBUG_LOGGING,
63+
SelectConfigOption::FRONTEND_WIZARD_RADIO,
64+
[true, false, 1, 0],
65+
self::CONFIG_PATH_DEBUG_LOGGING,
66+
'Enable debug logging'
67+
),
68+
new SelectConfigOption(
69+
self::INPUT_KEY_SYSLOG_LOGGING,
70+
SelectConfigOption::FRONTEND_WIZARD_RADIO,
71+
[true, false, 1, 0],
72+
self::CONFIG_PATH_SYSLOG_LOGGING,
73+
'Enable syslog logging'
74+
),
75+
];
76+
}
77+
78+
/**
79+
* @inheritdoc
80+
*/
81+
public function createConfig(array $options, DeploymentConfig $deploymentConfig)
82+
{
83+
$deploymentOption = [
84+
self::INPUT_KEY_DEBUG_LOGGING => self::CONFIG_PATH_DEBUG_LOGGING,
85+
self::INPUT_KEY_SYSLOG_LOGGING => self::CONFIG_PATH_SYSLOG_LOGGING,
86+
];
87+
88+
$config = [];
89+
foreach ($deploymentOption as $inputKey => $configPath) {
90+
$configValue = $this->processBooleanConfigValue(
91+
$inputKey,
92+
$configPath,
93+
$options
94+
);
95+
if ($configValue) {
96+
$config[] = $configValue;
97+
}
98+
}
99+
100+
return $config;
101+
}
102+
103+
/**
104+
* Provide config value from input.
105+
*
106+
* @param string $inputKey
107+
* @param string $configPath
108+
* @param array $options
109+
* @return ConfigData|null
110+
*/
111+
private function processBooleanConfigValue(string $inputKey, string $configPath, array &$options): ?ConfigData
112+
{
113+
$configData = null;
114+
if (isset($options[$inputKey])) {
115+
$configData = $this->configDataFactory->create(ConfigFilePool::APP_ENV);
116+
if ($options[$inputKey] === 'true'
117+
|| $options[$inputKey] === '1') {
118+
$value = 1;
119+
} else {
120+
$value = 0;
121+
}
122+
$configData->set($configPath, $value);
123+
}
124+
125+
return $configData;
126+
}
127+
128+
/**
129+
* @inheritdoc
130+
*/
131+
public function validate(array $options, DeploymentConfig $deploymentConfig)
132+
{
133+
return [];
134+
}
135+
}

0 commit comments

Comments
 (0)