Module 15 was about not doing the wrong thing. Module 16 starts with doing the right thing faster: the cheapest performance win in any application is skipping work you've already done once. ColdFusion gives you three different layers to do that at, query results, arbitrary objects, and whole rendered pages, and each one fits a different situation.
Learning Objectives
After completing this lesson, you'll be able to:
- Cache a query's results with cachedwithin and cachedafter.
- Cache an arbitrary value with cachePut(), cacheGet(), and cacheRemove().
- Cache a whole page or page fragment with cfcache.
- Use cacheRegionNew(), cacheIdExists(), cacheRemoveAll(), and cacheGetAllIds() to manage cache regions beyond basic put/get.
- Know that ORM has its own separate caching layer, distinct from cfcache and cachePut().
- Know a real naming difference between Adobe ColdFusion and Lucee for the same caching feature.
How Caching Fits Together
Request Comes In
same query, object, or page as before
Check the Cache First
cachedwithin, cacheGet(), cfcache
Miss? Do the Real Work
run the query, build the page
Store It, Return the Result
next request gets the fast path
Four Layers of Caching
| Layer | Tool | Caches |
|---|---|---|
| Query caching | cachedwithin / cachedafter on cfquery | A single query's result set |
| Object caching | cachePut() / cacheGet() / cacheRemove() | Any value: a struct, a computed result, an API response |
| Page caching | cfcache | An entire page or page fragment's rendered output |
| ORM caching | ORMExecuteQuery(cacheable=true), ORMEvictEntity() | Entities and queries loaded through ColdFusion's ORM |
Query Caching
cachedwithin and cachedafter both reuse a previous result instead of re-running the query, they just measure differently. A cached result is only reused if the SQL statement, datasource, query name, and credentials all match exactly, change any of those and it's a fresh query.
| Attribute | Type | Meaning |
|---|---|---|
| cachedwithin | Timespan (createTimeSpan()) | Reuse the result if it was run within this duration from now |
| cachedafter | Date | Reuse the result if it was run after this specific date |
<cfquery name="news" datasource="myDSN" cachedwithin="#createTimeSpan(0, 1, 0, 0)#">
SELECT id, title FROM news
</cfquery>Query Caching also has to be enabled in the ColdFusion Administrator before either attribute takes effect.
Object Caching
cachePut()/cacheGet()/cacheRemove() cache anything, not just query results, a struct, an expensive computed value, a parsed API response.
// store, with a 30-minute expiry
cachePut("homepage_stats", statsStruct, createTimeSpan(0, 0, 30, 0));
// retrieve, checking for a miss
cachedStats = cacheGet("homepage_stats");
if (isNull(cachedStats)) {
cachedStats = buildStatsStruct();
cachePut("homepage_stats", cachedStats, createTimeSpan(0, 0, 30, 0));
}
// remove early, e.g. after the underlying data changes
cacheRemove("homepage_stats");cacheGet() returns null on a miss, isNull() is the standard way to check for that before using the result.
More Cache Functions Worth Knowing
cachePut()/cacheGet()/cacheRemove() cover the common case, but the full cache-functions reference has more, including the one that actually creates a named region in the first place.
| Function | Does |
|---|---|
| cacheRegionNew(region [, properties, throwOnError]) | Creates a named cache region. Adobe's own example calls this before the first cachePut() into that region, rather than relying on cachePut() to create it implicitly |
| cacheIdExists(id [, region]) | Returns true/false for whether a key exists, cleaner than cacheGet() plus isNull() when you only need to check |
| cacheRemoveAll([region]) | Clears every object in a region (or the default region) at once |
| cacheGetAllIds([filter, cacheName, isAccurate]) | Lists every key currently cached, useful for debugging or building admin tooling |
ORM Has Its Own Separate Caching Layer
If an application loads data through ColdFusion's ORM (covered back in Module 8), that's a fourth caching layer entirely, separate from cfcache and cachePut(). ORM caching works at two levels: a session-level cache that holds entities only for the current ORM session (EntityReload() forces a fresh database hit), and a secondary-level cache that persists across sessions, the one actually worth configuring deliberately.
// cache this query's results at the secondary level
availableArts = ORMExecuteQuery(
"from CArt where issold = 0",
{},
false,
{ cacheable: true, cachename: "availableArtsQuery" }
);
// evict explicitly once the underlying data changes
ORMEvictEntity("CArt");
ORMEvictQueries("availableArtsQuery");ORM caching defaults to Ehcache too, but it's pluggable, Adobe's docs also list JBossCache, OSCache, SwarmCache, and Tangosol Coherence Cache as supported secondary cache providers.
A Real Detail: Same Feature, Different Parameter Name
Both engines support named cache regions, separate pools so unrelated cached data doesn't collide or get flushed together, but the parameter is named differently. Adobe ColdFusion's cachePut() calls it region, Lucee's calls the equivalent parameter cacheName (with region accepted as an alias). Writing cross-engine code that uses named regions is one more place worth double-checking against the target engine's own docs.
Page-Level Caching with cfcache
cfcache caches rendered output instead of data, an entire page, or just the content between its tags.
| action | Does |
|---|---|
| cache (or optimal) | Server-side and client-side caching together |
| serverCache | Server-side only |
| clientCache | Browser-side only |
| flush | Clears cached pages, optionally scoped with expireURL's wildcard pattern |
| get / put | Reads or writes the object cache directly, the same cache cachePut()/cacheGet() use |
<cfcache action="cache" timespan="#createTimeSpan(1, 0, 0, 0)#" idletime="#createTimeSpan(0, 12, 0, 0)#">
<div id="daily-deals"><!--- expensive content to render --></div>
</cfcache>Adobe ColdFusion's caching (query, object, and cfcache alike) runs on Ehcache under the hood, configured through ehcache.xml in the ColdFusion root's lib directory. Lucee also supports a content action on cfcache, caching only the tag's body rather than a complete template.
Common Beginner Mistakes
Assuming cachedwithin keeps working after the query's SQL changes slightly
The cache key depends on the exact SQL statement matching. Even a whitespace-only change to the query text means the next call is a cache miss, not reused.
Forgetting to enable Query Caching in the ColdFusion Administrator
cachedwithin/cachedafter on a cfquery silently do nothing (always running fresh) if that setting isn't enabled server-wide.
Not checking isNull() after cacheGet()
A cache miss returns null, not an error. Code that uses the result directly without checking fails or behaves incorrectly on a miss instead of falling back to computing the value.
Assuming cachePut() with a new region name creates that region automatically
Adobe's own documented pattern calls cacheRegionNew() explicitly before the first cachePut() into a new region. Relying on implicit creation isn't the documented behavior.
Not realizing ORM-loaded entities use a completely separate cache from cachePut()
Clearing the object cache with cacheRemove() or cacheRemoveAll() has no effect on ORM's own session-level or secondary-level cache. That needs EntityReload() or ORMEvictEntity()/ORMEvictQueries() instead.
Caching a page with cfcache that includes user-specific content
Page-level caching serves the same cached output to every visitor during the cache window. Content that differs per logged-in user shouldn't be wrapped in cfcache without accounting for that.
Best Practices
- Match the caching layer to the problem: query results with cachedwithin, arbitrary values with cachePut(), whole rendered output with cfcache.
- Always check isNull() on a cacheGet() result before using it.
- Use named cache regions to keep unrelated cached data from being flushed together, checking the correct parameter name (region vs cacheName) for the target engine.
- Call cacheRemove() explicitly when the underlying data changes, rather than waiting out a long timespan.
Interview Questions
What's the difference between cachedwithin and cachedafter on a cfquery?
cachedwithin is a relative timespan measured back from now, cachedafter is an absolute date. Both reuse a cached result only if the SQL, datasource, query name, and credentials all match exactly.
What does cacheGet() return on a cache miss, and how should that be handled?
It returns null. The standard pattern is checking isNull() on the result, and if true, computing the value and storing it with cachePut() for next time.
Why might a cross-engine ColdFusion/Lucee codebase need to pass a different parameter name into cachePut() for named cache regions?
Adobe ColdFusion names that parameter region, Lucee names it cacheName (though it accepts region as an alias). The feature is equivalent, the parameter name isn't guaranteed to be.
Summary
In this lesson, you cached a query's result with cachedwithin/cachedafter, cached an arbitrary value with cachePut()/cacheGet()/cacheRemove(), managed cache regions with cacheRegionNew()/cacheIdExists()/cacheRemoveAll(), cached rendered page output with cfcache, saw that ORM has its own separate caching layer entirely, and saw a real naming difference between Adobe ColdFusion and Lucee for named cache regions.
What's Next?
The next lesson covers broader performance tips: where else to look once caching alone isn't enough.
- Adobe — cfcache Reference ↗
- cfdocs.org — cfcache ↗
- cfdocs.org — cfquery ↗
- cfdocs.org — cachePut ↗
- cfdocs.org — cacheGet ↗
- cfdocs.org — cacheRemove ↗
- cfdocs.org — Cache Functions Index ↗
- cfdocs.org — cacheRegionNew ↗
- cfdocs.org — cacheIdExists ↗
- cfdocs.org — cacheRemoveAll ↗
- cfdocs.org — cacheGetAllIds ↗
- Adobe — Cache Functions Reference ↗
- Adobe — ORM Caching ↗
- Lucee Docs — cachePut ↗
- Lucee Docs — cache (tag) ↗