tkem/cachetools

[Bug] TTLCache.expire(time) seems to be non-deterministic

stephen-aruba opened this issue · 7 comments

Versions

Succeeds
MacOS 10.15.7
Python 3.7, 3.8, 3.9
CacheTools v4.2.2

Unpredictable (results vary between runs)
Ubuntu 20.04
Python 3.7, 3.8, 3.9
CacheTools v4.2.2

Reproduce

from datetime import datetime
from cachetools import TTLCache, cached

time_cache = TTLCache(maxsize=200, ttl=300)

@cached(time_cache)
def get_time(uuid: str) -> str:
    now = datetime.now()
    return now.strftime("%X.%f")

# return function response    
print(get_time("key"))    

# return cached result    
print(get_time("key"))  

# remove expired items from the cache, these should then be re-requested    
time_cache.expire(time=500)

# return function response    
print(get_time("key"))

Calling time_cache.expire(time=500) does not seem to expire items in the cache.

Prints:

<timestamp1>
<timestamp1>
<timestamp1>

Expected Behaviour

Calling time_cache.expire(time=500) should expire items in the cache, causing them to be re-requested.

Prints:

<timestamp1>
<timestamp1>
<timestamp2>  # new timestamp should be requested

Demo

Live demo (see Github Actions)

tkem commented

Why time_cache.expire(time=500)?

AFAICS you don't set a custom timer for your cache, so TTLCache uses time.monotonic() as it's timer function:
https://cachetools.readthedocs.io/en/stable/#cachetools.TTLCache

So you should pass the value of time.monotonic() at cache insertion time + specified ttl to time_cache.expire().

tkem commented

Better yet, since the timer is also a property of the cache

time_cache.expire(time_cache.timer() + 500)

Thanks for the suggestions, I think I misunderstood the operation of time.monotonic() on different platforms (on MacOS it seems to reset to zero at script initiation, I assumed this applied cross-platform).
I'll run your suggestions against my tests now and let you know...

tkem commented

Yeah, as you probably already found out, time.monotonic may count from any time point, so the actual value is pretty meaningless: https://docs.python.org/3/library/time.html#time.monotonic
I think it counts from last reboot (Linux uptime) on my machine, but I'm not quite sure, either.

tkem commented

However, extra kudos for your efforts, like creating a repo and actions for this - i I think this may be the best documented bug report I've received so far ;-)

Thanks very much for the suggestions. Both of them worked on Ubuntu and MacOS. I wan't able to verify for Windows (thus the failing check-marks above), and I suspect it would work on Windows too,but this solves my issues. Thank you 🙂

Thanks! Github actions was the easiest way to test cross-python and cross-platform on my side once I discovered behaviour differed between systems. Thank you for your quick response too - you've saved me some tech-debt on my side 🏅