At one stage or another most programmers have written some in-function caching. If you don't know what I mean my in-function caching, here is a simple example:
So you can call this method several times for the same username but only have to talk to the database once.
This method generally works well in situations where:
- The data you are reading is not going to be changing. If the data can change then it is possible that the cache will end up not being an accurate representation of the real data.
- You are only calling the function with a small number of values. If you were calling the function for a wide range of values then the cache could begin to consume a large amount of memory.
To illustrate the first point, imagine that you called getUserId() for the user 'adam'. You then delete the user 'adam'. You then call getUserId() (again with the username of 'adam') only to be returned with the cached user ID rather than a value of false (or an error).
I recently encountered the second point in the above list as I am in the process of writing a library that (among other things) parses Apache HTTPD log files into a database table. For this I had to convert IP addresses into country codes as well as parse user-agent strings for hundreds of thousands of records. Clearly caching would be useful here, but I could not use the above method as the script would quickly gobble up more and more memory.
A Class for In-Function Caching
In response to this problem I created the following PriorityCache class:
You can see that this cache addresses both of the problems with our simplistic example:
- You give your new cache a series of arbitrary string dependences. In our example we may have used the string 'table:users', and then whenever we modified the users table we could call
Stapi_Utils_PriorityCache::resetDependants('table:users');
- The cache will start dropping its oldest values once it fills up. This means we can now use our cache for a wide range of values without worrying about rampaging memory usage.
There are several ways in which you could extend this class. For example, you could probably save memory by passing all key values through md5() (or even better, crc32()). You could also look into making the cashed data persistent across requests, but if you need this level of caching then you should probably look at Zend_Cache.
I will leave you with an example of using the above class with a trivial getMd5 function:
Trackbacks