[Coldbox 6.4] Event Caching and Ignoring RC Scope?

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 :slight_smile:

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.

Here are some pointers.

Here are some standalone suites I use to test specific scenarios

Event Caching Suite:

1 Like

Thank you for the links to the tests! I’ll set aside some time this weekend to start playing with it.

This ticket might be relevant for a more general discussion: [COLDBOX-847] Coldbox Template Caching for the Future - Welcome

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()

@lmajano I made some progress…

Here’s the link to my fork with the changes:

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. :slight_smile:

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.