Skip to content

Commit 8d47050

Browse files
authored
MCLOUD-5721: Implement support of new Redis features added in 2.3.5 (magento#724)
1 parent 086d6e8 commit 8d47050

File tree

12 files changed

+999
-202
lines changed

12 files changed

+999
-202
lines changed

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ env:
2424
- TEST_SUITE=functional-ee FUNCTIONAL_INDEX=3
2525
- TEST_SUITE=functional-ee FUNCTIONAL_INDEX=4
2626
- TEST_SUITE=functional-ee FUNCTIONAL_INDEX=5
27+
- TEST_SUITE=functional-ee FUNCTIONAL_INDEX=6
2728

2829
stages:
2930
- test
@@ -40,6 +41,8 @@ jobs:
4041
env: TEST_SUITE=functional-ee FUNCTIONAL_INDEX=4
4142
- php: '7.1'
4243
env: TEST_SUITE=functional-ee FUNCTIONAL_INDEX=5
44+
- php: '7.1'
45+
env: TEST_SUITE=functional-ee FUNCTIONAL_INDEX=6
4346

4447
- php: '7.2'
4548
env: TEST_SUITE=functional-ee

config/schema.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,21 @@ variables:
404404
mq:
405405
host: mq.host
406406
port: 1234
407+
REDIS_BACKEND:
408+
description: "Configuration the backend model for redis cache."
409+
type: string
410+
allowed:
411+
- 'Cm_Cache_Backend_Redis'
412+
- '\Magento\Framework\Cache\Backend\Redis'
413+
- '\Magento\Framework\Cache\Backend\RemoteSynchronizedCache'
414+
stages:
415+
- deploy
416+
default:
417+
deploy: 'Cm_Cache_Backend_Redis'
418+
examples:
419+
- stage:
420+
deploy:
421+
REDIS_BACKEND: '\Magento\Framework\Cache\Backend\Redis'
407422
CACHE_CONFIGURATION:
408423
description: "Replace or modify the Magento cache configuration generated during the deployment process.
409424
To replace the existing configuration, specify values for each configuration option required for your environment.

src/Config/Factory/Cache.php

Lines changed: 159 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ class Cache
2727
*/
2828
const REDIS_DATABASE_PAGE_CACHE = 2;
2929

30+
const REDIS_BACKEND_CM_CACHE = 'Cm_Cache_Backend_Redis';
31+
const REDIS_BACKEND_REDIS_CACHE = '\Magento\Framework\Cache\Backend\Redis';
32+
const REDIS_BACKEND_REMOTE_SYNHRONIZED_CACHE = '\Magento\Framework\Cache\Backend\RemoteSynchronizedCache';
33+
34+
const AVAILABLE_REDIS_BACKEND = [
35+
self::REDIS_BACKEND_CM_CACHE,
36+
self::REDIS_BACKEND_REDIS_CACHE,
37+
self::REDIS_BACKEND_REMOTE_SYNHRONIZED_CACHE
38+
];
39+
3040
/**
3141
* @var Redis
3242
*/
@@ -73,19 +83,22 @@ public function __construct(
7383
* Returns an empty array in other case.
7484
*
7585
* @return array
86+
* @throws \Magento\MagentoCloud\Config\ConfigException
7687
*/
7788
public function get(): array
7889
{
7990
$envCacheConfiguration = (array)$this->stageConfig->get(DeployInterface::VAR_CACHE_CONFIGURATION);
91+
$envCacheBackendModel = (string)$this->stageConfig->get(DeployInterface::VAR_CACHE_REDIS_BACKEND);
8092

8193
if ($this->isCacheConfigurationValid($envCacheConfiguration)
8294
&& !$this->configMerger->isMergeRequired($envCacheConfiguration)
8395
) {
8496
if ($this->stageConfig->get(DeployInterface::VAR_REDIS_USE_SLAVE_CONNECTION)) {
8597
$this->logger->notice(
8698
sprintf(
87-
'The variable \'%s\' is ignored as you set your own cache connection in \'%s\'',
99+
'The variables \'%s\', \'%s\' are ignored as you set your own cache connection in \'%s\'',
88100
DeployInterface::VAR_REDIS_USE_SLAVE_CONNECTION,
101+
DeployInterface::VAR_CACHE_REDIS_BACKEND,
89102
DeployInterface::VAR_CACHE_CONFIGURATION
90103
)
91104
);
@@ -100,21 +113,69 @@ public function get(): array
100113
return [];
101114
}
102115

103-
$redisCache = [
104-
'backend' => 'Cm_Cache_Backend_Redis',
105-
'backend_options' => [
106-
'server' => $redisConfig['host'],
107-
'port' => $redisConfig['port'],
108-
],
109-
];
116+
if ($this->isSynchronizedConfigStructure()) {
117+
$redisCache = $this->getSynchronizedConfigStructure($envCacheBackendModel, $redisConfig);
118+
$redisCache['backend_options']['remote_backend_options'] = array_merge(
119+
$redisCache['backend_options']['remote_backend_options'],
120+
$this->getSlaveConnection($envCacheConfiguration, $redisConfig)
121+
);
122+
$finalConfig = [
123+
'frontend' => [
124+
'default' => $redisCache,
125+
],
126+
'type' => [
127+
'default' => ['frontend' => 'default'],
128+
],
129+
];
130+
} else {
131+
$redisCache = $this->getUnsyncedConfigStructure($envCacheBackendModel, $redisConfig);
132+
$slaveConnection = $this->getSlaveConnection($envCacheConfiguration, $redisConfig);
133+
if ($slaveConnection) {
134+
$redisCache['frontend_options']['write_control'] = false;
135+
$redisCache['backend_options'] = array_merge(
136+
$redisCache['backend_options'],
137+
$slaveConnection
138+
);
139+
}
140+
$finalConfig = [
141+
'frontend' => [
142+
'default' => array_replace_recursive(
143+
$redisCache,
144+
['backend_options' => ['database' => self::REDIS_DATABASE_DEFAULT]]
145+
),
146+
'page_cache' => array_replace_recursive(
147+
$redisCache,
148+
['backend_options' => ['database' => self::REDIS_DATABASE_PAGE_CACHE]]
149+
),
150+
]
151+
];
152+
}
153+
154+
return $this->configMerger->merge($finalConfig, $envCacheConfiguration);
155+
}
110156

111-
$slaveConnectionData = $this->getSlaveConnection();
112-
if ($slaveConnectionData) {
157+
/**
158+
* Retrieves Redis read connection data if it exists and variable REDIS_USE_SLAVE_CONNECTION was set as true,
159+
* also if CACHE_CONFIGURATION is compatible with slave connections.
160+
* Otherwise retrieves an empty array.
161+
*
162+
* @param array $envCacheConfiguration
163+
* @param array $redisConfig
164+
* @return array
165+
* @throws \Magento\MagentoCloud\Config\ConfigException
166+
*/
167+
private function getSlaveConnection(array $envCacheConfiguration, array $redisConfig): array
168+
{
169+
$config = [];
170+
$redisSlaveConfig = $this->redis->getSlaveConfiguration();
171+
$slaveHost = $redisSlaveConfig['host'] ?? null;
172+
173+
if ($this->stageConfig->get(DeployInterface::VAR_REDIS_USE_SLAVE_CONNECTION) && $slaveHost) {
113174
if ($this->isConfigurationCompatibleWithSlaveConnection($envCacheConfiguration, $redisConfig)) {
114-
$redisCache['backend_options']['load_from_slave'] = $slaveConnectionData;
115-
$redisCache['backend_options']['read_timeout'] = 1;
116-
$redisCache['backend_options']['retry_reads_on_master'] = 1;
117-
$redisCache['frontend_options']['write_control'] = false;
175+
$config['load_from_slave']['server'] = $slaveHost;
176+
$config['load_from_slave']['port'] = $redisSlaveConfig['port'] ?? '';
177+
$config['read_timeout'] = 1;
178+
$config['retry_reads_on_master'] = 1;
118179
$this->logger->info('Set Redis slave connection');
119180
} else {
120181
$this->logger->notice(
@@ -127,18 +188,7 @@ public function get(): array
127188
}
128189
}
129190

130-
return $this->configMerger->merge([
131-
'frontend' => [
132-
'default' => array_replace_recursive(
133-
$redisCache,
134-
['backend_options' => ['database' => self::REDIS_DATABASE_DEFAULT]]
135-
),
136-
'page_cache' => array_replace_recursive(
137-
$redisCache,
138-
['backend_options' => ['database' => self::REDIS_DATABASE_PAGE_CACHE]]
139-
),
140-
],
141-
], $envCacheConfiguration);
191+
return $config;
142192
}
143193

144194
/**
@@ -152,28 +202,6 @@ private function isCacheConfigurationValid(array $cacheConfiguration): bool
152202
return !$this->configMerger->isEmpty($cacheConfiguration) && !empty($cacheConfiguration['frontend']);
153203
}
154204

155-
/**
156-
* Retrieves Redis read connection data if it exists and variable REDIS_USE_SLAVE_CONNECTION was set as true.
157-
* Otherwise retrieves an empty array.
158-
*
159-
* @return array
160-
*/
161-
private function getSlaveConnection(): array
162-
{
163-
$connectionData = [];
164-
$redisSlaveConfig = $this->redis->getSlaveConfiguration();
165-
$slaveHost = $redisSlaveConfig['host'] ?? null;
166-
167-
if ($this->stageConfig->get(DeployInterface::VAR_REDIS_USE_SLAVE_CONNECTION) && $slaveHost) {
168-
$connectionData = [
169-
'server' => $slaveHost,
170-
'port' => $redisSlaveConfig['port'] ?? '',
171-
];
172-
}
173-
174-
return $connectionData;
175-
}
176-
177205
/**
178206
* Checks that cache configuration was changed in CACHE_CONFIGURATION variable
179207
* in not compatible way with slave connection.
@@ -183,21 +211,98 @@ private function getSlaveConnection(): array
183211
* @param array $envCacheConfig
184212
* @param array $redisConfig
185213
* @return bool
214+
* @throws \Magento\MagentoCloud\Config\ConfigException
215+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
186216
*/
187217
private function isConfigurationCompatibleWithSlaveConnection(
188218
array $envCacheConfig,
189219
array $redisConfig
190220
): bool {
191-
foreach (['default', 'page_cache'] as $type) {
192-
if ((isset($envCacheConfig['frontend'][$type]['backend_options']['server'])
193-
&& $envCacheConfig['frontend'][$type]['backend_options']['server'] !== $redisConfig['host'])
194-
|| (isset($envCacheConfig['frontend'][$type]['backend_options']['port'])
195-
&& $envCacheConfig['frontend'][$type]['backend_options']['port'] !== $redisConfig['port'])
196-
) {
221+
if ($this->isSynchronizedConfigStructure()) {
222+
$host = $envCacheConfig['frontend']['default']['backend_options']['remote_backend_options']['server']
223+
?? null;
224+
225+
$port = $envCacheConfig['frontend']['default']['backend_options']['remote_backend_options']['port']
226+
?? null;
227+
228+
if (($host !== null && $host !== $redisConfig['host'])
229+
|| ($port !== null && $port !== $redisConfig['port'])) {
197230
return false;
198231
}
232+
} else {
233+
foreach (['default', 'page_cache'] as $type) {
234+
$host = $envCacheConfig['frontend'][$type]['backend_options']['server'] ?? null;
235+
$port = $envCacheConfig['frontend'][$type]['backend_options']['port'] ?? null;
236+
237+
if (($host !== null && $host !== $redisConfig['host'])
238+
|| ($port !== null && $port !== $redisConfig['port'])) {
239+
return false;
240+
}
241+
}
199242
}
200243

201244
return true;
202245
}
246+
247+
/**
248+
* Returns backend config for unsynced cache implementation.
249+
*
250+
* @param string $envCacheBackendModel
251+
* @param array $redisConfig
252+
* @return array
253+
*/
254+
private function getUnsyncedConfigStructure(string $envCacheBackendModel, array $redisConfig): array
255+
{
256+
return [
257+
'backend' => addslashes($envCacheBackendModel),
258+
'backend_options' => [
259+
'server' => $redisConfig['host'],
260+
'port' => $redisConfig['port'],
261+
]
262+
];
263+
}
264+
265+
/**
266+
* Returns backend config for synchronized cache implementation.
267+
*
268+
* @param string $envCacheBackendModel
269+
* @param array $redisConfig
270+
* @return array
271+
*/
272+
private function getSynchronizedConfigStructure(string $envCacheBackendModel, array $redisConfig): array
273+
{
274+
return [
275+
'backend' => addslashes($envCacheBackendModel),
276+
'backend_options' => [
277+
'remote_backend' => addslashes('\Magento\Framework\Cache\Backend\Redis'),
278+
'remote_backend_options' => [
279+
'server' => $redisConfig['host'],
280+
'port' => $redisConfig['port'],
281+
'database' => self::REDIS_DATABASE_DEFAULT,
282+
'persistent' => 0,
283+
'password' => '',
284+
'compress_data' => '1',
285+
],
286+
'local_backend' => 'Cm_Cache_Backend_File',
287+
'local_backend_options' => [
288+
'cache_dir' => '/dev/shm/'
289+
]
290+
],
291+
'frontend_options' => [
292+
'write_control' => false,
293+
]
294+
];
295+
}
296+
297+
/**
298+
* Checks that config contains synchronized cache model and need to use synchronized config structure.
299+
*
300+
* @return bool
301+
* @throws \Magento\MagentoCloud\Config\ConfigException
302+
*/
303+
private function isSynchronizedConfigStructure(): bool
304+
{
305+
$model = (string)$this->stageConfig->get(DeployInterface::VAR_CACHE_REDIS_BACKEND);
306+
return $model === self::REDIS_BACKEND_REMOTE_SYNHRONIZED_CACHE;
307+
}
203308
}

src/Config/Stage/DeployInterface.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ interface DeployInterface extends StageConfigInterface
1919
public const VAR_QUEUE_CONFIGURATION = 'QUEUE_CONFIGURATION';
2020
public const VAR_SEARCH_CONFIGURATION = 'SEARCH_CONFIGURATION';
2121
public const VAR_ELASTICSUITE_CONFIGURATION = 'ELASTICSUITE_CONFIGURATION';
22+
public const VAR_CACHE_REDIS_BACKEND = 'REDIS_BACKEND';
2223
public const VAR_CACHE_CONFIGURATION = 'CACHE_CONFIGURATION';
2324
public const VAR_SESSION_CONFIGURATION = 'SESSION_CONFIGURATION';
2425
public const VAR_DATABASE_CONFIGURATION = 'DATABASE_CONFIGURATION';

src/Step/Deploy/PreDeploy/CleanRedisCache.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,16 @@ public function execute(): void
6363
}
6464

6565
foreach ($cacheConfigs['frontend'] as $cacheType => $cacheConfig) {
66-
if ($cacheConfig['backend'] !== 'Cm_Cache_Backend_Redis') {
66+
$backend = stripslashes($cacheConfig['backend']);
67+
68+
if (!in_array($backend, CacheConfig::AVAILABLE_REDIS_BACKEND, true)) {
6769
continue;
6870
}
6971

70-
$redisConfig = $cacheConfig['backend_options'];
72+
$redisConfig = ($backend === CacheConfig::REDIS_BACKEND_REMOTE_SYNHRONIZED_CACHE)
73+
? $cacheConfig['backend_options']['remote_backend_options']
74+
: $cacheConfig['backend_options'];
75+
7176
$this->logger->info('Clearing redis cache: ' . $cacheType);
7277

7378
$client = $this->credisFactory->create(

0 commit comments

Comments
 (0)