pallets-eco/flask-caching

_memver key entry for @cache.memoized call tracks TTL but probably shouldn't?

NotBrandon opened this issue · 0 comments

Issue

I've noticed the timeout param is not used how I'd expect when using @cache.memoized and am wondering whether this is a misunderstanding on my part, or whether a nasty bug has been in this library for potentially years. The issue is that the TTL is applied to both the cache entry for the function call and for the memoized version identifier entry, the end result being that cached values are being cleared far more often than they should and becoming inaccessible before the TTL expiration.

Example

@cache.memoized(timeout=60, args_to_ignore='self')
def my_function(param):
    return param

If I were to call my_function(param="icecream") just once, I'd have two entries in my Redis cache that would look something like this:

  1. "_flaskCachingnamespace.my_function_memver" = "ABCD"
  2. "_flaskCachingDEFGHIJABCD" = "cats"

The issue is that both of these entries have the TTL set to 60.

If I call the function again, with a different parameter value, at time=59 such as `my_function(param="waffles")'... this second function call is only going to be cached for 1 second. Not 60 like I'd expect.

The reason is because at time=59, the entry for memver still exists, and so an entry with our second call is created using the original memver value.

  1. "_flaskCachingKLMNOPABCD" = "dogs"

At time=60, entry number 1 is removed (memver). Now if you try to call my_function(param="waffles") at time=61.. it's not going to use the cached value created 2 seconds earlier because no _memver entry exists for this function. It'll instead create a new _memver entry along with a new entry for the cached value.

Expected Behavior

I wouldn't expect the _memoize_version to care about the TTL. It already has both 'reset' and 'delete' parameters to clear that entry from the cache or reset it for invalidation purposes. Applying a TTL to it has an effect on functions cached later, which I don't think is the intent.

Actual Behavior

All cached values for a function call are invalidated after the timeout of the first entry. In other words, while I'd expect the results of each individual function call to be good for 60 seconds instead we have all values getting wiped every 60 seconds regardless of how recently they were created.

Fix Thoughts

If my expectations are correct, I think this could be fixed by simply dropping the timeout param from the call to set inside _memoize_version. Alternatively, the TTL on the _memver entry would be reset after each a new function call is cached. I have no problem opening a PR for either solution if desired.

Here's the specific line of code in question:

dict(zip(fetch_keys, version_data_list)), timeout=timeout

Environment:

  • Python version: 3.11.4
  • Flask-Caching version: 2.0.2