Caching

Advanced Caching


Content Author

Dan Skaggs

dskaggs

Reviewed/Revised By

Matthew Clemente

mjclemente84

The optimal caching strategy is highly personalized - dependent on your hardware, application, and load. In many cases, the standard caching mechanisms provided by ColdFusion will be sufficient. However, at each tier of your caching strategy, ColdFusion provides options for further customization and control:

  • Server: The underlying default caching engine can be changed. There is built-in support for Ehcache, JCS, memcached, and Redis, each of which can have its default settings configured independently.
  • Application: The caching engine, size, and eviction settings can also be set at the application level, overriding the server configuration.
  • Object: Custom cache regions within an application enable even more fine-grained control of object caching.

Caching Engines

ColdFusion's default caching engine is Ehcache - it's been the underlying caching engine since ColdFusion 9. Though you may not have known it, all the code examples in the previous caching article utilized ColdFusion's implementation of Ehcache. ColdFusion 2018 added support for three additional caching engines: JCS, memcached, and Redis - as well as the ability to utilize a custom caching engine. (Note that Redis and memcached are restricted to ColdFusion Enterpise Edition only.)

If you're planning on implementing or exploring these alternatives, I encourage you to read the official documentation for these caching enhancements. Additionally, if you're interested in reasons for using one of these new caching options, I'll point you to this introductory blog post, which outlines pros and cons of the various engines. Both are helpful resources, with a good deal of detail.

Server Settings

Within the ColdFusion Administrator, the Server wide cache engine dropdown provides a straightforward means of changing the default caching engine for the server. Note that while you don't need to restart the server if you change the default caching engine, it will result in the loss of data cached with the previous engine.

Here in the Administrator you can also configure the following default cache properties for the three new caching engines:

  • Max idle time: The default length of time after which an object will be evicted from the cache if it hasn't been accessed, measured in seconds. This is sometimes referred to as idletime. It defaults to 86,400 (1 day).
  • Max life span: The default length of time for which an object can persist in the cache before it is evicted, measured in seconds. This is sometimes referred to as timespan. It also defaults to 86,400 (1 day).
  • Max elements: The default limit for items in the cache. Set to 10,000 by default.
  • Eternal: When set to true, the items in the cache never expire. They must be removed manually. This setting overrides the Max idle time and Max life span settings.

Additional settings for configuring ColdFusion's connection to Redis and/or memcached are found here as well. This is necessary because unlike Ehcache and JCS, they do not come bundled with ColdFusion and require separate installation. Note that unlike switching the default cache engine, changing any of these settings requires restarting ColdFusion.

The default values listed here for each caching engine may be used by applications, even if that particular caching engine is not the default for the server. This is because applications have the ability to override the server-wide cache engine and use one of the other options. So, for example, if Ehcache is the default cache engine for the server, an application can override this and use JCS. In this example, the defaults listed in the ColdFusion Administrator for JCS would be used, unless they were also overridden at the application level. Let's take a look at how that is done.

Application Specific Cache Settings

Within your Application.cfc, two optional properties enable you to declare application specific cache settings:

  • this.cache.engine: Used to declare the caching engine for the application. Valid options for this property are ehcache, jcs, redis, memcached, or the name of a custom caching engine that you have configured. The specifics of configuring a custom caching engine are beyond the scope of this article, but if you're interested in learning more, the process is explained in this post. You can verify the caching engine being used by an application using the new cacheGetEngineProperties function, which returns a struct containing the name of the caching engine in use.
  • this.cache.configfile: The path to a file containing the configuration properties for the caching engine. This can be absolute or relative to Application.cfc.

Here's a basic example of how a custom cache configuration might look within Application.cfc:

component {
    this.name = "applicationSpecificCache";
    this.cache.engine = 'jcs';
    this.cache.configfile = "cache.properties";
}

For JCS, memcached, and Redis, the file defined in this.cache.configfile should consist of simple key-value pairings, corresponding to the default server cache properties; here's an example:

timeToIdleSeconds=21600
timeToLiveSeconds=86400
maxElementsInMemory=5000
eternal=false

