cachedWithin in function

hello everyone,
I don’t understand why cachedWithin in the function DOES NOT work, but it does work in queryExecute.

What is the best way to make it work within functions as well?

Here’s an example: TryCF.com

Do I need to use variables (for example cache_time) to disable the cache createTimeSpan(0,0,0,0); in a local development environment, but I haven’t yet found the best way to do this for functions.

Did you read the error message on yer own example code?

The error message that explains what’s wrong and how to fix it? That might be a good place to start, anyhow.

Now why Lucee works that way is another thing. But… shrug…

Hello @AdamCameron,
Yes, I have read the error message but I don’t have a solution, and that’s why I posted here.

I have tried differently, but without success:


<cfscript>

cache_days = 1; 

function foo(args) cachedwithin=createTimeSpan(cache_days,0,0,0){
    return args;
}

writeDump( foo('test') );

</cfscript>

I’m not very familiar with Java, but I’ve observed that for functions, they define:

private Literal cachedWithin;

Whereas for StoredProc, fileTag, http, query

the approach is:

private Object cachedWithin;

Is there a specific rationale for this distinction in handling functions, or could the same methodology be applied there too?

OK - I’ll try to be more explicit - so if you run your code, you get this error:

Paremeters of the function createTimeSpan have to be literal numeric values in this context

What does that mean?

Using your code as an example:

cache_days = 1; 

createTimeSpan(cache_days,0,0,0)

1 is a literal
cache_days is not a literal. it’s a variable.

So when it says you need to use literals, you need to actually - literally :wink: - use “hard-coded” values.

Why? Fuck knows, beyond “unhelpful implementation”. It looks to me - without looking at the source as I don’t want to entirely lose my will to live - that the timespan value is used at compile time, and they’re doing some jiggery-pokery to extract the createTimeSpan call (although that ought to be a runtime thing too), but can’t - for what are hopefully obvious reasons - can’t resolve a variable as that can only be done at runtime.

There shouldn’t be such a constraint as you allude to above, but… well… here we all are.

Make sense now?

1 Like

@AdamCameron
Yes, I’ve grasped the issue from the start.

What I’m trying to figure out is:

  • Is there a workaround to use cachedWithin in functions the same way it’s used in queries/cfhttp/etc., utilizing a variable instead of a literal value? (I assume not, but one never knows…)
  • Is this behavior intended only for functions? Perhaps someone from the Lucee team could shed light on this. At this point, I’m inclined to think this behavior is deliberate, as the same mechanics apply to ACF as well.

Yes, I’ve grasped the issue from the start.

Really? Hrm. OK.

Due to the nature of the situation, the answers are:

  • no - it needs to be compile time, so you need to use literal values. That’s the long and the short of it.
  • yes - they’ve even documented it thus, so it’s a deliberate thing.

In your situation, I’d dispense with the Lucee implementation (which seems half-baked. And poss using the wrong recipe), and write a caching decorator and DIY.

Where are you seeing that CF supports this? It’s not on cffunction…? (This does not necessarily mean anything, as they’re patchy with keeping their docs up to date).

From my testing, whilst CF will complain on this:

<cffunction name="f" cachedwithin="#createTimespan(1,0,0,0)#">
Expression at line 1 has to be constant value

It doesn’t actually do anything if one uses a literal instead:

<cffunction name="f" cachedwithin="1">
    <cfreturn now()>
</cffunction>


<cfoutput>#f()#</cfoutput>
<br>

<cfset sleep(2000)>
<cfoutput>#f()#</cfoutput>
<br>

<cfset sleep(2000)>
<cfoutput>#f()#</cfoutput>
<br>

{ts '2024-02-19 11:51:55'}
{ts '2024-02-19 11:51:57'}
{ts '2024-02-19 11:51:59'}

(TryCF.com)

(Lucee works as expected here).

So fuck knows what CF is doing. Not relevant to this thread though, I think.

1 Like

Really? Hrm. OK.

:rofl:

and write a caching decorator and DIY.

Ok, thank you. I’ll try to figure out how to do it.

If you need some help with the notion of decorators, sing out.

If yer au fait with PHP, there’s a basic caching example as part of this: Adam Cameron's Dev Blog: Using a decorator pattern to reduce inappropriate code complexity

It’s for DB-call-caching, but it’s the decoration technique rather than what’s being decorated that is the main thing here.

1 Like

Late to the party, but… I’ve never been a big fan of cachedWithin on anything (http requests, functions, queries) simply because its a giant PITA to clear those caches (having to pass 0 timespan to clear).

I instead lean on cacheGet(), cachePut() and cacheRemove(), when I’m not using something like CacheBox to handle caching.

function doSomething( required struct data, boolean clearCache = false, numeric cacheDays = 90 ) {

    // get the data from the cache
    var myResult = cacheGet( 'myResult' );

    // check if we have data in the cache, or are forcing a regeneration of the data
    if( isNull( myResult ) || arguments.clearCache ) {

         // meat and potatoes of my function to generate the output from the input (arguments.data)
        myResult = //whatever result the function should return;

        // cache the result for later use
        cachePut( 'myResult', myResult, createTimeSpan( arguments.cacheDays, 0, 0, 0 ) );

    }

    // return myResult (either the cached version, if available, or the regenerated data from the function)
    return myResult;

}

Generally speaking I have a decorator CFC that I use (similar to what Adam shows in his blog post) that has get(), set() and clear() functions, along with some other rudimentary cache management stuff so the CFC gets autowired to the component and I can make use of the functions inside the CFC instead of the native caching functions. This also lets me replace this CFC with CacheBox without a whole lot of rework and allows different caching engines to be implemented (redis, memached, etc.)

But at the end of the day the pattern is the same… get your data from cache, check if you have it or need to/want to regenerate it, put it in the cache and return the result. Either way myResult has data - either from the cache or generated during that call.

Pattern works everywhere you would use cachedWithin - you’re simply caching the result of the function (or http call or query or whatever) in a roll your own fashion vs. depending on cachedWithin and all its idiosyncrasies.

HTH

– Denny

1 Like

Is it really ?

componentCacheClear();
ctCacheClear();
cfobjectcache( type="function"  action="clear" );
cfobjectcache( type="include"   action="clear" );
systemCacheClear();

Those are great functions to clear the entire cache, but for clearing the specific cache item (in the OPs case a function) using cachedWithin… yes, it is a giant PITA.

If everything looks like a nail, then hammers are great… but for precision control over individual cache items… I’ve yet to find anything that beats the pattern I have shown.

Edit: By ‘those caches’ in my original response, I should have said those items cached with cachedWithin - which would would have clarified that I was specifically talking about clearing any one specific cache item that uses the cachedWithin attribute vs. clearing the entire cache.

HTH

– Denny