2009
09.24

On a recent Symfony 1.0 project I’ve been working on, I noticed that there were quite a few pages that heavily made use of a particular database table that basically held a set of data that would rarely change. I thought it would be nice to implement a second level cache for  these objects at the ORM level,  I used to use a similar technique with Hibernate and EHCache on my Java projects.

I started poking around the Propel documentation, looking into what features they had to offer in this area and came across a few forum posts requesting that sort of second level cache functionality, but it looked like those features were not going to be implemented until a later release, not going to do me much good for my current project.  But I found a way, and it turned out to be pretty simple.  enter memcache function caching.

First you need to install the sfMemcache plugin

You will also need to install the memcache php pecl extension in your php / apache setup as well as memcached on your server.  If you don’t run memcached, then the memcache plugin will default to using Symfony file based caching, but you will see php warnings unless you suppress them.


The sfMemcache plugin has a feature called a function cache which will call a function for you, and cache the results of the function call.  The id for each cache is calculated from the function arguments, which will fit in nicely with what we want to do for caching object queries, because our criteria object will contain different parameters for the separate types of queries accross our application.

Once the requisite plugins & extensions are installed on your server, it is trivial to get object caching enabled for your model entities.  Let’s say I have a model object named StoreRegion.  These regions rarely change, but get looked up quite frequently.  I’ve decided to enable caching for these particular model objects.  In StoreRegionPeer I would simply override two methods:

class StoreRegionPeer extends BaseStoreRegionPeer {
 
    public static function doSelectOne(Criteria $criteria, $con = null){
        $fc = new sfMemcacheFunctionCache();
        $items = $fc->call ('StoreRegionPeer::doSelectOne', $criteria);
        return $items;
    }
 
    public static function doSelect(Criteria $criteria, $con = null){
        $fc = new sfMemcacheFunctionCache();
        $items = $fc->call ('StoreRegionPeer::doSelect', $criteria);
        return $items;
     }
}

And that’s it! As our application begins doing StoreRegion lookups, the results will begin caching. sfMemcache uses php serialization to store a serialized versions of the StoreRegion objects in memcached, when we request further StoreRegion objects the Peer class will skip the database lookup and object hydration step. We could also over ride the save method to clear the cache when an instance of StoreRegion is updated.

2 comments so far

Add Your Comment
  1. This looks great but how does the function cache update when the query results change? Is this based on a timeout setting?

    • Nate, sorry I missed your comment for a couple months.

      Basically to update the cache when the query results change, I’m assuming you mean, when a record is added / removed from the database that would match a set of query results.

      In the above example what you would do is hook into when an object is saved or deleted at which point you clear the memcache bucket and on next request, the results will be re-cached / updated.

      On the other hand if you were referring how the cache is updated if the doSelect method is called with differing criteria, sfmemcache will cache the results based on the criteria passed to the function. So for instance it will cache two sets of query results for if you were to pass two different criteria objects.