Feature Request #723
Cache should allow memcache multi_get for performance
| Status: | Closed | Start date: | ||
|---|---|---|---|---|
| Priority: | Low | Due date: | ||
| Assignee: | % Done: | 100% |
||
| Category: | Libraries:Cache | |||
| Target version: | 2.4 | |||
| Resolution: | fixed | Points: |
Description
The get($id) method of the Cache library breaks when passed an array of keys. ($this->driver->get($id) returns an array, which errors out since if (substr($data, 0, 14) === '<{serialized}>') expects a string).
Memcache::get performance is enhanced when passed an array rather than multiple individual calls, as the requests can be made in parallel and often spread across multiple memcache servers.
Related issues
Associated revisions
refs #723 - "New" cache library. More drivers to come..
refs #723 - More updates and fixes.
History
Updated by Hans - over 3 years ago
The get() and set() methods of the Cache library and its related drivers should be refactored so that serialization and unserialization takes place within the drivers when necessary. In the case of the memcache driver, serialization and unserialization would never be necessary since anything other than a string or int is automatically serialized and unserialized by the native Memcache methods. (This refactoring would also fix the issue described above with passing in an array.)
Updated by Mark van der Velden - about 3 years ago
Geert and I discussed this during a Bughuntday meeting (The topic was Zend Framework, but Geert and I made it our own Kohana meeting ;-) ), the previously attached patch is partially the feature-request. It allows cache back ends to do their own serializing, however it does not fix the array parameter getting of data. Since this forces some active design changes.
e.g.:
The Id's are translated from / or \\ to = to keep valid key names, so returning a associative array might be inconsistent:
<pre>
$cache->set('a/a', range(0,10));
$cache->set('b/b', range('a','z'));
var_dump( $cache->get(array('a/a','b/b')) );
// Return a-1, with the current translated id's
array(
'a=a' => array(0,10),
'b=b' => array('a','z')
)
// Return a-2, with the untranslated id's
array(
'a/a' => array(0,10),
'b/b' => array('a','z')
)
// Return n
array(
0 => array(0,10),
1 => array('a','z')
)
</pre>
Return A-2, would be most favorable (for obvious reasons) and doable of the original id's will remain available when returning data. However it leads to a inconsistent return value when called with a single id.
Proposal to introduce a new method e.g.: getMultiple and possibly a counter part: setMultiple rather then changing the current set and get
Updated by Geert De Deckere about 3 years ago
Memcache driver now takes care of seriliazing stuff itself, r3652.
Updated by Kiall Mac Innes almost 3 years ago
- Assignee deleted (
Redmine Admin)
Updated by Jeremy Bush over 2 years ago
- Assignee set to Chris Bandy
- Priority changed from High to Low
- 11 set to 2.3.4
Updated by Jeremy Bush over 2 years ago
- Status changed from New to Review
Updated by Kiall Mac Innes over 2 years ago
- Tracker changed from Bug Report to Feature Request
Updated by Kiall Mac Innes over 2 years ago
- Status changed from Review to Assigned
- Assignee changed from Chris Bandy to Kiall Mac Innes
Updated by Kiall Mac Innes over 2 years ago
$data = Cache::instance()->get(array('key1','key2'));
var_dump($data)
outputs:
array(2) { ["key1"]=> string(6) "value1" ["key2"]=> string(6) "value2" }
I believe this already works?
Additionally, the memcache ext (note: not the memcacheD ext) does not support multi set.
There is nothing preventing a call like so being handled by the drivers supporting multi set:
Cache::instance()->set(array('key1','key2'),array('value1','value2'));
Thoughts?
Updated by Kiall Mac Innes over 2 years ago
Scrap that last comment.
I'm adding 2 methods to the Cache library:
get_multi(array('key1','key2','key_that_doesnt_exist');
which returns:
array('key1'=>'value1','key2'=>'value2','key_that_doesnt_exist'=>NULL)
and the second method
set_multi(array('key1'=>'value1','key2'=>'value2'), $tags, $lifetime);
which returns TRUE if all items were set correctly, or FALSE if a single item fails.
Drivers are free to override these methods to take advantage of any driver specific methods for better performance than a simple foreach()
Updated by Kiall Mac Innes over 2 years ago
- File new-cache-file-driver.png added
- File new-cache-memcache-driver.png added
Scrap that last comment (again).
I've spent some time on the Cache lib trying to clean it up so we can A) support multi get/set on all the drivers consistently... and B) support tags on all drivers (including memcache)
Some
Updated by Kiall Mac Innes over 2 years ago
...Some benchmarks are attached, I'm not sure how accurate they are - the memcache results are too good...
I've still got to work out the details for layering a tags supporting driver over a tag un-supporting driver... It will be slow(er) if you use tags - but the same if you dont... and at least the option will be there!
Updated by Kiall Mac Innes over 2 years ago
- File memcache-vs-memcached-10k.png added
- File Cache_Bench.php added
- File Cache_Bench2.php added
BTW those first 2 benchmarks are based on 10k iterations.
This new memcache one is based on 100k iterations - the results are proportionally the same as for 10k iterations.
I also wrote up a memcacheD based driver and compared the new memcache driver to this one - memcacheD was slower for everything bar multi set. Results for this attached as memcache-vs-memcached-10k where "new" is memcacheD and "old" is memcache. Code for this test is attached as Cache_Bench2.php
Code for the other tests attched as Cache_Bench.php
Theres plenty more scenarios that could be benchmarked -
Updated by Kiall Mac Innes over 2 years ago
- File new-cache-memcache-driver-100k.png added
forgot to attach this ;) .. Your Inbox's must be hating me right now! Sorry!
Updated by Willem Mulder over 2 years ago
I noticed a bug (?) in the root / branches / 2.4 / system / libraries / Cache.php
on line 159 in the delete_tag function, where I guess this "return $this->driver->delete($tags);" should be "return $this->driver->delete_tag($tags);"
Also, a discussion on implementing tags on memcache/xcache etc can be found here:
http://framework.zend.com/issues/browse/ZF-4253
The bottom line is that one cache location stores all tags and their related keys. This 'mastertag' should be implemented in File Cache and should never expire so that you won't lose it somehow.
Updated by Kiall Mac Innes over 2 years ago
we actually had something like that until a commit I made about 30 seconds ago :)
Using a single cache item to store the tags is incredibly prone to race conditions... if any 2 requests change the tags at the same time, one of them wins... but I'll read that and see if they come up with a way around it anyway ;)
That being said... the file cache driver suffers the same problem, to a lesser extent...
Updated by Kiall Mac Innes over 2 years ago
Well - that ZF suggestion is identical to what I just removed ;)
one of the comments suggests something slightly better... but still prone to race conditions if two requests use the same tag at the same time...
Personally - I use the memcache patch for tags support... the syntax is WOEFUL but it works...
Updated by Willem Mulder over 2 years ago
Kiall Mac Innes wrote:
we actually had something like that until a commit I made about 30 seconds ago :)
Using a single cache item to store the tags is incredibly prone to race conditions... if any 2 requests change the tags at the same time, one of them wins... but I'll read that and see if they come up with a way around it anyway ;)
That being said... the file cache driver suffers the same problem, to a lesser extent...
The line is still the same in http://dev.kohanaphp.com/projects/kohana2/repository/revisions/4605/entry/branches/2.4/system/libraries/Cache.php? Or is it not supposed to change right away? Still noob with trac/versioning systems etc ;)
About the race condition... Could it not be solved with some exclusion flag?
Updated by Willem Mulder over 2 years ago
The exclusion could be with something like the bakery algorithm
num[i] = 0 ; choosing[i] = False
concurrentbegin
P1 P2 P3 ... ... Pn
concurrentend
Pi: ...
choosing[i] = True // Entry: take ticket
mn = 0 // unshared variable
for (j=1 ; j <= n ; j++)
mn = max (mn , num[j] )
num[i] = mn+1
choosing[i] = False
for (j=1 ; j <= n ; j++) // Entry: Waiting for turn
{ while choosing[j] { }
while ( num[j] > 0 and
(num[j],j) < (num[i],i) ) { }
}
Critical section (updating cache)
num[i] = 0
The only problem being how to implement the to be shared choosing[] and num[] arrays and how to know what processnumber (i) you have... Which probably makes the race condition impossible to solve...
Updated by Kiall Mac Innes over 2 years ago
- Status changed from Assigned to Closed
- % Done changed from 0 to 100
- Resolution set to fixed
Yea - I'm 99.9% sure the race condition is impossible to solve completely - Personally, I'm not happy leaving massive race conditions in Kohana - hence, I've removed the flawed support that was in 2.3.X.
Lets move this tags chat to #2109 as its unrelated to this ticket!
Also - That URL is pointing at r4605 of that file, and not the latest revision - r4607 :) (use those URLs in tickets since there essentially permalink's).
Updated by Willem Mulder over 2 years ago
ah, so i was browsing an early version... Should have checked that out. Thanks for fixing :)
I will further reply about the tags problem in the other issue!