Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit229e33e

Browse files
[Cache] Add tags based invalidation + RedisTaggedAdapter
1 parent22f7ed7 commit229e33e

12 files changed

+752
-103
lines changed
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\Cache\Adapter;
13+
14+
usePsr\Cache\CacheItemInterface;
15+
useSymfony\Component\Cache\CacheItem;
16+
17+
/**
18+
* @author Nicolas Grekas <p@tchwork.com>
19+
*/
20+
abstractclass AbstractTagAwareAdapterimplements TagAwareAdapterInterface
21+
{
22+
private$adapter;
23+
private$deferred =array();
24+
private$createCacheItem;
25+
private$getTagsByKey;
26+
27+
/**
28+
* Removes tag-invalidated keys and returns the removed ones.
29+
*
30+
* @param array &$keys The keys to filter
31+
*
32+
* @return array The keys removed from $keys
33+
*/
34+
abstractprotectedfunctionfilterInvalidatedKeys(array &$keys);
35+
36+
/**
37+
* Persists tags for cache keys.
38+
*
39+
* @param array $tags The tags for each cache keys as index
40+
*
41+
* @return bool True on success
42+
*/
43+
abstractprotectedfunctiondoSaveTags(array$tags);
44+
45+
publicfunction__construct(AdapterInterface$adapter,$defaultLifetime)
46+
{
47+
$this->adapter =$adapter;
48+
$this->createCacheItem = \Closure::bind(
49+
function ($key)use ($defaultLifetime) {
50+
$item =newCacheItem();
51+
$item->key =$key;
52+
$item->isHit =false;
53+
$item->defaultLifetime =$defaultLifetime;
54+
55+
return$item;
56+
},
57+
null,
58+
CacheItem::class
59+
);
60+
$this->getTagsByKey = \Closure::bind(
61+
function ($deferred) {
62+
$tagsByKey =array();
63+
foreach ($deferredas$key =>$item) {
64+
$tagsByKey[$key] =$item->tags;
65+
}
66+
67+
return$tagsByKey;
68+
},
69+
null,
70+
CacheItem::class
71+
);
72+
}
73+
74+
/**
75+
* {@inheritdoc}
76+
*/
77+
publicfunctionhasItem($key)
78+
{
79+
if ($this->deferred) {
80+
$this->commit();
81+
}
82+
if (!$this->adapter->hasItem($key)) {
83+
returnfalse;
84+
}
85+
$keys =array($key);
86+
87+
return !$this->filterInvalidatedKeys($keys);
88+
}
89+
90+
/**
91+
* {@inheritdoc}
92+
*/
93+
publicfunctiongetItem($key)
94+
{
95+
if ($this->deferred) {
96+
$this->commit();
97+
}
98+
$keys =array($key);
99+
100+
if ($keys =$this->filterInvalidatedKeys($keys)) {
101+
foreach ($this->generateItems(array(),$keys)as$item) {
102+
return$item;
103+
}
104+
}
105+
106+
return$this->adapter->getItem($key);
107+
}
108+
109+
/**
110+
* {@inheritdoc}
111+
*/
112+
publicfunctiongetItems(array$keys =array())
113+
{
114+
if ($this->deferred) {
115+
$this->commit();
116+
}
117+
$invalids =$this->filterInvalidatedKeys($keys);
118+
$items =$this->adapter->getItems($keys);
119+
120+
return$this->generateItems($items,$invalids);
121+
}
122+
123+
/**
124+
* {@inheritdoc}
125+
*/
126+
publicfunctionclear()
127+
{
128+
$this->deferred =array();
129+
130+
return$this->adapter->clear();
131+
}
132+
133+
/**
134+
* {@inheritdoc}
135+
*/
136+
publicfunctiondeleteItem($key)
137+
{
138+
return$this->adapter->deleteItem($key);
139+
}
140+
141+
/**
142+
* {@inheritdoc}
143+
*/
144+
publicfunctiondeleteItems(array$keys)
145+
{
146+
return$this->adapter->deleteItems($keys);
147+
}
148+
149+
/**
150+
* {@inheritdoc}
151+
*/
152+
publicfunctionsave(CacheItemInterface$item)
153+
{
154+
if (!$iteminstanceof CacheItem) {
155+
returnfalse;
156+
}
157+
if ($this->deferred) {
158+
$this->commit();
159+
}
160+
$this->deferred[$item->getKey()] =$item;
161+
162+
return$this->commit();
163+
}
164+
165+
/**
166+
* {@inheritdoc}
167+
*/
168+
publicfunctionsaveDeferred(CacheItemInterface$item)
169+
{
170+
if (!$iteminstanceof CacheItem) {
171+
returnfalse;
172+
}
173+
$this->deferred[$item->getKey()] =$item;
174+
175+
returntrue;
176+
}
177+
178+
/**
179+
* {@inheritdoc}
180+
*/
181+
publicfunctioncommit()
182+
{
183+
$ok =true;
184+
185+
if ($this->deferred) {
186+
foreach ($this->deferredas$key =>$item) {
187+
if (!$this->adapter->saveDeferred($item)) {
188+
unset($this->deferred[$key]);
189+
$ok =false;
190+
}
191+
}
192+
$f =$this->getTagsByKey;
193+
$ok =$this->doSaveTags($f($this->deferred)) &&$ok;
194+
$this->deferred =array();
195+
}
196+
197+
return$this->adapter->commit() &&$ok;
198+
}
199+
200+
publicfunction__destruct()
201+
{
202+
$this->commit();
203+
}
204+
205+
privatefunctiongenerateItems($items,$invalids)
206+
{
207+
foreach ($itemsas$key =>$item) {
208+
yield$key =>$item;
209+
}
210+
211+
$f =$this->createCacheItem;
212+
213+
foreach ($invalidsas$key) {
214+
yield$key =>$f($key);
215+
}
216+
}
217+
}