Ehcache, on the other hand, is a bit more complex. Its application specific configuration is defined in an XML file, ehcache.xml. Here's an example configuration file, formatted so that you can see the four settings it shares with the other caching engines:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" >
    <diskStore path="java.io.tmpdir"/>
    <cacheManagerEventListenerFactory class="" properties=""/>
    <defaultCache
        timeToIdleSeconds="21600"
        timeToLiveSeconds="86400"
        maxElementsInMemory="5000"
        eternal="false"
        overflowToDisk="false" diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="3600" memoryStoreEvictionPolicy="LRU" clearOnFlush="true" statistics="true">
</ehcache>

For further reference, you can review (but don't modify) ColdFusion's own Ehcache configuration file, located at cf_root\lib\ehcache.xml.

Regardless of caching engine, you can confirm your application specific cache settings have been applied using cacheGetProperties( 'object'), which returns a struct containing the settings for the application's default OBJECT cache.

Cache Regions

Finally, within an application, custom cache regions provide an additional level of control. While they cannot change the underlying caching engine, custom cache regions can provide different eviction settings, memory limits, and a means of organizing data.

A common, straightforward use case for custom cache regions is to simplify partial cache eviction. Grouping and storing like-data in separate cache regions enables you to manually clear one region without impacting the rest of the cache. For example, if you cached administrative settings in a region separate from rendered frontend data, you could purge one without impacting the other. At a basic level, ColdFusion already implements this itself, by using separate regions for its TEMPLATE, QUERY, and OBJECT caches.

A more complex approach could involve storing data in cache regions based on eviction policy. Within an application, certain types of cached data may have distinctly different lifecycles. While a list of countries and the results of a daily report could both be placed within ColdFusion's default object cache, storing them in expiration-specific cache regions enables your application to automatically evict the report after a day, but keep the country list cached indefinitely, until a change is made.

The Cache Region Functions

New cache regions are created with cacheRegionNew, which takes the name of the new region as its first argument. Properties for the region can be configured via a struct, passed in as an optional second argument; if they're not provided the region is created with the same settings as the default cache. Here's an example, creating a region with custom properties:

<cfscript>
    properties = {
        timeToIdleSeconds = 3500,
        timeToLiveSeconds = 21600,
        maxElementsInMemory = 2500,
        eternal = false
    };
    cacheRegionNew( 'htmlBlock', properties );
</cfscript>

You can check for the existence of a region using cacheRegionExists, and remove existing regions with cacheRegionRemove. Both take the name of the region as their only argument.

Let's take a quick look at how cache regions can be utilized with the programmatic caching functions discussed in the previous article:

cachePut

cachePut( id, value, [timeSpan], [idleTime], [region], [throwOnError] );

You can store data in a custom region by providing its name as the region argument (for reference, the name of the default region used is OBJECT). If the custom region does not exist, it is created with the default cache settings. You can override this behavior using the final argument, throwOnError; when set to true, an error is thrown if the region doesn't exist. To bypass the timeSpan and idleTime arguments, pass in empty strings; for example, cachePut( "greeting", "<h1>Hi!</h1>", "", "", "htmlBlock" ).

cacheGet

cacheGet( id, [region] );

If you put data into a custom cache region, you'll need to provide its name as the region argument, in order to retrieve it. Like cachePut, when the region isn't provided, the default OBJECT cache region is used.

cacheGetAllIds

cacheGetAllIds( [region] );

Returns an array of all the identifiers of objects stored in the cache region provided.

cacheRemove

cacheRemove( id, [throwOnError], [region], [exact] );

As you'd expect, the region argument specifies the cache region from which the cache identifier should be evicted; it corresponds to the region arguments for cachePut and cacheGet (as with those functions, it defaults to using the OBJECT cache region).

The final argument, exact, can be a very powerful option, if you construct your cache identifiers to account for it. By default, eviction is limited to exact matching identifers. When you set this argument to false, eviction is based on a wildcard match - any cached identifers containing the id are evicted. So, for example, if you had data cached with the identifiers learncfinaweek and learncfinaday, you could use cacheRemove( 'cf', false, 'object', false ) to remove both.

cacheRemoveAll

cacheRemoveAll( [region] );

Used to remove all objects within the specified cache region. When called without argument, it flushes the default OBJECT cache region.

cacheGetProperties

cacheGetProperties( [region] );

Returns a struct with the properties for the cache region specified. When no region is provided, it returns an array, containing property structs for the OBJECT, TEMPLATE, and QUERY cache regions.