Skip to content
This repository was archived by the owner on Apr 29, 2019. It is now read-only.

Commit bc35ff1

Browse files
slopukhovduhon
authored andcommitted
MAGETWO-87611: Cache storage separation support
1 parent 3aa0a16 commit bc35ff1

File tree

5 files changed

+598
-8
lines changed

5 files changed

+598
-8
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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\Frontend;
8+
9+
use Magento\TestFramework\Helper\Bootstrap;
10+
11+
class FactoryTest extends \PHPUnit\Framework\TestCase
12+
{
13+
/**
14+
* Object Manager
15+
*
16+
* @var \Magento\Framework\ObjectManagerInterface
17+
*/
18+
private $objectManager;
19+
20+
/**
21+
* @var \Magento\Framework\App\Cache\Frontend\Factory
22+
*/
23+
private $factory;
24+
25+
/**
26+
* @var \Magento\Framework\App\Area
27+
*/
28+
private $model;
29+
30+
protected function setUp()
31+
{
32+
$this->objectManager = Bootstrap::getObjectManager();
33+
$this->factory = $this->objectManager->create(
34+
\Magento\Framework\App\Cache\Frontend\Factory::class
35+
);
36+
}
37+
38+
/**
39+
* Check RemoteSynchronizedCache
40+
* Removing any cache item in the RemoteSynchronizedCache must invalidate all cache items
41+
*
42+
* @magentoAppIsolation enabled
43+
* @magentoDbIsolation enabled
44+
*/
45+
public function testRemoteSynchronizedCache()
46+
{
47+
$data = 'data';
48+
$identifier = 'identifier';
49+
$secondIdentifier = 'secondIdentifier';
50+
$secondData = 'secondData';
51+
52+
$frontendOptions = ['backend' => 'remote_synchronized_cache'];
53+
$this->model = $this->factory->create($frontendOptions);
54+
55+
//Saving data
56+
$this->assertTrue($this->model->save($data, $identifier));
57+
$this->assertTrue($this->model->save($secondData, $secondIdentifier));
58+
59+
//Checking data
60+
$this->assertEquals($this->model->load($identifier), $data);
61+
$this->assertEquals($this->model->load($secondIdentifier), $secondData);
62+
63+
//Removing data
64+
sleep(2);
65+
$this->assertTrue($this->model->remove($secondIdentifier));
66+
$this->assertEquals($this->model->load($identifier), false);
67+
$this->assertEquals($this->model->load($secondIdentifier), false);
68+
69+
//Saving data
70+
$this->assertTrue($this->model->save($data, $identifier));
71+
$this->assertTrue($this->model->save($secondData, $secondIdentifier));
72+
73+
//Checking data
74+
$this->assertEquals($this->model->load($identifier), $data);
75+
$this->assertEquals($this->model->load($secondIdentifier), $secondData);
76+
77+
//Removing data
78+
sleep(2);
79+
$this->assertTrue($this->model->remove($identifier));
80+
$this->assertEquals($this->model->load($identifier), false);
81+
$this->assertEquals($this->model->load($secondIdentifier), false);
82+
}
83+
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,15 @@ protected function _getBackendOptions(array $cacheOptions)
266266
$backendType = \Magento\Framework\Cache\Backend\Database::class;
267267
$options = $this->_getDbAdapterOptions();
268268
break;
269+
case 'remote_synchronized_cache':
270+
$backendType = \Magento\Framework\Cache\Backend\RemoteSynchronizedCache::class;
271+
$options['remote_backend'] = \Magento\Framework\Cache\Backend\Database::class;
272+
$options['remote_backend_options'] = $this->_getDbAdapterOptions();
273+
$options['local_backend'] = \Cm_Cache_Backend_File::class;
274+
$cacheDir = $this->_filesystem->getDirectoryWrite(DirectoryList::CACHE);
275+
$options['local_backend_options']['cache_dir'] = $cacheDir->getAbsolutePath();
276+
$cacheDir->create();
277+
break;
269278
default:
270279
if ($type != $this->_defaultBackend) {
271280
try {

lib/internal/Magento/Framework/Cache/Backend/Database.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -216,16 +216,15 @@ public function save($data, $id, $tags = [], $specificLifetime = false)
216216
$time = time();
217217
$expire = $lifetime === 0 || $lifetime === null ? 0 : $time + $lifetime;
218218

219+
$idCol = $connection->quoteIdentifier('id');
219220
$dataCol = $connection->quoteIdentifier('data');
221+
$createCol = $connection->quoteIdentifier('create_time');
222+
$updateCol = $connection->quoteIdentifier('update_time');
220223
$expireCol = $connection->quoteIdentifier('expire_time');
221-
$query = "INSERT INTO {$dataTable} (\n {$connection->quoteIdentifier(
222-
'id'
223-
)},\n {$dataCol},\n {$connection->quoteIdentifier(
224-
'create_time'
225-
)},\n {$connection->quoteIdentifier(
226-
'update_time'
227-
)},\n {$expireCol})\n VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE\n
228-
{$dataCol}=VALUES({$dataCol}),\n {$expireCol}=VALUES({$expireCol})";
224+
225+
$query = "INSERT INTO {$dataTable} ({$idCol}, {$dataCol}, {$createCol}, {$updateCol}, {$expireCol}) " .
226+
"VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE {$dataCol}=VALUES({$dataCol}), " .
227+
"{$updateCol}=VALUES({$updateCol}), {$expireCol}=VALUES({$expireCol})";
229228

