Skip to content

Commit becbf3d

Browse files
committed
Merge branch 'fix-cache-lock-cycle-write' into MC-33275-2
# Conflicts: # app/code/Magento/Config/App/Config/Type/System.php # app/code/Magento/Config/etc/di.xml # app/etc/di.xml # lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php
2 parents b4c05a4 + 6dbb941 commit becbf3d

File tree

19 files changed

+1622
-40
lines changed

19 files changed

+1622
-40
lines changed

app/code/Magento/PageCache/etc/di.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@
4040
<type name="Magento\Framework\App\FrontControllerInterface">
4141
<plugin name="page_cache_from_key_from_cookie" type="Magento\PageCache\Plugin\RegisterFormKeyFromCookie" />
4242
</type>
43+
<type name="Magento\Framework\App\Cache\RuntimeStaleCacheStateModifier">
44+
<arguments>
45+
<argument name="cacheTypes" xsi:type="array">
46+
<item name="full_page_cache" xsi:type="const">Magento\PageCache\Model\Cache\Type::TYPE_IDENTIFIER</item>
47+
</argument>
48+
</arguments>
49+
</type>
4350
<preference for="Magento\PageCache\Model\VclGeneratorInterface" type="Magento\PageCache\Model\Varnish\VclGenerator"/>
4451
<preference for="Magento\PageCache\Model\VclTemplateLocatorInterface" type="Magento\PageCache\Model\Varnish\VclTemplateLocator"/>
4552
</config>

dev/tests/integration/framework/Magento/TestFramework/Application.php

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -389,19 +389,6 @@ public function initialize($overriddenParams = [])
389389
);
390390
}
391391
$objectManager->configure($objectManagerConfiguration);
392-
/** Register event observer of Integration Framework */
393-
/** @var \Magento\Framework\Event\Config\Data $eventConfigData */
394-
$eventConfigData = $objectManager->get(\Magento\Framework\Event\Config\Data::class);
395-
$eventConfigData->merge(
396-
[
397-
'core_app_init_current_store_after' => [
398-
'integration_tests' => [
399-
'instance' => \Magento\TestFramework\Event\Magento::class,
400-
'name' => 'integration_tests'
401-
]
402-
]
403-
]
404-
);
405392

406393
if ($this->canLoadArea) {
407394
$this->loadArea(\Magento\TestFramework\Application::DEFAULT_APP_AREA);

dev/tests/integration/testsuite/Magento/Config/App/Config/Type/SystemTest.php

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@
55
*/
66
namespace Magento\Config\App\Config\Type;
77

8+
use Magento\Config\Model\Config\Factory;
9+
use Magento\Framework\Lock\Backend\Cache;
10+
use Magento\Framework\App\Cache\Type\Config;
11+
use Magento\Framework\Cache\FrontendInterface;
12+
use Magento\Framework\Lock\LockManagerInterface;
813
use Magento\Framework\ObjectManagerInterface;
14+
use Magento\Store\Model\ScopeInterface;
915
use Magento\TestFramework\Helper\Bootstrap;
1016

1117
/**
1218
* @magentoDataFixture Magento/Config/_files/config_data.php
1319
* @magentoAppIsolation enabled
20+
* @magentoCache config enabled
1421
*/
1522
class SystemTest extends \PHPUnit\Framework\TestCase
1623
{
@@ -27,24 +34,145 @@ class SystemTest extends \PHPUnit\Framework\TestCase
2734
protected function setUp()
2835
{
2936
$this->objectManager = Bootstrap::getObjectManager();
30-
$this->system = $this->objectManager->create(System::class);
37+
$this->system = $this->createSystemConfig();
3138
}
3239

33-
public function testGetValueDefaultScope()
40+
public function testGetValueForDefaultScope()
3441
{
3542
$this->assertEquals(
3643
'value1.db.default.test',
3744
$this->system->get('default/web/test/test_value_1')
3845
);
46+
}
3947

48+
public function testGetValueForWebsiteScope()
49+
{
4050
$this->assertEquals(
4151
'value1.db.website_base.test',
4252
$this->system->get('websites/base/web/test/test_value_1')
4353
);
54+
}
4455

56+
public function testGetValueForStoreScope()
57+
{
4558
$this->assertEquals(
4659
'value1.db.store_default.test',
4760
$this->system->get('stores/default/web/test/test_value_1')
4861
);
4962
}
63+
64+
public function testCachingDoesNotBreakValueRetrievalForStoreScope()
65+
{
66+
// First uncached call to configuration
67+
$this->createSystemConfig()->get('stores/default/web/test/test_value_1');
68+
69+
// Second call after cache data is stored
70+
$this->assertEquals(
71+
'value1.db.store_default.test',
72+
$this->createSystemConfig()->get('stores/default/web/test/test_value_1')
73+
);
74+
}
75+
76+
public function testCachingDoesNotBreakValueRetrievalForWebsiteScope()
77+
{
78+
// First uncached call to configuration
79+
$this->createSystemConfig()->get('websites/base/web/test/test_value_1');
80+
81+
// Second call after cache data is stored
82+
$this->assertEquals(
83+
'value1.db.website_base.test',
84+
$this->createSystemConfig()->get('websites/base/web/test/test_value_1')
85+
);
86+
}
87+
88+
public function testCachingDoesNotBreakValueRetrievalForDefaultScope()
89+
{
90+
// First uncached call to configuration
91+
$this->createSystemConfig()->get('default/web/test/test_value_1');
92+
93+
// Second call after cache data is stored
94+
$this->assertEquals(
95+
'value1.db.default.test',
96+
$this->createSystemConfig()->get('default/web/test/test_value_1')
97+
);
98+
}
99+
100+
public function testClearingCachePrefixAndLockingItReturnsStaleCachedValue()
101+
{
102+
// First uncached call to configuration
103+
$this->createSystemConfig()->get('default/web/test/test_value_1');
104+
105+
$this->clearConfigCachePrefix();
106+
$this->accessLock()->lock('SYSTEM_CONFIG');
107+
// Second call after cache data is stored
108+
$configValue = $this->createSystemConfig()->get('default/web/test/test_value_1');
109+
$this->accessLock()->unlock('SYSTEM_CONFIG');
110+
111+
$this->assertEquals(
112+
'value1.db.default.test',
113+
$configValue
114+
);
115+
}
116+
117+
public function testClearingConfigurationCacheAndLockingFallsBackToStaleCache()
118+
{
119+
// First uncached call to configuration
120+
$this->createSystemConfig()->get('stores/default/web/test/test_value_1');
121+
122+
$this->accessCacheFrontend()->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [System::CACHE_TAG]);
123+
124+
$this->accessLock()->lock('SYSTEM_CONFIG');
125+
// Second call after cache data is stored
126+
$configValue = $this->createSystemConfig()->get('stores/default/web/test/test_value_1');
127+
$this->accessLock()->unlock('SYSTEM_CONFIG');
128+
129+
$this->assertEquals(
130+
'value1.db.store_default.test',
131+
$configValue
132+
);
133+
}
134+
135+
public function testChangingConfigurationValueRefreshesACache()
136+
{
137+
$systemConfig = $this->objectManager->get(System::class);
138+
139+
// First uncached call to configuration
140+
$systemConfig->get('stores/default/web/test/test_value_1');
141+
142+
/** @var Factory $configFactory */
143+
$configFactory = $this->objectManager->create(Factory::class);
144+
$config = $configFactory->create();
145+
$config->setScope(ScopeInterface::SCOPE_STORES);
146+
$config->setStore('default');
147+
148+
$config->setDataByPath('web/test/test_value_1', 'new_uncached_value');
149+
$config->save();
150+
151+
$this->assertEquals(
152+
'new_uncached_value',
153+
$systemConfig->get('stores/default/web/test/test_value_1')
154+
);
155+
}
156+
157+
private function clearConfigCachePrefix()
158+
{
159+
$cache = $this->accessCacheFrontend();
160+
161+
$cache->remove(System::STALE_CACHE_KEY_FOR_PREFIX);
162+
}
163+
164+
private function accessCacheFrontend(): FrontendInterface
165+
{
166+
return $this->objectManager->get(Config::class);
167+
}
168+
169+
private function createSystemConfig(): System
170+
{
171+
return $this->objectManager->create(System::class);
172+
}
173+
174+
private function accessLock(): LockManagerInterface
175+
{
176+
return $this->objectManager->get(Cache::class);
177+
}
50178
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Framework\App\Cache;
8+
9+
/**
10+
* In memory cache state
11+
*
12+
* Used to ease testing of cache state modifications
13+
*/
14+
class InMemoryState implements StateInterface
15+
{
16+
/** @var bool[] */
17+
private $runtimeState = [];
18+
19+
/** @var bool[] */
20+
private $persistedState = [];
21+
22+
/**
23+
* InMemoryState constructor.
24+
* @param array $persistedState
25+
*/
26+
public function __construct(array $persistedState = [])
27+
{
28+
$this->persistedState = $persistedState;
29+
}
30+
31+
/**
32+
* @inheritDoc
33+
*/
34+
public function isEnabled($cacheType)
35+
{
36+
return $this->runtimeState[$cacheType]
37+
?? $this->persistedState[$cacheType]
38+
?? false;
39+
}
40+
41+
/**
42+
* @inheritDoc
43+
*/
44+
public function setEnabled($cacheType, $isEnabled)
45+
{
46+
$this->runtimeState[$cacheType] = $isEnabled;
47+
}
48+
49+
/**
50+
* @inheritDoc
51+
*/
52+
public function persist()
53+
{
54+
$this->persistedState = $this->runtimeState + $this->persistedState;
55+
$this->runtimeState = [];
56+
}
57+
58+
/**
59+
* Creates new instance with persistent state updated values
60+
*
61+
* @param bool[] $state
62+
* @return self
63+
*/
64+
public function withPersistedState(array $state): self
65+
{
66+
$newState = new self();
67+
$newState->persistedState = $state + $this->persistedState;
68+
return $newState;
69+
}
70+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Framework\App\Cache;
8+
9+
use Magento\Framework\Cache\StaleCacheNotifierInterface;
10+
11+
/**
12+
* Modifier of runtime cache state based on stale data notification from cache loader
13+
*/
14+
class RuntimeStaleCacheStateModifier implements StaleCacheNotifierInterface
15+
{
16+
/** @var StateInterface */
17+
private $cacheState;
18+
19+
/** @var string[] */
20+
private $cacheTypes;
21+
22+
/**
23+
* @param StateInterface $cacheState
24+
* @param string[] $cacheTypes
25+
*/
26+
public function __construct(StateInterface $cacheState, array $cacheTypes = [])
27+
{
28+
$this->cacheState = $cacheState;
29+
$this->cacheTypes = $cacheTypes;
30+
}
31+
32+
/**
33+
* Disabled configures cache types when stale cache was detected in the current request
34+
*/
35+
public function cacheLoaderIsUsingStaleCache()
36+
{
37+
foreach ($this->cacheTypes as $type) {
38+
$this->cacheState->setEnabled($type, false);
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)