Enhance performance with Zend_Cache

1/7/10 Update:  I submitted this post to Zend Developer Zone and they published it! Short Link: http://dz.zend.com/a/11582

So I finally took the Zend Framework plunge a few weeks ago.  No, I didn’t start building an application with the framework, but I did start investigating some of the ZF classes for stand-alone implementation in my existing projects.

The first ZF class to catch my eye was Zend_Cache, for its obvious performance implications.  The applications I develop and manage are very heavy with database transactions.  Hitting the db every time you need an object is a serious performance bottleneck, and on a shared environment can be troubling to other applications living in the same environment.

I’m going to describe my experiences with Zend_Cache here, but I am not going to bore you with lots of code detail and specifics. The above-linked reference page on the ZF site has more than adequate documentation.

Zend_Cache is incredibly flexible, and allows you to use any one of a number of back end caching methods…i.e. the place you want to store your cached data.  Each of these methods is implemented with an adapter class.  Your choices include file-based caching, sqlite, APC, memcached, Xcache, and ZendPlatform.  There are additional adapters to permit caching in two different methods (TwoLevels) and for methods specific to Zend Server.

The enterprise-shared hosting environment my apps live on includes APC, so I went with that adapter.  My dev environment runs Zend Server CE, which includes an APC-compatible Zend caching extension, so I enabled it through the Zend Server config pane.

There are also a few front end adapters to use, which operate the caching machinery.  Zend_Cache_Core is the base adapter and is, in Zend’s words, generic and flexible.  However, for more exotic solutions are available: File, Function, Output and File.

You can cache just about any kind of data that you can store in a variable:  intergers, strings, arrays, serialized stuff, and even objects.  Think about the possible implementations and their performance benefits for just a moment: Object persistence across sessions, recordset caching for speedier searches and paged results, I suppose one could even use this for an alternative session manager.

I already use a vastly simpler file-based caching method for storing serialized arrays for use in various select boxes that occurr frequently across my apps (e.g. “select your department” type stuff).  I may port these over to Zend_Cache later.  Instead, I have implemented Zend_Cache to store objects built from database queries. I am able to manipulate objects across multiple page requests and reduce database requests to only when the object is being modified and saved.

The implementation is extremely simple.  Let me demonstrate how to create a cached object. I will assume we already have an instance of the object we want to cache ($this), and I am not providing a complete class below (so don’t try to cut and paste the code literally).

// we need a method create a unique ID for the object
// this will be used to identify the cached object when created or when retrieving
// I would suggest using a hash that includes your db unique id
public function create_cache_id($id) {
    $cache_id = md5('myRecord_' . $id);
    return $cache_id;
}
 
// this method will retrieve the object
// $id should be unique id of your database record
public function fetch($id) {
 
    $cache_id = $this->create_cache_id($id);
 
    $frontendOptions = array(
        'lifetime' => 300 ,                 // cached object will expire after this many seconds
        /* this ROCKS because you don't spend time writing code to check if the cache is expired */
        'automatic_serialization' => true   // we'll need this on to store the object, also for an array
    );
 
    // note that for the APC adapter there are no back end options to configure
    // instantiate instance of Zend_Cache
 
    $cache = Zend_Cache::factory('Core', 'APC', $frontendOptions);
 
    if(!($obj = $cache->load($this->cache_id)) ) {
 
        /* if a cache with this ID doesn't exist, then execute your db query and build your object, then we'll add it to cache: */
 
        $this->myQuery($id); // this is just a generic method meant to represent your db transation
 
        $cache->save($this); // adds the object to cache
 
        echo 'this is not from cache'; // diagnostic, comment out later
 
    } else {
 
        /* if the cached object does exist, then we need to re-populate our object properties */
 
	// loop through each element of $obj and add as a property to $this
        foreach($obj as $key => $val) {
            $this->$key = $val;
        }
 
        echo 'this is from cache';  // diagnostic, comment out later
 
    }
 
}

I’ve grossly oversimplified the external parts of the process such as the parent object class and the db interactions, but this should still give you the idea. Any time you generate your object, you’ll automatically check the cache first before querying the database.

You’re also going to want a method to clear the cache if your object changes. For example, if your user edits a record using an input form, you’re going to want to update the db and the cache. Rather than immediately re-loading the object into cache, I prefer to simply clear it out of cache and wait for the next fetch request to add back into cache. This way you don’t have a bunch of objects stored in cache that aren’t being used.