230229
$result = $connection->query($query, [$id, $data, $time, $time, $expire])->rowCount();
231230
}
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Framework\Cache\Backend;
8+
9+
/**
10+
* Remote synchronized cache
11+
*
12+
* This class created for correct work local caches with multiple web nodes,
13+
* that will be check cache status from remote cache
14+
*/
15+
class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache_Backend_ExtendedInterface
16+
{
17+
/**
18+
* Local backend cache adapter
19+
*
20+
* @var \Zend_Cache_Backend_ExtendedInterface
21+
*/
22+
private $local;
23+
24+
/**
25+
* Remote backend cache adapter
26+
*
27+
* @var \Zend_Cache_Backend_ExtendedInterface
28+
*/
29+
private $remote;
30+
31+
/**
32+
* Cache invalidation time
33+
*
34+
* @var \Zend_Cache_Backend_ExtendedInterface
35+
*/
36+
protected $cacheInvalidationTime;
37+
38+
/**
39+
* {@inheritdoc}
40+
*/
41+
protected $_options = [
42+
'remote_backend' => '',
43+
'remote_backend_invalidation_time_id' => 'default_remote_backend_invalidation_time',
44+
'remote_backend_custom_naming' => true,
45+
'remote_backend_autoload' => true,
46+
'remote_backend_options' => [],
47+
'local_backend' => '',
48+
'local_backend_options' => [],
49+
'local_backend_custom_naming' => true,
50+
'local_backend_autoload' => true
51+
];
52+
53+
/**
54+
* @param array $options
55+
*/
56+
public function __construct(array $options = [])
57+
{
58+
parent::__construct($options);
59+
60+
$universalOptions = array_diff_key($options, $this->_options);
61+
62+
if ($this->_options['remote_backend'] === null) {
63+
\Zend_Cache::throwException('remote_backend option must be set');
64+
} elseif ($this->_options['remote_backend'] instanceof \Zend_Cache_Backend_ExtendedInterface) {
65+
$this->remote = $this->_options['remote_backend'];
66+
} else {
67+
$this->remote = \Zend_Cache::_makeBackend(
68+
$this->_options['remote_backend'],
69+
array_merge($universalOptions, $this->_options['remote_backend_options']),
70+
$this->_options['remote_backend_custom_naming'],
71+
$this->_options['remote_backend_autoload']
72+
);
73+
if (!($this->remote instanceof \Zend_Cache_Backend_ExtendedInterface)) {
74+
\Zend_Cache::throwException(
75+
'remote_backend must implement the Zend_Cache_Backend_ExtendedInterface interface'
76+
);
77+
}
78+
}
79+
80+
if ($this->_options['local_backend'] === null) {
81+
\Zend_Cache::throwException('local_backend option must be set');
82+
} elseif ($this->_options['local_backend'] instanceof \Zend_Cache_Backend_ExtendedInterface) {
83+
$this->local = $this->_options['local_backend'];
84+
} else {
85+
$this->local = \Zend_Cache::_makeBackend(
86+
$this->_options['local_backend'],
87+
array_merge($universalOptions, $this->_options['local_backend_options']),
88+
$this->_options['local_backend_custom_naming'],
89+
$this->_options['local_backend_autoload']
90+
);
91+
if (!($this->local instanceof \Zend_Cache_Backend_ExtendedInterface)) {
92+
\Zend_Cache::throwException(
93+
'local_backend must implement the Zend_Cache_Backend_ExtendedInterface interface'
94+
);
95+
}
96+
}
97+
}
98+
99+
/**
100+
* Update remote cache status info
101+
*
102+
* @return void
103+
*/
104+
private function updateRemoteCacheStatusInfo()
105+
{
106+
$this->remote->save(time(), $this->_options['remote_backend_invalidation_time_id'], [], null);
107+
$this->cacheInvalidationTime = null;
108+
}
109+
110+
/**
111+
* {@inheritdoc}
112+
*/
113+
public function setDirectives($directives)
114+
{
115+
return $this->local->setDirectives($directives);
116+
}
117+
118+
/**
119+
* {@inheritdoc}
120+
*/
121+
public function load($id, $doNotTestCacheValidity = false)
122+
{
123+
$dataModificationTime = $this->local->test($id);
124+
if ($this->cacheInvalidationTime === null) {
125+
$this->cacheInvalidationTime = $this->remote->load($this->_options['remote_backend_invalidation_time_id']);
126+
}
127+
if ($dataModificationTime >= $this->cacheInvalidationTime) {
128+
return $this->local->load($id, $doNotTestCacheValidity);
129+
} else {
130+
return false;
131+
}
132+
}
133+
134+
/**
135+
* {@inheritdoc}
136+
*/
137+
public function test($id)
138+
{
139+
return $this->local->test($id);
140+
}
141+
142+
/**
143+
* {@inheritdoc}
144+
*/
145+
public function save($data, $id, $tags = [], $specificLifetime = false)
146+
{
147+
return $this->local->save($data, $id, $tags, $specificLifetime);
148+
}
149+
150+
/**
151+
* {@inheritdoc}
152+
*/
153+
public function remove($id)
154+
{
155+
$this->updateRemoteCacheStatusInfo();
156+
return $this->local->remove($id);
157+
}
158+
159+
/**
160+
* {@inheritdoc}
161+
*/
162+
public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, $tags = [])
163+
{
164+
$this->updateRemoteCacheStatusInfo();
165+
return $this->local->clean($mode, $tags);
166+
}
167+
168+
/**
169+
* {@inheritdoc}
170+
*/
171+
public function getIds()
172+
{
173+
return $this->local->getIds();
174+
}
175+
176+
/**
177+
* {@inheritdoc}
178+
*/
179+
public function getTags()
180+
{
181+
return $this->local->getTags();
182+
}
183+
184+
/**
185+
* {@inheritdoc}
186+
*/
187+
public function getIdsMatchingTags($tags = [])
188+
{
189+
return $this->local->getIdsMatchingTags($tags);
190+
}
191+
192+
/**
193+
* {@inheritdoc}
194+
*/
195+
public function getIdsNotMatchingTags($tags = [])
196+
{
197+
return $this->local->getIdsNotMatchingTags($tags);
198+
}
199+
200+
/**
201+
* {@inheritdoc}
202+
*/
203+
public function getIdsMatchingAnyTags($tags = [])
204+
{
205+
return $this->local->getIdsMatchingAnyTags($tags);
206+
}
207+
208+
/**
209+
* {@inheritdoc}
210+
*/
211+
public function getFillingPercentage()
212+
{
213+
return $this->local->getFillingPercentage();
214+
}
215+
216+
/**
217+
* {@inheritdoc}
218+
*/
219+
public function getMetadatas($id)
220+
{
221+
return $this->local->getMetadatas($id);
222+
}
223+
224+
/**
225+
* {@inheritdoc}
226+
*/
227+
public function touch($id, $extraLifetime)
228+
{
229+
return $this->local->touch($id, $extraLifetime);
230+
}
231+
232+
/**
233+
* {@inheritdoc}
234+
*/
235+
public function getCapabilities()
236+
{
237+
return $this->local->getCapabilities();
238+
}
239+
}

0 commit comments

Comments
 (0)