Last active
November 15, 2022 00:22
-
-
Save xkstein/868036348e3fff2a01c57bba448a0f2b to your computer and use it in GitHub Desktop.
Little redis caching util
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| '''Two simple examples of redis caching | |
| Includes: | |
| - Simple function-type decorator | |
| - Class with method for time dependent caching | |
| They both make entries in redis with key [function name] + [bound args] | |
| ''' | |
| from time import sleep, time | |
| from functools import wraps, partial | |
| from inspect import signature | |
| import pickle | |
| import redis | |
| class Cache: | |
| '''Util for caching function outputs to redis | |
| Decorater which caches function output to the passed redis instance. | |
| Includes method for only caching output for a given duration of time. | |
| Args: | |
| redis_client: redis object to cache to | |
| Example: | |
| cache = Cache(r) | |
| @cache | |
| def results_cached(): | |
| ... | |
| @cache.for_time(duration=15) | |
| def results_cached_for_duration(): | |
| ... | |
| ''' | |
| def __init__(self, redis_client): | |
| self.redis_client = redis_client | |
| def _store(self, key, val, duration=None): | |
| if duration: | |
| self.redis_client.setex(key, duration, val) | |
| return | |
| self.redis_client.set(key, val) | |
| def __call__(self, func, duration=None): | |
| @wraps(func) | |
| def wrapper(*args, **kwargs): | |
| bound_args = signature(func).bind(*args, **kwargs) | |
| bound_args.apply_defaults() | |
| key = func.__qualname__ + str(bound_args.arguments) | |
| if not self.redis_client.exists(key): | |
| val = func(*args, **kwargs) | |
| self._store(key, pickle.dumps(val), duration=duration) | |
| return val | |
| # Sort of a nasty work around if redis is set to decode_response | |
| try: | |
| val = self.redis_client.get(key) | |
| except UnicodeDecodeError as e: | |
| val = e.object | |
| return pickle.loads(val) | |
| return wrapper | |
| def for_time(self, duration: int): | |
| '''Decorator that caches function output for duration''' | |
| assert isinstance(duration, int) and duration > 0,\ | |
| 'The indicated duration has to be a positive integer' | |
| return partial(self, duration=duration) | |
| def cache_simple(func): | |
| '''Simple redis caching, without any time stuff''' | |
| @wraps(func) | |
| def wrapper(*args, **kwargs): | |
| bound_args = signature(func).bind(*args, **kwargs) | |
| bound_args.apply_defaults() | |
| key = func.__qualname__ + str(bound_args.arguments) | |
| if not r.exists(key): | |
| val = func(*args, **kwargs) | |
| r.set(key, pickle.dumps(val)) | |
| return val | |
| return pickle.loads(r.get(key)) | |
| return wrapper | |
| r = redis.Redis() | |
| cache = Cache(r) | |
| @cache.for_time(1) | |
| def request_from_changing_database(): | |
| return time() | |
| @cache_simple | |
| def fib(n, a=None, b=None): | |
| if n == 0: | |
| print('Computed Fib') | |
| return 0 | |
| return fib(n - 1, a=a, b=b) + n | |
| if __name__ == '__main__': | |
| r.flushall() | |
| print('fib(50): ', end='') | |
| fib(50) | |
| print('fib(50, b=None, a=None): ') | |
| fib(50, b=None, a=None) | |
| print('fib(50, a=None, b=None): ') | |
| fib(50, a=None, b=None) | |
| print('\nfib(50, a=1, b=None): ', end='') | |
| fib(50, a=1, b=None) | |
| print(f'\nRequest -> {request_from_changing_database()}') | |
| print('Sleep 0.1') | |
| sleep(0.1) | |
| print(f'Request -> {request_from_changing_database()}') | |
| print('Sleep 1') | |
| sleep(1) | |
| print(f'Request -> {request_from_changing_database()}') | |
| # Output: | |
| # fib(50): Computed Fib | |
| # fib(50, b=None, a=None): | |
| # fib(50, a=None, b=None): | |
| # | |
| # fib(50, a=1, b=None): Computed Fib | |
| # | |
| # Request -> 1668468370.6167119 | |
| # Sleep 0.1 | |
| # Request -> 1668468370.6167119 | |
| # Sleep 1 | |
| # Request -> 1668468371.724545 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment