Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memcached Key Generation From arguments to a function

Tags:

php

memcached

md5

This question is similar to a question asked about java, but i'm doing this in php so i don't think it qualifies as a duplicate.

I would like a way to generate a deterministic key when this function is called. the function should operate like a read through cache. if the key exists, retrieve the data. if not, call the function store the data, then return it.

here's what i have and it works, but im not sure if its safe and if its deterministic enough or even unique enough, since i have absolutely 0 understanding on these topics.

// $call = function being called $args = arguments to that function
// $force = force cache to bypassed, then updated
public function cachedCall($call,$args = [],$force = false)
{
    $cache = \App\App::getInstance()->cache;
    $key = md5($call) . md5(serialize($args));
    $res = $cache->get($key);
    if($res === -1 || $force){
        $res = call_user_func_array([$this,$call],$args);
        if(!empty($res) && $res !== false && $res !== 0 && !is_null($res)){
            $cache->set($key,$res,0); //never set empty data in the cache.
        }
    }
    return $res;
}

My question only pertains to the third line, where the key is calculated. you can see that it is calculated by the called function and the arguments to be supplied to that function. I have had collisions in some instances. I'm looking for ways to improve this so its more useful and the hashes are consistent but not likely to collide.The third argument can be ignored as its simply a way to force the cache to be bypassed.

Examples of how this function is called:

$data = $db->cachedCall('getUserByEmail',[$this->email],true);

$data = $db->cachedCall('getCell',['SELECT id FROM foobar WHERE foo=:bar',[':bar'=>55]]);

If possible, i would like to guarantee the keys have a consistent length at the same time.

like image 228
r3wt Avatar asked Oct 30 '22 07:10

r3wt


1 Answers

This is because the key could be the same in different instances, for example when calling to the method cachedCall have the same arguments. As I image you should share the same memcached server for each instance, and then that is the reason why you have cache collisions.

Demostration

As I read, the variable $call will have a limited values shared with any of the other parts of the code, because will contain a name of a method of the class that contains the method cachedCall, that means it is very easy that two different calls shares this value.

Furthermore, you can call to this method with an empty array of arguments.

So, is very easy to have the same method call in two different instances:

cachedCall('methodX', array()); <- From instance A
cachedCall('methodX', array()); <- From instance B

This will store this content in the same memcached key

Solution

Inside the method, take in account in someway the instance name. For example, you could use the current url as part of the key, or the domain name (depending on your case):

$key = md5($call) . md5(serialize($args)) . md5($_SERVER['HTTP_HOST']);
$key = md5($call) . md5(serialize($args)) . md5($_SERVER['REQUEST_URI']);

Above you can see two examples of how you can change the memcached key depending on your instance.

like image 88
Miguel Avatar answered Nov 15 '22 07:11

Miguel