Recently at work we needed a way to log successful and failed hits to automatically disable web services for a failover system. There are many ways to implement this but for our application's needs and available tools, only two ideas made sense.

  1. MySQL: store these values on a memory table with delayed insertions.
  2. Memcached: implement Zend_Cache_Backend_Memcached ::increment function

I have seen the mysql solution working great for a couple thousands hits every minute but with our application hitting sometimes 10.000 web service calls every minute we became skeptical really fast. The memcached solution has two major problems: first it's memcaheD you don't know if the value you put in cache will actually make it back and secondly the Zend Memcached adapter needs a patch to implement the increment and a custom method to retrieve the counters. The reason increment has never been officially implemented before in ZF1 is because Zend stores values to cache in a custom way alongside the cache date and lifetime.

$result = @$this->_memcache->set($id, array($data, time(), $lifetime), $flag, $lifetime);

Memcached::increment needs the values to be numerical. I can only speculate why ZF1 dev team decided to save all values as array but anyway. I added the following two methods in Zend\Cache\Backend\Memcached.php

/**
 * Memcached::increment
 *
 * @param string $id cache id
 * @param numerical $offset to increase counter
 * @param numerical $initial value to set counter
 * @param int $extraLifetime
 * @return numerical new items value on success or FALSE on failure.
 */
public function increment($id, $offset = 1, $initial = 0, $specificLifetime = false)
{
	$result = $this->_memcache->increment($id, $offset);
	if (!$result) {
			$lifetime = $this->getLifetime($specificLifetime);
			$this->_memcache->add($id, $initial, false, $lifetime);
			$result = $this->_memcache->increment($id, $offset);
	}
	return $result;
}

/**
 * Custom get to retrieve incremental items
 *
 * @param string $id cache id
 * @return numerical|false cached item
 */
public function getCounterKey($id)
{
	$tmp = $this->_memcache->get($id);
	return is_numeric($tmp) ? $tmp : false;
}

The getCounterKey  is pretty straight forward, we wanted to bypass the default load method that tries to access the data from a custom array. The increment is a little more complex because we wanted a thread safe solution. The memcached::increment and memcached::add used like this, is thread safe.

Unfortunately you can't use these methods directly from the Zend_Cache_Core instance, you need a new patch for that to modify the Core class and all the adapters to add this new capability. It's doable but we were satisfied with just using the following snippet to access the Memcached adapter and the two new methods directly.

$cache = Zend_Registry::get('cache')->getBackend();
$cache->increment($key, 1, 0, 0);
$cache->getCounterKey($key);

The solution works like a charm when memcached is working properly but i would love someday to also test the MySQL idea. I would love to hear what solution other people used in similar problems.