Skip to content

Commit 705f821

Browse files
committed
MC-41192: Fatal error after stopping Redis server with configured Redis page caching
1 parent 81672a2 commit 705f821

File tree

2 files changed

+122
-50
lines changed
  • dev/tests/integration/testsuite/Magento/Framework/App/Cache/Frontend
  • lib/internal/Magento/Framework/App/Cache/Frontend

2 files changed

+122
-50
lines changed

dev/tests/integration/testsuite/Magento/Framework/App/Cache/Frontend/FactoryTest.php

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,43 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
declare(strict_types=1);
67

78
namespace Magento\Framework\App\Cache\Frontend;
89

10+
use Magento\Framework\App\Area;
11+
use Magento\Framework\Cache\Backend\Redis;
12+
use Magento\Framework\Cache\FrontendInterface;
13+
use Magento\Framework\ObjectManagerInterface;
914
use Magento\TestFramework\Helper\Bootstrap;
15+
use PHPUnit\Framework\TestCase;
1016

11-
class FactoryTest extends \PHPUnit\Framework\TestCase
17+
class FactoryTest extends TestCase
1218
{
1319
/**
1420
* Object Manager
1521
*
16-
* @var \Magento\Framework\ObjectManagerInterface
22+
* @var ObjectManagerInterface
1723
*/
1824
private $objectManager;
1925

2026
/**
21-
* @var \Magento\Framework\App\Cache\Frontend\Factory
27+
* @var Factory
2228
*/
2329
private $factory;
2430

2531
/**
26-
* @var \Magento\Framework\App\Area
32+
* @var Area
2733
*/
2834
private $model;
2935

36+
/**
37+
* @ingeritdoc
38+
*/
3039
protected function setUp(): void
3140
{
3241
$this->objectManager = Bootstrap::getObjectManager();
33-
$this->factory = $this->objectManager->create(
34-
\Magento\Framework\App\Cache\Frontend\Factory::class
35-
);
42+
$this->factory = $this->objectManager->create(Factory::class);
3643
}
3744

3845
/**
@@ -75,4 +82,22 @@ public function testRemoteSynchronizedCache()
7582
$this->assertEquals($this->model->load($identifier), $data);
7683
$this->assertEquals($this->model->load($secondIdentifier), $secondData);
7784
}
85+
86+
/**
87+
* Verify factory will create cache frontend instance with default options in case Redis is not available.
88+
*
89+
* @return void
90+
*/
91+
public function testCreateCacheFrontedInstanceWithFallbackToDefaultOptions(): void
92+
{
93+
$options = [
94+
'backend_options' => [
95+
'server' => null,
96+
],
97+
'id_prefix' => 'test_prefix',
98+
'backend' => Redis::class,
99+
];
100+
101+
self::assertInstanceOf(FrontendInterface::class, $this->factory->create($options));
102+
}
78103
}

lib/internal/Magento/Framework/App/Cache/Frontend/Factory.php

Lines changed: 90 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,22 @@
99
*/
1010
namespace Magento\Framework\App\Cache\Frontend;
1111

12+
use Cm_Cache_Backend_File;
13+
use Exception;
14+
use LogicException;
1215
use Magento\Framework\App\Filesystem\DirectoryList;
16+
use Magento\Framework\App\ResourceConnection;
17+
use Magento\Framework\Cache\Backend\Database;
18+
use Magento\Framework\Cache\Backend\Eaccelerator;
19+
use Magento\Framework\Cache\Backend\RemoteSynchronizedCache;
20+
use Magento\Framework\Cache\Core;
21+
use Magento\Framework\Cache\Frontend\Adapter\Zend;
22+
use Magento\Framework\Cache\FrontendInterface;
1323
use Magento\Framework\Filesystem;
24+
use Magento\Framework\ObjectManagerInterface;
25+
use Magento\Framework\Profiler;
26+
use UnexpectedValueException;
27+
use Zend_Cache;
1428

1529
/**
1630
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -28,7 +42,7 @@ class Factory
2842
const PARAM_CACHE_FORCED_OPTIONS = 'cache_options';
2943

3044
/**
31-
* @var \Magento\Framework\ObjectManagerInterface
45+
* @var ObjectManagerInterface
3246
*/
3347
private $_objectManager;
3448

