-
Notifications
You must be signed in to change notification settings - Fork 24
Introduction to Caching
- Overview
- Getting Started
- Caching Related Commands
- Caching Strategies
- Multiple Caching Strategies
- Advanced Information
- General Usage Notes
Mach-II includes a caching package sub-system that allows you to cache data retrieved by listeners and output generated by views. In other words, you can cache the result of one or more listener calls (i.e. notify commands specified in the XML configuration file) and the result of a rendered view (i.e. view-page commands specified in the XML configuration file). Calls to subroutines can also be cached.
The developer can control the state of cache and clear it when data in the cache has become stale. For example, if a user updates a list of parts and this list is cached by Mach-II, the developer can clear the parts cache on demand, which causes the parts cache to be re-populated the next time the parts list is requested.
Three caching strategies are included with the Mach-II framework:
- Time Span
- Native CFML Time Span and
- Least Recently Used (LRU)
Developers have the ability to write their own and have it extend the AbstractCacheStrategy
component.
This tutorial will show you how to use the Caching Property and how to alter the defaults to your liking. This tutorial assumes you already have a basic understanding of Mach-II (editing the config.xml, calling an event, etc.).
This feature was developed under the Mach-II specification and feedback process (M2SFP).
Adding the Caching Property requires you to put the following line of xml within the <properties>
section of your config.xml file:
<property name="Caching" type="MachII.caching.CachingProperty"/>
With no additional parameters specified, this will set the default settings for caching (which is one hour in the application scope - for more information see below). To add something to the cache, you will also need to add the cache command to an event-handler. Below is an example of caching the view of a web site's home page.
This example is the barest form using only one command in the cache handler, but you can nest as many commands in the cache as you want:
<event-handler name="home" access="public">
<cache>
<view-page name="home" />
</cache>
</event-handler>
The cache command instructs Mach-II to save the "home" view into an application scoped variable and use it instead of reading home.cfm each time this event is called.
The <cache>
command only caches data that is located in the event that:
- has been added to the event by a command or other code inside the
<cache>
block - was changed by a command or other code inside the
<cache>
block
The <cache>
command does not cache data that:
- was set to the event or existed in the event before the <cache> block was executed
- does not exist in the event object such as in an external scope like the
request
scope (this affects users of the deprecatedresultKey
andcontentKey
features)
Mach-II ships with commands available for your use in the Mach-II XML configuration file. Below is information on the command attributes with examples on how to use them.
Attribute | Values | Required | Default | Description |
---|---|---|---|---|
id | unique id | optional | Unique id this cache block is to be known by | |
aliases | list of aliases | optional | List of aliases this cache can be referenced by later on such as clearing the cache. | |
strategyName | name of cache strategy to use | optional | The name of the cache strategy you wish to use as specified in the parameter for the Caching property. If left out, defaultCacheName will be used. | |
criteria | optional | true | Comma delimited list of optional criteria to further identify the storage of your data. See below for more information. |
Cache criteria: The criteria allows you to define additional identification to your cache such as an url parameter or other event argument. For example, perhaps you want to cache the output for several items in your product catalog and they are identified by itemId. Any variable listed here will be pulled from the event arguments. If the event-argument does not exist, it will default to blank. The following uses itemId as its caching criteria:
<cache aliases="itemCache" criteria="itemId">
OR
<cache aliases="itemCache" criteria="${event.itemId}">
Separate caches would be created by each of the the following URLs.
/index.cfm?event=listenerTest&ItemId=99
/index.cfm?event=listenerTest&ItemId=1
Both of these requests would be cached independently from each other using the 'itemId' as one component of the caching criteria. In terms of its concrete implementation, the itemId is used to compose the identity key.
Additionally, you can enter something like "itemId=itemId" and Mach-II will translate that as "itemID=99". You can specify multiple criteria as a comma-delimted list (e.g., criteria="mainCategory=mainCatId,subCategory=subCatId").
Remember that you cannot use <announce>
, announceEvent()
, announceEventInModule()
or <redirect>
inside of a cache command as these actions are not compatible with the caching infrastructure (i.e. cached data does not replay event announcements or redirects).
Attribute | Values | Required | Default | Description |
---|---|---|---|---|
ids | unique ids | optional | List of unique ids to clear | |
aliases | list of aliases | optional | List of aliases to clear | |
strategyNames | list of cache strategy names | optional | The names of the cache strategies you wish to clear as specified in the parameter for the Caching property. If left out, defaultCacheName will be used. | |
criteria | optional | true | Comma delimited list of optional criteria to further identify which items in the cache you want to clear. See below for more information. | |
condition | expression | optional | A expression that must be met in order for the cache to be cleared. |
N.B. A few attribute names differ from the attributes available in the cache
command. Those attributes are aliases
, ids
and strategyNames
which are all plural however in the cache
command they are singular. This is because cache-clear
support clearing multiple aliases, id or cache strategies by name at once. Please beware of these differences as leaving of aliases
or ids
when trying to clear a specific element will essentially clear the entire cache because no aliases or ids were specified.
The cache-clear command also accepts the strategyNames
attribute, so an entire caching strategy may be cleared programmatically in one command. You can also specify the ids
, aliases
and criteria
attributes so that you clear out a specific item in the cache. Here is an example event-handler with cache-clear:
Clears cache blocks by alias acctNav
where the criteria is user_id
key is value returned from an event arg named userId
:
<event-handler event="logout">
<cache-clear aliases="acctNav" criteria="user_Id=${event.userId}" />
<notify listener="AccountListener" method="logoutUser" />
<announce event="home" copyEventArgs="true" />
</event-handler>
Clears an entire cache strategy by name of general
:
<event-handler event="logout">
<cache-clear strategyNames="general" />
<notify listener="AccountListener" method="logoutUser" />
<announce event="home" copyEventArgs="true" />
</event-handler>
Clears a cache block with id of home
when the condition evaluates to true:
<event-handler event="logout">
<cache-clear ids="home" condition="${event.someArg} EQ 1" />
<notify listener="AccountListener" method="logoutUser" />
<announce event="home" copyEventArgs="true" />
</event-handler>
Clears the entire default cache strategy:
<event-handler event="logout">
<cache-clear />
<notify listener="AccountListener" method="logoutUser" />
<announce event="home" copyEventArgs="true" />
</event-handler>
The Time Span and Least Recently Used (LRU) Caching strategies are included with Mach-II. The CFCs can be found in \MachII\caching\strategies
. Additional caching strategies will be added in future versions of Mach-II. You can also write your own strategy that could interface with distributed caching systems like MemCached or Ehcache by writing a concrete CFC that extends MachII.caching.strategies.AbstractCacheStrategy
.
The default cache strategy is the Time Span strategy. This allows the developer to define an amount of time that the data in question should be cached. The Notes section of the comments in the MachII.caching.strategies.TimeSpanCache
shows all the configuration options available:
<property name="Caching" type="MachII.caching.CachingProperty">
<parameters>
<!-- Naming a default cache name is not required, but required if you do not want
to specify the 'name' attribute in the cache command -->
<parameter name="defaultCacheName" value="default" />
<parameter name="default">
<struct>
<key name="type" value="TimeSpanCache" />
<key name="cachingEnabled">
<struct>
<!-- All key names prefixed with 'group:' indicate environment groups -->
<key name="group:development,qa" value="false"/>
<!-- Explicitly setting environment names of 'prod' and 'prodFailOver' -->
<key name="prod,prodFailOver" value="true"/>
</struct>
</key>
<key name="scope" value="application" />
<key name="timespan" value="0,1,0,0" />
<key name="cleanupIntervalInMinutes" value="3" />
</struct>
</parameter>
</parameters>
</property>
As noted above, the defaultCacheName
is not required, but a good idea to name the default cache so that you won't have to specify the cache name within the event handler. Setting a default is useful if you wish to use more than one caching strategy. For example, you may want some items cached for 1 hour and other items cached for 24 hours.
You then define each cache giving it a name and passing in a structure of various keys which are explained below. Each key
tag above is explained below.
Key | Description | Required | Values | Default Value |
---|---|---|---|---|
type | The cache strategy to use which in this case is MachII.caching.strategies.TimeSpanCache . |
yes | CFC dot path | n/a |
cachingEnabled | Turns caching on or off | no | boolean or struct of environment names / groups (prefixed with group: ) with corresponding booleans (struct supported in Mach-II 1.8+) |
true |
scope | The scope that the cache should be placed in. | no | application, server, session | application |
timespan | Takes a string formatted like CFML's CreateTimeSpan() function that indicates the cache for time span. The list of 0,0,0,0 is days, hours, minutes, seconds or may take the value of forever . |
no | numeric list values or forever
|
0,1,0,0 (1 hour) |
cleanupIntervalInMinutes | The number of minutes in which to run the reap() method. Reap removes expired elements from the cache, but does not "refresh" the data. Data is added back into the cache only when an event-handler requests that data. | no | numeric value | 3 |
Using all of the default settings will result in caching each element of data for 1 hour in the application scope. Expired cache elements will be cleaned up via reap() which is run every 3 minutes.
This allows the developer to define an amount of time that the data in question should be cached however unlike the TimeSpanCache
this cache uses native CFML cacheGet()
/ cachePut()
methods. Because the native functions are being used, this allows you leverage other caching systems like Eh Cache or Memory. Remember that these native CFML functions are only available on Adobe CF9+, Railo 3+ and OpenBD 1.5+. The Notes section of the comments in the MachII.caching.strategies.TimeSpanNativeCfmlCache
shows all the configuration options available:
<property name="Caching" type="MachII.caching.CachingProperty">
<parameters>
<!--
Naming a default cache name is not required, but required if you do not want
to specify the 'name' attribute in the cache command
-->
<parameter name="defaultCacheName" value="default" />
<parameter name="default">
<struct>
<key name="type" value="TimeSpanNativeCfmlCache" />
<key name="cachingEnabled">
<struct>
<!-- All key names prefixed with 'group:' indicate environment groups -->
<key name="group:development,qa" value="false"/>
<!-- Explicitly setting environment names of 'prod' and 'prodFailOver' -->
<key name="prod,prodFailOver" value="true"/>
</struct>
</key>
<key name="timespan" value="0,1,0,0" />
<key name="timespanIdle" value="0,1,0,0" />
</struct>
</parameter>
</parameters>
</property>
As noted above, the defaultCacheName
is not required, but a good idea to name the default cache so that you won't have to specify the cache name within the event handler. Setting a default is useful if you wish to use more than one caching strategy. For example, you may want some items cached for 1 hour and other items cached for 24 hours.
You then define each cache giving it a name and passing in a structure of various keys which are explained below. Each key
tag above is explained below.
Key | Description | Required | Values | Default Value |
---|---|---|---|---|
type` | The cache strategy to use which in this case is MachII.caching.strategies.TimeSpanCache . |
yes | CFC dot path | n/a |
cachingEnabled | Turns caching on or off | no | boolean or struct of environment names / groups (prefixed with group: ) with corresponding booleans (struct supported in Mach-II 1.8+) |
true |
timespan | The duration until the object is flushed from the cache. Takes a string formatted like CFML's CreateTimeSpan() function. The list of 0,0,0,0 is days, hours, minutes, seconds or may take the value of forever . |
no | numeric list values or forever
|
0,1,0,0 (1 hour) |
timespanIdle | The duration after which the object is flushed from the cache if it is not accessed during that time. Takes a string formatted like CFML's CreateTimeSpan() function. The list of 0,0,0,0 is days, hours, minutes, seconds or may take the value of forever . |
no | numeric list values or forever
|
0,1,0,0 (1 hour) |
cacheName | The name of the native CFML cache configuration to use. Supported in Adobe 9.01+ or Railo 3+ only. | no | string | n/a |
LRU stands for Least Recently Used and is a caching policy that keeps the most recently accessed data in the cache and discards least accessed data first. You can visualize the LRU cache as a sized array. Elements at the top of the array are the most recently accessed data while elements at the bottom are older. At a certain point, old data exceeds the size of the array and is discarded. LRU caches are somewhat more process intensive as each time a new piece of data is added, all other pieces of data must shift so the least recently used piece of data is discarded first. LRU caches are better suited for smaller datasets that change infrequently.
The Notes section of the comments in the MachII.caching.strategies.LRUCache
shows all the configuration options available:
<property name="Caching" type="MachII.caching.CachingProperty">
<parameters>
<!--
Naming a default cache name is not required, but required if you do not want
to specify the 'name' attribute in the cache command
-->
<parameter name="defaultCacheName" value="default" />
<parameter name="default">
<struct>
<key name="type" value="MachII.caching.strategies.LRUCache" />
<key name="cachingEnabled">
<struct>
<!-- All key names prefixed with 'group:' indicate environment groups -->
<key name="group:development,qa" value="false"/>
<!-- Explicitly setting environment names of 'prod' and 'prodFailOver' -->
<key name="prod,prodFailOver" value="true"/>
</struct>
</key>
<key name="size" value="100" />
<key name="scope" value="application" />
</struct>
</parameter>
</parameters>
</property>
You then define each cache giving it a name and passing in a structure of various keys which are explained below. Each key
tag above is explained below.
Key | Description | Required | Values | Default Value |
---|---|---|---|---|
type | The cache strategy to use which in this case is MachII.caching.strategies.LRUCache . |
yes | CFC dot path | n/a |
cachingEnabled | Turns caching on or off | no | boolean or struct of environment names / groups (prefixed with group: ) with corresponding booleans (struct supported in Mach-II 1.8+) |
true |
size | The size of the LRU cache size before data is discarded. | no | numeric | 100 |
scope | The scope that the cache should be placed in. | no | application, server, session | application |
Using all of the default settings will result in caching 100 elements of data in the application scope with least recently used elements of data being discarded first.
It is possible to use more than one caching strategy. One example is to use the Time Span Cache strategy twice for different lengths of time.
<property name="Caching" type="MachII.caching.CachingProperty">
<parameters>
<!--
Naming a default cache name is not required, but required if you do not want
to specify the 'name' attribute in the cache command
-->
<parameter name="defaultCacheName" value="static_content" />
<parameter name="static_content">
<struct>
<key name="type" value="MachII.caching.strategies.TimeSpanCache" />
<key name="scope" value="application" />
<key name="timespan" value="0,1,0,0" />
<key name="cleanupIntervalInMinutes" value="10" />
</struct>
</parameter>
<parameter name="user_specific">
<struct>
<key name="type" value="MachII.caching.strategies.TimeSpanCache" />
<key name="scope" value="application" />
<key name="timespan" value="0,1,0,0" />
<key name="cleanupIntervalInMinutes" value="3" />
</struct>
</parameter>
</parameters>
</property>
And here are two event-handlers showing how to reference these. Note that the first one does not specify a cacheName, therefore it will use the default specified above - "static_Content". The second event-handler will cache the user's account page and create a separate cache based on the userId
<event-handler event="home">
<cache aliases="homePage">
<view-page name="home" />
</cache>
</event-handler>
<event-handler event="myAccountHome">
<cache aliases="myAccountHomePage" strategyName="user_specific" criteria="userId">
<notify listener="AccountListener" method="getUserInfo" resultArg="qryUserInfo">
<view-page name="myAccountHomeView" />
</cache>
</event-handler>
In Mach-II 1.6 currently, you have to do define the complete type to the strategy you want to use MachII.caching.strategies.TimeSpanCache
:
<parameter name="generalPage">
<struct>
<key name="type" value="MachII.caching.strategies.TimeSpanCache"/>
<key name="scope" value="application"/>
<key name="timespan" value="forever"/>
</struct>
</parameter>
Begining with Mach-II Simplicity (1.8), you can use type shortcuts like this:
<parameter name="generalPage">
<struct>
<key name="type" value="TimeSpanCache"/>
<key name="scope" value="application"/>
<key name="timespan" value="forever"/>
</struct>
</parameter>
The follow shortcuts are available as of Mach-II Simplicity (1.8) and will be expanded when additional cache strategies are added:
Shortcut Name | Full Type Dot Path |
---|---|
TimeSpanCache |
MachII.caching.strategies.TimeSpanCache |
LRUCache |
MachII.caching.strategies.LRUCache |
Please note well that shortcuts do not work on Mach-II 1.6.
All cache strategies have a caching stats CFC that provides information regarding the performance of the cache. The stats include total hits, misses, evictions, active elements and total elements. Also, one calculation is performed that indicates the hit ratio of the cache. The hit ratio is a percentage that indicates the general performance of the strategy as it is current configured and is computed the total hits divided by total accesses (hits + misses). In order to get accurate data, your cache strategy must be active for a period of time (minutes to hours depending on load). Most developers should aim for a hit ratio above 70-75% where ratios in the 90%+ are ideal.
The easiest way to view cache stats is to use the Mach-II Dashboard module. It offers charts of the cache efficiency in addition to the raw data and other extremely useful functionality wrapped up in a nice GUI.
Or you can programmatically get the caching stats CFC by calling:
getAppManager().getCacheManager().getCacheStrategyManager().getCacheStrategyByName("nameOfDesiredStrategy").getCacheStats()
Calling getCacheStats()
returns an object with getters. The metrics tracked by stats are as follows:
- Cache hits - the total number of hits
- Cache misses - the total number of misses
- Cache active element count - the total number of elements currently in the cache
- Cache total element count - the total number of element put into the cache over the lifetime of the cache
- Cache evictions - number of elements that the cache removed to make room for new elements
- Cache hit ratio - hit ratio (decimal) is (hits / total accesses) where total accesses is hits + misses.
- Cache stats active since date - the date/time when the cache stats started or last reset
The Caching Property offers two difference levels to turn caching on or off. The global caching enabled parameter turns all cache strategies defined in the property on or off while the local parameter is granular and is used to turn individual caching strategies on or off.
Below the cachingEnabled
parameter enables or disabled all cache strategies defined by this property (which is the general
cache). This parameter takes a boolean or struct of environments name or groups. In this case, the same shows environment groups and names. Remember concrete environment names take precedence over environment groups when deciding the global caching enabled parameter.
<property name="Caching" type="CachingProperty">
<parameters>
<parameter name="cachingEnabled">
<struct>
<!-- Turns off if the current environment group is in the development group -->
<key name="group:development" value="false" />
<!-- Turns off if the current environment *name* is 'local-prod' --->
<key name="local-prod" value="false" />
<!-- Turns on if the current environment group is either staging or production -->
<key name="group:staging,production" value="true" />
</struct>
</parameter>
<parameter name="defaultCacheName" value="General" />
<parameter name="General">
<struct>
<key name="type" value="TimeSpanCache" />
<key name="scope" value="application" />
<key name="timespan" value="forever" />
</struct>
</parameter>
<parameters>
</property>
Local caching enabled on strategies override the global value (think external CSS files being overrided by inline CSS):
<property name="Caching" type="CachingProperty">
<parameters>
<parameter name="cachingEnabled">
<struct>
<!-- Turns off if the current environment group is in the development group -->
<key name="group:development" value="false" />
<!-- Turns off if the current environment *name* is 'local-prod' --->
<key name="local-prod" value="false" />
<!-- Turns on if the current environment group is either staging or production -->
<key name="group:staging,production" value="true" />
</struct>
</parameter>
<parameter name="defaultCacheName" value="General" />
<parameter name="General">
<struct>
<key name="type" value="TimeSpanCache" />
<key name="scope" value="application" />
<key name="cachingEnabled" value="true" />
<key name="timespan" value="forever" />
</struct>
</parameter>
<parameters>
</property>
Persisting the cache across multiple applications on the same server by using the server scope and the scopeKey parameter
As described above, the Caching Property offers 3 scopes in which to store the cache (application, server, and session). In the case where multiple applications are used on the same server and the cache needs to be persisted across them, the server scope is ideal. One caveat of this option is that a scopeKey parameter needs to be specified. By using the same generic scopeKey parameter value for each application, the applications will be able to share the cache. If it is not specified, separate keys for each application's cache will be created in the server scope, using the application name as the key, and the cache will not be shared.
<parameter name="Caching">
<struct>
<key name="type" value="TimeSpanCache"/>
<key name="scope" value="server"/>
<key name="scopeKey" value="genericScopeKeyName"/>
<key name="cachingEnabled" value="true" />
<key name="timespan" value="forever"/>
</struct>
</parameter>
Caching data can yield unexpected results if not used correctly. Here are some general notes to keep in mind.
- An error will be thrown if you use the
<announce>
,<event-mapping>
or<redirect>
commands within cache tags as they will not cache correctly. - Likewise, you should not cache a listener notification that announces a new event. The new event will not be announced if that command or listener method is cached as event announcements cannot be replayed when using cached data.
- Also, filter commands that possibly redirect events should not be cached as redirects will not be replayed when using cached data.
- Use the criteria attribute to differentiate content that varies based on form or url parameters.
- When using clear-cache with the criteria attribute, you may have to manually put those criteria into the event arguments before the clear-cache command. This would be needed if the event with clear-cache does not normally have those arguments.
- Mach-II will not cache data when using deprecated features like
contentKey
andresultKey
. - Mach-II has to recreate the cache when Mach-II is reloaded if you are using the time span or LRU caches.
- Cache will not replay text inserted in the head via
<cfhtmlhead>
. UseaddHTMLHeadElement("textToInsert")
in views orarguments.eventContext.addHTMLHeadElement("textToInsert")
in places where there is access to the EventContext to have the cache replay. - Cache will not replay headers set via
<cfheader>
. UseaddHTTPHeader(name="some", value="thing", charset="utf-8")
,addHTTPHeader(statuscode="500", statustext="Some Text")
,addHTTPHeaderByName(name, value)
oraddHTTPHeaderByStatus(statuscode, statustext)
in views or same method names in the EventContext to have the cache replay.
Special thanks to Matt Williams (mgw4jc@…) for contributing the initial version of this wiki entry.