From 531c4989dba50d6beb24443067d67f97caf1c042 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Thu, 25 Jan 2024 11:41:55 +0000 Subject: [PATCH] cache: Remove avatars/images from cache if not used This creates a simple LRU policy to evict items from the cache that have not been accessed in 24 hours. Fixes: #13547 Signed-off-by: Michael Tremer --- src/backend/accounts.py | 18 ++++++++++++++---- src/backend/wiki.py | 19 ++++++++++++++++--- src/web/users.py | 17 +++++++++++++---- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/backend/accounts.py b/src/backend/accounts.py index 9df7d627..397f217c 100644 --- a/src/backend/accounts.py +++ b/src/backend/accounts.py @@ -1301,11 +1301,21 @@ class Account(LDAPObject): return photo # Compose the cache key - key = "accounts:%s:avatar:%s:%s:%s" \ + cache_key = "accounts:%s:avatar:%s:%s:%s" \ % (self.uid, self.avatar_hash, format or "N/A", size) - # Try to retrieve something from the cache - avatar = await self.backend.cache.get(key) + # Try to fetch the data from the cache + async with await self.backend.cache.pipeline() as p: + # Fetch the key + await p.get(cache_key) + + # Reset the TTL + await p.expire(cache_key, 86400) + + # Execute the pipeline + avatar, _ = await p.execute() + + # Return the cached value (if any) if avatar: return avatar @@ -1313,7 +1323,7 @@ class Account(LDAPObject): avatar = util.generate_thumbnail(photo, size, square=True, format=format) # Save to cache for 24h - await self.backend.cache.set(key, avatar, 86400) + await self.backend.cache.set(cache_key, avatar, 86400) return avatar diff --git a/src/backend/wiki.py b/src/backend/wiki.py index 7ef21401..9c5f55aa 100644 --- a/src/backend/wiki.py +++ b/src/backend/wiki.py @@ -713,6 +713,9 @@ class File(misc.Object): async def get_thumbnail(self, size, format=None): assert self.is_bitmap_image() + # Let thumbnails live in the cache for up to 24h + ttl = 24 * 3600 + cache_key = ":".join(( "wiki", "thumbnail", @@ -724,15 +727,25 @@ class File(misc.Object): )) # Try to fetch the data from the cache - thumbnail = await self.backend.cache.get(cache_key) + async with await self.backend.cache.pipeline() as p: + # Fetch the key + await p.get(cache_key) + + # Reset the TTL + await p.expire(cache_key, ttl) + + # Execute the pipeline + thumbnail, _ = await p.execute() + + # Return the cached value if thumbnail: return thumbnail # Generate the thumbnail thumbnail = util.generate_thumbnail(self.blob, size, format=format, quality=95) - # Put it into the cache for forever - await self.backend.cache.set(cache_key, thumbnail) + # Put it into the cache for 24h + await self.backend.cache.set(cache_key, thumbnail, ttl) return thumbnail diff --git a/src/web/users.py b/src/web/users.py index b27c09a2..bc4e57f9 100644 --- a/src/web/users.py +++ b/src/web/users.py @@ -93,15 +93,24 @@ class AvatarHandler(base.BaseHandler): size = 2048 # Cache key - key = "avatar:letter:%s:%s:%s" % ("".join(letters), format or "N/A", size) + cache_key = "avatar:letter:%s:%s:%s" % ("".join(letters), format or "N/A", size) + + # Try to fetch the data from the cache + async with await self.backend.cache.pipeline() as p: + # Fetch the key + await p.get(cache_key) + + # Reset the TTL + await p.expire(cache_key, 86400) + + # Execute the pipeline + avatar, _ = await p.execute() - # Fetch avatar from the cache - avatar = await self.backend.cache.get(key) if not avatar: avatar = self._make_avatar(letters, size=size, **args) # Cache for forever - await self.backend.cache.set(key, avatar) + await self.backend.cache.set(cache_key, avatar, 86400) return avatar -- 2.39.5