iamsinghrajat/async-cache

Add option to ignore some parameters when making key

PaulRudin opened this issue · 3 comments

There are some occasions when it's useful to ignore some parameters for the purpose of determining a key. For example if you're caching a method, it might be that self is irrelevant to the desired caching behaviour.

from cache import AsyncLRU
import asyncio
import time

class ClassFn:
    @AsyncLRU(maxsize=128)
    async def obj_f(self, wait: int):
        await asyncio.sleep(wait)

    @classmethod
    @AsyncLRU(maxsize=128)
    async def class_f(cls, wait: int):
        await asyncio.sleep(wait)


def test_obj_fn():
    t1 = time.time()
    obj = ClassFn()
    asyncio.get_event_loop().run_until_complete(obj.obj_f(4))
    t2 = time.time()
    asyncio.get_event_loop().run_until_complete(obj.obj_f(4))
    t3 = time.time()
    t_first_exec = (t2 - t1) * 1000
    t_second_exec = (t3 - t2) * 1000
    print(t_first_exec)
    print(t_second_exec)
    assert t_first_exec > 4000
    assert t_second_exec < 4000


def test_class_fn():
    t1 = time.time()
    asyncio.get_event_loop().run_until_complete(ClassFn.class_f(4))
    t2 = time.time()
    asyncio.get_event_loop().run_until_complete(ClassFn.class_f(4))
    t3 = time.time()
    t_first_exec = (t2 - t1) * 1000
    t_second_exec = (t3 - t2) * 1000
    print(t_first_exec)
    print(t_second_exec)
    assert t_first_exec > 4000
    assert t_second_exec < 4000

if __name__ == "__main__":
    test_obj_fn()
    test_class_fn()

@PaulRudin Used this script to verify that caching works even with functions with self and cls arguments. Can you add any other case where ignoring a function argument is required. Thanks!

The issue isn't that the caching fails. Rather there are occasions on which it would be convenient for some arguments (let's say just the first for now) are ignored when making the key (the thing about self is just an possible example). So we'd get a cache hit when a method was called from different instances (i.e. different objects bound to self).

aiocache has this kind of thing specifically for self: https://aiocache.readthedocs.io/en/latest/decorators.html#cached (see noself) but it doesn't really need to be limited to the special case of self.

So we might have something along the lines of:

@AsyncLRU(skip_args=1)
def foo(*args, **kwarg):
   ...

And the effect of this would be that the key is computed using args[1:] rather than args

@PaulRudin Thanks for suggestion. Added skip args option for AsyncTTL.

@AsyncTTL(maxsize=128, time_to_live=None, skip_args=1)
    async def skip_arg_func(arg: int, wait: int):
        await asyncio.sleep(wait)

Available in async-cache 1.1.1