First of all, Coldbox event caching is awesome. The ability to cache the output of an entire event can make a Coldbox app extremely fast. However, one caveat/tradeoff of event caching is that Coldbox uses the rc scope to determine the cache key, instead of the event name. This gives users the flexibility to create different event caches for pages like:
/blog/?postId=1
/blog/?postId=2
While this is beneficial for pages that utilize rc scope to display different output, it does make it difficult to cache pages if you do not want the rc scope to influence whether a cached copy of an event should be returned or not.
For example, letās say you cache the home page of your app to make sure it loads extremely fast. You may not be able to control whether site visitors access the home page through a āpureā URL with no parameters. If the visitor arrived by clicking a link from an email marketing campaign, there might be a series of tracking variables like ?utm_source appended to the URL. Depending on the situation, you could have every site visitor create a new cached copy of the event. Additionally, a malicious user could hit a page with an infinite number of URL variables which would create new cached instances of the output on every request.
Is there a way to tell Coldbox to cache an event and ignore the rc scope? If not, this might be a valuable feature, especially for static pages like a home page or marketing landing pages.
At this point it does not. It always looks for the rc context hash to incorporate it for unique cacheable items. This is done by the EventURLFacade object called from the request service. The tricky thing, is that even if you turn it off, you might want it ON for other events. So maybe if this was a feature request, it would have to be at the event action level via another metadata annotation. Right now we have the following available annotations:
cache - Turn on or off
cacheTimeout
cacheLastAccessTimeout
cacheProvider - which provider to use, default to template
We could add something like:
cacheIgnoreRC : Defaults to false
Then only if that is set to true, will it ignore it. However, we then need to modify request services, url facades and then create tests cases for those
If you are willing to take this on with me. We can do it!
Sure thing! I think it would be a great addition to Coldbox. Iām fairly confident I can handle adding the metadata annotation and request service changes. However, I will probably need some help from the Chuck Norris of TestBox in order to write the right unit tests for it.
I seem to be stuck. Where in the Coldbox code does the handler metadata get parsed out? Iād like to allow for cacheIgnoreRc to be a metadata value as @lmajano indicated in his post.
Here are my changes thus far to runEvent() in system\web\Controller.cfc
function runEvent(
event = "",
boolean prePostExempt = false,
boolean private = false,
boolean defaultEvent = false,
struct eventArguments = {},
boolean cache = false,
cacheTimeout = "",
cacheLastAccessTimeout = "",
cacheSuffix = "",
cacheProvider = "template",
cacheIgnoreRc = false // added this argument
){
...
if ( isCachingOn ) {
// Build cache references
var oCache = variables.cachebox.getCache( arguments.cacheProvider );
var oEventURLFacade = oCache.getEventURLFacade();
var cacheKey = oEventURLFacade.buildBasicCacheKey(
keySuffix = arguments.cacheSuffix,
targetEvent = arguments.event
// check if we should ignore the RC scope
) & ( !arguments.cacheIgnoreRc ? hash( arguments.eventArguments.toString() ) : '' );
...
I believe the above changes should allow for ignoring the RC scope when building the cache key. However, Iām not sure how I can allow the new metadata value of cacheIgnoreRc to be parsed and passed through to `runEvent()
I wrote some tests in EventCaching.cfc which will check that:
ā¦ it can handle different RC collections. (passes)
ā¦ it can ignore the RC scope. (partial passing)
I have successfully implemented the metadata detection in the handler action and that part of my tests pass (wohoo!). However, the actual cacheKey is giving me problems because it still takes the RC into consideration.
I think I need to make a change in EventUrlFacade.cfc, specifically with the methods buildEventKey() or buildEventKeyNoContext() (Iām not sure what the difference is between the two). However, I donāt see where the RC gets considered at all at this point in the request flow.
Does that make sense? If it would be easier, I would be happy to chat on Slack or something if writing out an explanation would take too long.
Thanks for your help! I think once this gets wrapped up, it will be a big performance booster for Coldbox.
Just wanted to see if anyone could lend a hand on this or at least point me in the right direction? I am still stuck trying to create this new enhancement for Coldbox.
Update:
Iāve made significant progress on the caching feature and I have a working dummy app that Iām fine-tuning. I have successfully been able to get Coldbox to ignore the RC scope via the cacheIgnoreRc annotation when building the cache key.
I realized after creating the new feature, that many users may actually want to consider one or more rc variables when building the cache key while ignoring others, similarly to how the Wirebox Populator has include/exclude options available to them.
I was thinking we could change the original annotation from cacheIgnoreRc to cacheIncludeRcKeys which would be a list of keys to specifically include when building the cache key. When the annotation is omitted, it would include the entire rc scope (like the Wirebox populator), however, if a blank value was passed, it would ignore the complete RC scope, and if a list is passed, it would consider just the included keys, if present.
The end result would be a backward-compatible feature that gives developers more control over how the cache keys are generated for their handlers.
Hereās how it might look when implemented:
function show( event, rc, prc )
cache = "true"
cacheTimeout = "10"
cacheIncludeRcKeys = "slug" // considers just rc.slug when building cache key
{ ... }
function show( event, rc, prc )
cache = "true"
cacheTimeout = "10"
cacheIncludeRcKeys = "" // ignores the rc scope when building cache key
{ ... }
function show( event, rc, prc )
cache = "true"
cacheTimeout = "10"
// omitted: works just like it does now by considering the entire rc scope
{ ... }
Any thoughts? Iāll continue developing in the meantime.