memcached PHP semaphore & cache expiration handling

There are a lot of different ways that people use memcached and PHP. The most common of which is probably your basic set and get to cache data from your database.

function get_my_data1() {
    $cache_id = "mykey";
    $data = $memcache_obj->get($cache_id);
    if ( !$data ) {
        $data = get_data_from_db_function();
        $memcache_obj->set($cache_id, $data, $sec_to_cache_for);
    }
    return $data;
}

But what if the query that’s going to hit the database is pretty intensive and you don’t want more than one user to hit the db at a time? That’s easily handling via a semaphore lock.

function get_my_data2() {
    $cache_id = "mykey";
    $data = $memcache_obj->get($cache_id);
    if ( !$data ) {
        // check to see if someone has already set the lock
        $data_lock = $memcache_obj->get($cache_id . '_qry_lock');
        if ( $data_lock ) {
            $lock_counter = 0;
            // loop until you find that the lock has been released.  that implies that the query has finished
            do while ( $data_lock ) {
                // you may only want to wait for a specified period of time.
                // one second is usually sufficient since your goal is to always have sub-second response time
                // if you query takes more than 1 second, you should consider "warming" your cached data via a cron job
                if ( $lock_counter > $max_time_to_wait ) {
                    $lock_failed = true;
                    break;
                }
                // you really want this to be a fraction of a second so the user waits as little as possible
                // for the simplicity of example, I'm using the sleep function.
                sleep(1);
                $data_lock = $memcache_obj->get($cache_id . '_qry_lock');
            }
            // if the loop is completed, that either means the user waited for too long
            // or that the lock has been removed.  try to get the cached data again; it should exist now
            $data = $memcache_obj->get($cache_id);
            if ( $data ) {
                return $data;
            }
        }
        // set a lock for 2 seconds
        $memcache_obj->set($cache_id . '_qry_lock', true, 2);
        $data = get_data_from_db_function();
        $memcache_obj->set($cache_id, $data, $sec_to_cache_for);
        // don't forget to remove the lock
        $memcache_obj->delete($cache_id . '_qry_lock');
    }
    return $data;
}

More below the break –> Read More…

Posted under PHP, Tips & Tricks, Web Development, mysql

This post was written by Michael Tougeron on January 11, 2008

Tags: , ,