@@ -75,21 +89,21 @@ class Factory
7589
/**
7690
* Resource
7791
*
78-
* @var \Magento\Framework\App\ResourceConnection
92+
* @var ResourceConnection
7993
*/
8094
protected $_resource;
8195

8296
/**
83-
* @param \Magento\Framework\ObjectManagerInterface $objectManager
97+
* @param ObjectManagerInterface $objectManager
8498
* @param Filesystem $filesystem
85-
* @param \Magento\Framework\App\ResourceConnection $resource
99+
* @param ResourceConnection $resource
86100
* @param array $enforcedOptions
87101
* @param array $decorators
88102
*/
89103
public function __construct(
90-
\Magento\Framework\ObjectManagerInterface $objectManager,
104+
ObjectManagerInterface $objectManager,
91105
Filesystem $filesystem,
92-
\Magento\Framework\App\ResourceConnection $resource,
106+
ResourceConnection $resource,
93107
array $enforcedOptions = [],
94108
array $decorators = []
95109
) {
@@ -104,7 +118,7 @@ public function __construct(
104118
* Return newly created cache frontend instance
105119
*
106120
* @param array $options
107-
* @return \Magento\Framework\Cache\FrontendInterface
121+
* @return FrontendInterface
108122
*/
109123
public function create(array $options)
110124
{
@@ -139,29 +153,32 @@ public function create(array $options)
139153
'frontend_type' => $frontend['type'],
140154
'backend_type' => $backend['type'],
141155
];
142-
\Magento\Framework\Profiler::start('cache_frontend_create', $profilerTags);
156+
Profiler::start('cache_frontend_create', $profilerTags);
143157

144-
/** @var $result \Magento\Framework\Cache\Frontend\Adapter\Zend */
145-
$result = $this->_objectManager->create(
146-
\Magento\Framework\Cache\Frontend\Adapter\Zend::class,
147-
[
148-
'frontendFactory' => function () use ($frontend, $backend) {
149-
return \Zend_Cache::factory(
150-
$frontend['type'],
151-
$backend['type'],
152-
$frontend,
153-
$backend['options'],
154-
true,
155-
true,
156-
true
157-
);
158-
}
159-
]
160-
);
158+
try {
159+
$result = $this->_objectManager->create(
160+
Zend::class,
161+
[
162+
'frontendFactory' => function () use ($frontend, $backend) {
163+
return Zend_Cache::factory(
164+
$frontend['type'],
165+
$backend['type'],
166+
$frontend,
167+
$backend['options'],
168+
true,
169+
true,
170+
true
171+
);
172+
},
173+
]
174+
);
175+
} catch (\Exception $e) {
176+
$result = $this->createCacheWithDefaultOptions($options);
177+
}
161178
$result = $this->_applyDecorators($result);
162179

163180
// stop profiling
164-
\Magento\Framework\Profiler::stop('cache_frontend_create');
181+
Profiler::stop('cache_frontend_create');
165182
return $result;
166183
}
167184

@@ -179,24 +196,24 @@ private function _getExpandedOptions(array $options)
179196
/**
180197
* Apply decorators to a cache frontend instance and return the topmost one
181198
*
182-
* @param \Magento\Framework\Cache\FrontendInterface $frontend
183-
* @return \Magento\Framework\Cache\FrontendInterface
184-
* @throws \LogicException
185-
* @throws \UnexpectedValueException
199+
* @param FrontendInterface $frontend
200+
* @return FrontendInterface
201+
* @throws LogicException
202+
* @throws UnexpectedValueException
186203
*/
187-
private function _applyDecorators(\Magento\Framework\Cache\FrontendInterface $frontend)
204+
private function _applyDecorators(FrontendInterface $frontend)
188205
{
189206
foreach ($this->_decorators as $decoratorConfig) {
190207
if (!isset($decoratorConfig['class'])) {
191-
throw new \LogicException('Class has to be specified for a cache frontend decorator.');
208+
throw new LogicException('Class has to be specified for a cache frontend decorator.');
192209
}
193210
$decoratorClass = $decoratorConfig['class'];
194211
$decoratorParams = isset($decoratorConfig['parameters']) ? $decoratorConfig['parameters'] : [];
195212
$decoratorParams['frontend'] = $frontend;
196213
// conventionally, 'frontend' argument is a decoration subject
197214
$frontend = $this->_objectManager->create($decoratorClass, $decoratorParams);
198-
if (!$frontend instanceof \Magento\Framework\Cache\FrontendInterface) {
199-
throw new \UnexpectedValueException('Decorator has to implement the cache frontend interface.');
215+
if (!$frontend instanceof FrontendInterface) {
216+
throw new UnexpectedValueException('Decorator has to implement the cache frontend interface.');
200217
}
201218
}
202219
return $frontend;
@@ -258,18 +275,18 @@ protected function _getBackendOptions(array $cacheOptions)
258275
case 'varien_cache_backend_eaccelerator':
259276
if (extension_loaded('eaccelerator') && ini_get('eaccelerator.enable')) {
260277
$enableTwoLevels = true;
261-
$backendType = \Magento\Framework\Cache\Backend\Eaccelerator::class;
278+
$backendType = Eaccelerator::class;
262279
}
263280
break;
264281
case 'database':
265-
$backendType = \Magento\Framework\Cache\Backend\Database::class;
282+
$backendType = Database::class;
266283
$options = $this->_getDbAdapterOptions();
267284
break;
268285
case 'remote_synchronized_cache':
269-
$backendType = \Magento\Framework\Cache\Backend\RemoteSynchronizedCache::class;
270-
$options['remote_backend'] = \Magento\Framework\Cache\Backend\Database::class;
286+
$backendType = RemoteSynchronizedCache::class;
287+
$options['remote_backend'] = Database::class;
271288
$options['remote_backend_options'] = $this->_getDbAdapterOptions();
272-
$options['local_backend'] = \Cm_Cache_Backend_File::class;
289+
$options['local_backend'] = Cm_Cache_Backend_File::class;
273290
$cacheDir = $this->_filesystem->getDirectoryWrite(DirectoryList::CACHE);
274291
$options['local_backend_options']['cache_dir'] = $cacheDir->getAbsolutePath();
275292
$cacheDir->create();
@@ -283,7 +300,7 @@ protected function _getBackendOptions(array $cacheOptions)
283300
$backendType = $type;
284301
}
285302
}
286-
} catch (\Exception $e) {
303+
} catch (Exception $e) {
287304
}
288305
}
289306
}
@@ -358,7 +375,7 @@ protected function _getTwoLevelsBackendOptions($fastOptions, $cacheOptions)
358375
$options['slow_backend_options'] = $this->_backendOptions;
359376
}
360377
if ($options['slow_backend'] == 'database') {
361-
$options['slow_backend'] = \Magento\Framework\Cache\Backend\Database::class;
378+
$options['slow_backend'] = Database::class;
362379
$options['slow_backend_options'] = $this->_getDbAdapterOptions();
363380
if (isset($cacheOptions['slow_backend_store_data'])) {
364381
$options['slow_backend_options']['store_data'] = (bool)$cacheOptions['slow_backend_store_data'];
@@ -392,8 +409,38 @@ protected function _getFrontendOptions(array $cacheOptions)
392409
if (!array_key_exists('automatic_cleaning_factor', $options)) {
393410
$options['automatic_cleaning_factor'] = 0;
394411
}
395-
$options['type'] =
396-
isset($cacheOptions['frontend']) ? $cacheOptions['frontend'] : \Magento\Framework\Cache\Core::class;
412+
$options['type'] = isset($cacheOptions['frontend']) ? $cacheOptions['frontend'] : Core::class;
397413
return $options;
398414
}
415+
416+
/**
417+
* Create frontend cache with default options.
418+
*
419+
* @param array $options
420+
* @return Zend
421+
*/
422+
private function createCacheWithDefaultOptions(array $options): Zend
423+
{
424+
unset($options['backend']);
425+
unset($options['frontend']);
426+
$backend = $this->_getBackendOptions($options);
427+
$frontend = $this->_getFrontendOptions($options);
428+
429+
return $this->_objectManager->create(
430+
Zend::class,
431+
[
432+
'frontendFactory' => function () use ($frontend, $backend) {
433+
return Zend_Cache::factory(
434+
$frontend['type'],
435+
$backend['type'],
436+
$frontend,
437+
$backend['options'],
438+
true,
439+
true,
440+
true
441+
);
442+
},
443+
]
444+
);
445+
}
399446
}

0 commit comments

Comments
 (0)