Tuesday, October 18, 2011

Learn to Love the Cache

I've avoided using the caching aspects of Yii for a long time, simply because I never had time to dig into it. My cursory understanding was that you needed to have APC or memcached enabled on your server for it to work, and that wasn't something I could guarantee. That understanding was NOT correct.

There are several caching options built in to Yii, including a simple CFileCache which will write files to your application/runtime/cache/ folder and a CDummyCache that you can use if you need to bypass the real cache without altering your code.

This example starts with just the most basic situation where we are consuming data from an external source and caching it locally so that we do not continually ping the remote server for data.

The Guide to Caching can be found here: http://www.yiiframework.com/doc/guide/1.1/en/caching.overview


NOTE:
If you use MySQL and you're not already doing so, you should enable query caching by setting your query_cache_size to a non-0 value. There are a lot of good write ups about tuning your mysql caching, but as a jump-start, you can easily set that to 16M and instantly see improvements. Too large (500MB) and you'll lose performance due to the indexing demands on your cache.

See: http://dev.mysql.com/doc/refman/5.6/en/query-cache-configuration.html

NOTE 2:
For purposes of this example, we're populating our data with a simple array. Going through and explaining how to consume data via curl is beyond the scope of this example and there are lots of good examples out there of how to do so!

The biggest hurdle to implementing data caching for your application, is getting yourself in the habit of caching the data and checking there first!

So, lets step through an example.

Lets assume I have an application that displays data that I get by running an API call on a remote server. That data changes, but not too frequently (lets assume hourly updates).

First, I need to ensure that I have a cache set up in my application.

Open up protected/config/main.php and add the cache to the components array:
...
   'components'=>array(
       ...
       // Add a cache component to store data 
       // For demo, we are using the CFileCache, you can use any 
       // type your server is configured for. This is the simplest as it
       // requires no configuration or setup on the server.
       'cache'=>array(
           'class'=>'system.caching.CFileCache',
       ),
       ...
   ),
...

I now have access to my cache by simply refrencing:
Yii::app()->cache

So, in my action that is running the API query, I can now cache those results and use them rather than running the API every time.

...
public function actionCachedDataSample()
{
    // Specify the ID for the cache item we wish to 
    // reference
    $api_cache_id = 'my_api_data';

    // Attempt to load the data from the cache, based on the key
    $apidata = Yii::app()->cache->get( $api_cache_id  );

    // If the results were false, then we have no valid data, 
    // so load it
    if($apidata===false)
    {
        // No valid cached data was found, so we will generate it.
        $apidata = $this->loadRemoteContent();

        // Store the data into the cache and allow it to be
        // valid for 1 hour (3600 seconds)
        Yii::app()->cache->set( $api_cache_id , $apidata, 3600 );
    }

    // Render some stuff here with my data
    // $this->render( 'sample', array( 'apidata'=>$apidata ) );
    // For now we'll just do a print_r so that we can see the data.
    print_r( $apidata );
}

/**
 * In a real scenario, this would be a curl_exec() to a remote server
 * to get a consumable data source - xml, json data, etc. 
 * For this example, we'll return a simple array that includes the time
 * it was generated.
 */
protected function loadRemoteContent( )
{
    return array( 
        'You are viewing cached data.',
        'The time is now '.date('Y-m-d H:i:s'),
    ); 
}
...

It's literally that simple to get started.

There is a LOT more that you can do with caching. This sample just scratches the surface of how to use it even without APC or memcached on your server. If you are using APC or memcached, then simply change cache component configuration in the main.php file. Your action does not need to change at all!

5 comments:

  1. Very nice and very timely. I was just thinking about caching for an app that makes a lot of remote calls. I was originally thinking about adding a caching proxy server like squid to my VPS, but I suspect this gives me more control.

    --bglee

    ReplyDelete
  2. Thanks. And what about caching SQL-requests?

    ReplyDelete
  3. Hi Olga, caching sql request is as simple as using cache( ) before the method that retrieves the data: ex:

    Post::model()->cache(5)->findAll();

    This will cache your query for 5 minutes.
    You can even add a dependency as a second parameter, so that it will last 5 minutes OR if the dependency status changes, from the time the first call was made, then, the cache is invalidated, and the query is run again.

    Check this url http://www.yiiframework.com/doc/api/1.1/CActiveRecord#cache-detail for cache reference, and http://www.yiiframework.com/doc/guide/1.1/en/caching.data#query-caching for cache sample.

    ReplyDelete
  4. Thanks! This is enough me to start as beginner.

    ReplyDelete
  5. Terima Kasih. Akhirnya saya bisa menggunakan metode caching berkat artikel anda ini. Sangat bermanfaat :)

    ReplyDelete