PHP Atomic Memcache

memcached, php

Solution

Consider what problem you are trying to solve here. Do you:

- just want to avoid the lost-update problem. (E.g. with a counter that increments by one.)

- or you do need to ensure that while one client has retrieved an item's value, no other client can use that value? (This is where values represent a limited resource.)

Most of the time people just want (1). If this is all you want, you can use check-and-set semantics with `Memcached::cas()`, or if you have a simple integer value you can use the atomic `Memcached::increment()` and `Memcached::decrement()` operations.

If, however, you need to use the key to represent a finite resource (2), consider using a different set of semantics:

$keyname = 'key_with_known_name_representing_finite_resource';

// try to "acquire" the key with add().
// If the key exists already (resource taken), operation will fail
// otherwise, we use it then release it with delete()
// specify a timeout to avoid a deadlock.
// timeout should be <= php's max_execution_time
if ($mcache->add($keyname, '', 60)) {
   // resource acquired
   // ...do stuff....
   // now release
   $mcache->delete($keyname);
} else {
   // try again?
}

If for some reason you do not have access to `cas()`, you can implement a lock using two keys and `add()`/`delete`

$key = 'lockable_key_name';
$lockkey = $key.'##LOCK';

if ($mcache->add($lockkey, '', 60)) { // acquire
    $storedvalue = $mcache->get($key);
    // do something with $storedvalue
    $mcache->set($key, $newvalue);
    // release
    $mcache->delete($lockkey);
}

This approach leads to much more resource contention than you would get with a check-and-set approach.

Problem

Im wondering what is the best way to make my Memcache operations atmoic in my web app. Take the following scenario into consideration: ``` Client 1 connects and retrieves data from key 1 Client 2 connects a few microsecond after Client 1, requests the same data from key 1 Client 1 saves new data to key 1 Client 2 saves new (different data) to key 1, not taking into account that Client 1 modified the value already ``` In this case, there is no atomicity in the process. My (potential) solution is to set, acquire and release locks on keys from within my app. So the above process would work like this after my implementation: ``` Client 1 connects, checks for an active lock on key 1, finds none, and gets the data Client 2 connects a few microsecond after Client 1, requests the same data from key 1, but finds a lock Client 2 enters a retry loop until Client 1 releases the lock Client 1 saves new data to key 1, releases the lock Client 2 gets the fresh data, sets a lock on key 1, and continues ``` Thoughts? Will this method work, and will there be any performance hits I should be wary of?

Original source

Related problems