Anyway, the approach to clearing the cache is extremely simple. Let’s assume you already have a method for processing an update to a database record that underlies your object. To clear the old version of the object from cache, simply call a method like this:

public function clear_cached($id) {
 
       $cache_id = $this->create_cache_id($id);
       $cache = Zend_Cache::factory('Core', 'APC');
       $cache->remove($cache_id);
 
}

It is truly not much more complicated than the above examples, particularly if you already have an existing application structure you want to add Zend_Cache to. I’ve been pleased with my first experiments into Zend Framework and intend to explore some other components for use in my projects. Hopefully this tutorial will help someone else out there who’s been hesitant to buy into the framework craze.

UPDATE: Just thought I’d link to a couple of other great resources on Zend_Cache:

Zend Framework Hidden Gems: Zend_Cache (Zend Developer Zone)

Joey Rivera: Caching using PHP/Zend_Cache and MySQL

Lifehacker: Teach Yourself to How to Code

Lifehacker continues to move up my list of favorite sites to visit on a daily basis.  They recently ran a list of top how-to guides from 2009, and included among them is Programming 101: Teach Yourself How to Code.
PHP gets plenty of attention in the section about server-side scripting languages, althought I will note they chose to post the cover of a Python book in the paragraph.

Server-side scripting: Once you’re good at making things happen inside a web page, you’re going to need to put some dynamic server action behind it—and for that, you’ll need to move into a server-side scripting language, like PHP, Python, Perl, or Ruby. For example, to make a web-based contact form that sends an email somewhere based on what a user entered, a server-side script is required. Scripting languages like PHP can talk to a database on your web server as well, so if you want to make a site where users can log in and store information, that’s the way to go. Excellent web development site Webmonkey is full of tutorials for various web programming languages. See their PHP Tutorial for Beginners. When you’re ready, check out how to use PHP to talk to a database in WebMonkey’s PHP and MySQL tutorial. PHP’s online documentation and function reference is the best on the web. Each entry (like this one on the strlen function) includes user comments at the bottom which are often as helpful as the documentation itself. (I happen to be partial to PHP, but there are plenty of other server-side scripting languages you might decide to go with instead.)

The author, Gina Trapani, is Lifehacker’s founder and, like me,  is a self-taught programmer:

Good coders are a special breed of persistent problem-solvers who are addicted to the small victories that come along a long path of trial and error. Learning how to program is very rewarding, but it can also be a frustrating and solitary experience. If you can, get a buddy to work with you along the way. Getting really good at programming, like anything else, is a matter of sticking with it, trying things out, and getting experience as you go.

New theme for my blog…

I’ve been paying more attention to web design lately.  Why?  Well I’m definitely not morphing into a designer–I don’t have the DNA for it–but rather because design is relevant, even in an enterprise environment.  As I have mentioned before, the interface matters to the end user–perhaps moreso than all the backend code you’ve spent hours building and that only other coders can truly appreciate.

With that said, I think this blog was getting stale, and although I continue to adore everything Apple, the “Mac” themed blog thing is just a little dated.  After searching WordPress.org, I stumbled upon MacPress, a very clean and modern Web 2.0-ish theme by the folks at Sizlopedia.  I hope you enjoy the cleaner look, which I happen to think is vastly more readable.  I’ll probably tweak here and there in the next few weeks, but I am largely happy with it as-is.

You can download MacPress from WordPress.org or from Sizlopedia.com.

Smashing Mag: How to Support IE and Still be Cutting Edge

Smashing Magazine is more for the web “design” and photoshop crowd, but any developers working with GUI/front ends at all (which is most of us) will find tons of great information there.  Chris Blatnik says that it is the GUI that makes or breaks an app (after all, the users never see the code, no matter how great the developer thinks it is).

Their latest post is about supporting IE on your websites while still utilizing the latest web technologies, such as CSS3.

The payoff:

Remember that the purpose of this post is not to teach you how to hack IE or deal with its quirks or even how to achieve effects by resorting to JavaScript. Rather, it is to explain how we can design and build websites knowing that differences will arise between browsers.

You won’t see people rioting over the lack of rounded corners on Twitter or WordPress; they aren’t even upset by it, because those differences don’t fundamentally break the websites. People can still use the websites and have a good experience. In most cases, they won’t even notice it!