I am attempting to control caching on component fu...
# lucee
r
I am attempting to control caching on component functions, in Lucee latest, utilizing
cachedWithin
directive by wanting to use an application or session scoped variable that is utilized by specific functions. Any change in the variable will then make a sweeping change to all functions utilizing cachedWithin with this variable. I even attempted to set a variable inside like so
cachedWithin = createTimeSpan(0,0,variableHere,0)
, but every attempt leads to an error needing a literal value. Is there any way to create a cache on functions with a variable? Is there a reason why we cannot use a variable on a
cachedWithin
directive?
w
fwiw, in the past i've chosen to forego the builtin cache methods and instead use something like ehCache. you have far more control over key names and if you have a solid naming strategy you can even purge a whole slew of related keys in one go, such as prefixing "user_#userid#_"to cached values for that user, then you can invalidate any cached values by iterating keys and deleting anything with that prefix. i stopped using cachedWithin and its ilk a while ago in favor of the above approach, with no regrets
if i'm reading what you wrote correctly, it's not that different from what you're trying to achieve
r
Thanks, @websolete. I will look into using ehCache.
Still curious about why a variable would not be allowed to store a numeric value for a CacheWithin directive, however. I even tried wrapping a variable in pound signs wrapped in single quotes, but the issue remained.
w
not clear either on that. did you try to create the timespan and THEN assigning the timespan to cachedWithin?
could you paste the code that fails? maybe it's just a language parser issue and would work if expressed differently
r
yes, I tried the following. Keep in mind that any time you see CachedWithin, it is in a component function as a directive:
Copy code
timevar = createTimeSpan(0,0,1,0);
cachedWithin = timevar
Copy code
timevar = val(createTimeSpan(0,0,1,0));
cachedWithin = timevar
Copy code
timevar = 1;
cachedWithin = timevar
Copy code
timevar = 1;
cachedWithin = createTimeSpan(0, 0, timevar, 0)
Copy code
timevar = createTimeSpan(0,0,1,0);
cachedWithin = '#timevar#'
Copy code
timevar = val(createTimeSpan(0,0,1,0));
cachedWithin = '#timevar#'
The following is the error:
Copy code
value of cachedWithin must be a literal value (string,boolean,number), a timespan can be defined as follows: 0.1 or createTimespan(1,2,3,4)
w
and all of those fail?
r
yep, every single one, because a variable is not allowed
w
wtf
how about:
timeVar = evaluate("createTimeSpan(0,0,someVar,0)")
out of sheer morbid curiosity?
r
oh yeah, tried that too
lol
w
and it complained?
r
yes
w
lucee AND acf complain or just one or the other?
r
only tested in Lucee. I do remember doing a similar thing many years ago in ACF, but it was for queries, not functions.
w
can you get this code closer to your actual code, since this appears to work in this simple example: https://trycf.com/gist/2918778eb749e5b18ee45dbf11165004/lucee5?theme=monokai
r
yeah, that will work because it is not using the cachedWithin directive.
createTimeSpan
works fine. Let me see if I can make my scenario in there. One moment, please
w
seems like a bug
r
that's what it feels like 🙂
However, the error message seems accurate regarding the need for a literal value.
w
having said that though, it seems what it doesn't like is the method signature itself is 'dynamic'
so it can't compile it approriately
r
hmm that's interesting
w
so if that's the case, that the method signature can't be dynamic, then you'd have to find an alternate way to manage its caching behavior
never done it myself, but have you tried creating a runtime function definition and injecting it into a component? thought i'd seen something about doing such things
r
I haven't tried that
w
one example on this page would suggest that you may be able to pull it off by wrapping the entire function definition in evaluate(), but holy shit that sounds gross as hell: https://docs.lucee.org/guides/cookbooks/Hidden_gems.html
r
HAHA wow, I agree with your sentiment!
that makes sense though that it would basically be a way to do it
the function basically becomes a "literal" entirely. lol
w
feels like it should be possible to achieve a similar result by way of custom tags, but also seems dirty
MUST your cachedwithin timespan really be dynamic?
heh, the thick plottens. you may not actually need to control cachedWithin dynamically like you're trying. if the current time is within the timespan of cachedwithin AND THE ARGUMENTS ARE THE SAME VALUES, then it will use the cached values. otherwise it won't. run this and let it run for about ten seconds. first two values will be the same, third will not, since the cache timespan is 7 seconds, but the third dump doesn't fire until ten secons: https://trycf.com/gist/8c93742039f97aa0bdae8ec6a9073e0e/lucee5?theme=monokai
change the timespan to 15 seconds and you'll get the same result
which is expected
for all intents and purposes it's using the argument values collectively as the 'name' of the cached value
i use a similar approach with ehCache as i mentioned before, where i have a function that creates a cache key name based upon argument values (simple values)
r
I see, thanks for the example. I think this is the same as if the
cacheWithin = 'request'
if I'm not mistakened
If any changes occur in the arguments, then it will cache again
I don't have any arguments that will change in this function. The function is actually used to call an API.
My intent is to cache the content from the API
However, based on what your explaining, I could basically create a sort of hack alternative by passing in a dummy parameter that would essentially force the function to cache again. That might actually work 🙂
I do think the better solution might be using what you originally suggeste with ehCache, though
w
if the method doesn't have arguments, then you could still force it to refresh by passing an argument when you want, such as
myFunc( cacheBuster = getticcount() )
when you need to force a refresh
r
I keep thinking about the hack solution and that would become a bit cumbersome having to pass parameters into a function whenever I need to force a new cache again.
it is a good alternative, but I like your original ehCache idea as it is a cleaner way of maintaining caches as it is intended to be
Thank you for going down this hole with me, @websolete! 🙂
w
yes i think so. you could always do a 'poor man's' cachedWithin but there are some requirements (singleton, using the variables scope to maintain 'state', etc.) that seem like a poor substitute for a 'real' caching solution like ehCache or redis
last thing and then i'll shut up. another variation of cache busting by way of arguments would be to have that single argument itself by the 'name' of the cached item. e.g.:
Copy code
function myApiMethod( string cacheAs = "default" ) cachedWithin=createTimeSpan(0,1,0,0) {
	return whatever; 
}
then when you're calling it you pass in the 'name' as the cacheAs value:
Copy code
blah = myApiMethod();
blech = myApiMethod( cacheAs="user_#userid#_api_results" );
ahem = myApiMethod( cacheAs="account_#accountid#_api_results" );

blooie = myApiMethod( cacheAs="user_#userid#_api_results" ); <--- will return the cached copy from above within an hour
i still like ehCache though, seems more fitting
r
yeah, that make sense and agree it could be used, but ehCache is more appropriate so we don't have to play around to make it work. Thanks, again! 🙂
m
createTimespan() just returns a number where 1 = 1 day, provide cachedwithin with variableHere, but make variableHere contain the numeric value representing the time you want cached
r
Hi Matt, please take a look at the examples above and you will see the attempts we made that you also mention do not work.
m
I likely focused too much on the original code colored portion, we definitely have used variables with cachedWithin in queries, but i doubt we had ever made any attempt using it outside of queries. I do see the same error for functions.
r
Yeah, thanks for confirming on both the error with the function and that you also used variables on query cacheWithin. It would seem that there should be consistency for any cacheWithin derivative available for use across all of Lucee's functionalities. Maybe this might be a bug for functions, but I'm not quite sure at this moment.