‎src/Symfony/Component/Cache/Adapter/RedisAdapter.php‎

Lines changed: 3 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
namespaceSymfony\Component\Cache\Adapter;
1313

1414
usePredis\Connection\Factory;
15-
usePredis\Connection\Aggregate\PredisCluster;
16-
usePredis\Connection\Aggregate\RedisCluster;
1715
useSymfony\Component\Cache\Exception\InvalidArgumentException;
1816

1917
/**
@@ -22,29 +20,23 @@
2220
*/
2321
class RedisAdapterextends AbstractAdapter
2422
{
23+
use RedisAdapterTrait;
24+
2525
privatestatic$defaultConnectionOptions =array(
2626
'class' =>null,
2727
'persistent' =>0,
2828
'timeout' =>0,
2929
'read_timeout' =>0,
3030
'retry_interval' =>0,
3131
);
32-
private$redis;
3332

3433
/**
3534
* @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient
3635
*/
3736
publicfunction__construct($redisClient,$namespace ='',$defaultLifetime =0)
3837
{
3938
parent::__construct($namespace,$defaultLifetime);
40-
41-
if (preg_match('#[^-+_.A-Za-z0-9]#',$namespace,$match)) {
42-
thrownewInvalidArgumentException(sprintf('RedisAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.',$match[0]));
43-
}
44-
if (!$redisClientinstanceof \Redis && !$redisClientinstanceof \RedisArray && !$redisClientinstanceof \RedisCluster && !$redisClientinstanceof \Predis\Client) {
45-
thrownewInvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given',__METHOD__,is_object($redisClient) ?get_class($redisClient) :gettype($redisClient)));
46-
}
47-
$this->redis =$redisClient;
39+
$this->setRedis($redisClient,$namespace);
4840
}
4941

5042
/**
@@ -157,51 +149,6 @@ protected function doHave($id)
157149
return (bool)$this->redis->exists($id);
158150
}
159151

160-
/**
161-
* {@inheritdoc}
162-
*/
163-
protectedfunctiondoClear($namespace)
164-
{
165-
// When using a native Redis cluster, clearing the cache cannot work and always returns false.
166-
// Clearing the cache should then be done by any other means (e.g. by restarting the cluster).
167-
168-
$hosts =array($this->redis);
169-
$evalArgs =array(array($namespace),0);
170-
171-
if ($this->redisinstanceof \Predis\Client) {
172-
$evalArgs =array(0,$namespace);
173-
174-
$connection =$this->redis->getConnection();
175-
if ($connectioninstanceof PredisCluster) {
176-
$hosts =array();
177-
foreach ($connectionas$c) {
178-
$hosts[] =new \Predis\Client($c);
179-
}
180-
}elseif ($connectioninstanceof RedisCluster) {
181-
returnfalse;
182-
}
183-
}elseif ($this->redisinstanceof \RedisArray) {
184-
foreach ($this->redis->_hosts()as$host) {
185-
$hosts[] =$this->redis->_instance($host);
186-
}
187-
}elseif ($this->redisinstanceof \RedisCluster) {
188-
returnfalse;
189-
}
190-
foreach ($hostsas$host) {
191-
if (!isset($namespace[0])) {
192-
$host->flushDb();
193-
}else {
194-
// As documented in Redis documentation (http://redis.io/commands/keys) using KEYS
195-
// can hang your server when it is executed against large databases (millions of items).
196-
// Whenever you hit this scale, it is advised to deploy one Redis database per cache pool
197-
// instead of using namespaces, so that FLUSHDB is used instead.
198-
$host->eval("local keys=redis.call('KEYS',ARGV[1]..'*') for i=1,#keys,5000 do redis.call('DEL',unpack(keys,i,math.min(i+4999,#keys))) end",$evalArgs[0],$evalArgs[1]);
199-
}
200-
}
201-
202-
returntrue;
203-
}
204-
205152
/**
206153
* {@inheritdoc}
207154
*/
@@ -248,47 +195,4 @@ protected function doSave(array $values, $lifetime)
248195

249196
return$failed;
250197
}
251-
252-
privatefunctionexecute($command,$id,array$args,$redis =null)
253-
{
254-
array_unshift($args,$id);
255-
call_user_func_array(array($redis ?:$this->redis,$command),$args);
256-
}
257-
258-
privatefunctionpipeline(\Closure$callback)
259-
{
260-
$redis =$this->redis;
261-
262-
try {
263-
if ($redisinstanceof \Predis\Client) {
264-
$redis->pipeline(function ($pipe)use ($callback) {
265-
$this->redis =$pipe;
266-
$callback(array($this,'execute'));
267-
});
268-
}elseif ($redisinstanceof \RedisArray) {
269-
$connections =array();
270-
$callback(function ($command,$id,$args)use (&$connections) {
271-
if (!isset($connections[$h =$this->redis->_target($id)])) {
272-
$connections[$h] =$this->redis->_instance($h);
273-
$connections[$h]->multi(\Redis::PIPELINE);
274-
}
275-
$this->execute($command,$id,$args,$connections[$h]);
276-
});
277-
foreach ($connectionsas$c) {
278-
$c->exec();
279-
}
280-
}else {
281-
$pipe =$redis->multi(\Redis::PIPELINE);
282-
try {
283-
$callback(array($this,'execute'));
284-
}finally {
285-
if ($pipe) {
286-
$redis->exec();
287-
}
288-
}
289-
}
290-
}finally {
291-
$this->redis =$redis;
292-
}
293-
}
294198
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp