mirror of
https://github.com/flibusta-apps/book_library_server.git
synced 2025-12-06 15:15:36 +01:00
Add random caching
This commit is contained in:
@@ -48,7 +48,7 @@ class AuthorTGRMSearchService(TRGMSearchService):
|
|||||||
GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY
|
GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY
|
||||||
|
|
||||||
|
|
||||||
GET_OBJECT_ID_QUERY = """
|
GET_OBJECTS_ID_QUERY = """
|
||||||
WITH filtered_authors AS (
|
WITH filtered_authors AS (
|
||||||
SELECT id FROM authors
|
SELECT id FROM authors
|
||||||
WHERE EXISTS (
|
WHERE EXISTS (
|
||||||
@@ -64,7 +64,7 @@ SELECT id FROM filtered_authors;
|
|||||||
|
|
||||||
class GetRandomAuthorService(GetRandomService):
|
class GetRandomAuthorService(GetRandomService):
|
||||||
MODEL_CLASS = Author
|
MODEL_CLASS = Author
|
||||||
GET_OBJECT_ID_QUERY = GET_OBJECT_ID_QUERY
|
GET_OBJECTS_ID_QUERY = GET_OBJECTS_ID_QUERY
|
||||||
|
|
||||||
|
|
||||||
class AuthorMeiliSearchService(MeiliSearchService):
|
class AuthorMeiliSearchService(MeiliSearchService):
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ SELECT id FROM filtered_books;
|
|||||||
|
|
||||||
class GetRandomBookService(GetRandomService):
|
class GetRandomBookService(GetRandomService):
|
||||||
MODEL_CLASS = BookDB
|
MODEL_CLASS = BookDB
|
||||||
GET_OBJECT_ID_QUERY = GET_OBJECT_IDS_QUERY
|
GET_OBJECTS_ID_QUERY = GET_OBJECTS_ID_QUERY
|
||||||
|
|
||||||
|
|
||||||
class BookMeiliSearchService(MeiliSearchService):
|
class BookMeiliSearchService(MeiliSearchService):
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class BaseSearchService(Generic[MODEL, QUERY], abc.ABC):
|
|||||||
SELECT_RELATED: Optional[Union[list[str], str]] = None
|
SELECT_RELATED: Optional[Union[list[str], str]] = None
|
||||||
PREFETCH_RELATED: Optional[Union[list[str], str]] = None
|
PREFETCH_RELATED: Optional[Union[list[str], str]] = None
|
||||||
CUSTOM_CACHE_PREFIX: Optional[str] = None
|
CUSTOM_CACHE_PREFIX: Optional[str] = None
|
||||||
CACHE_TTL = 60 * 60
|
CACHE_TTL = 6 * 60 * 60
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_params(cls) -> AbstractParams:
|
def get_params(cls) -> AbstractParams:
|
||||||
@@ -237,6 +237,8 @@ class MeiliSearchService(Generic[MODEL], BaseSearchService[MODEL, SearchQuery]):
|
|||||||
class GetRandomService(Generic[MODEL]):
|
class GetRandomService(Generic[MODEL]):
|
||||||
MODEL_CLASS: Optional[MODEL] = None
|
MODEL_CLASS: Optional[MODEL] = None
|
||||||
GET_OBJECTS_ID_QUERY: Optional[str] = None
|
GET_OBJECTS_ID_QUERY: Optional[str] = None
|
||||||
|
CUSTOM_CACHE_PREFIX: Optional[str] = None
|
||||||
|
CACHE_TTL = 6 * 60 * 60
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@property
|
@property
|
||||||
@@ -249,6 +251,21 @@ class GetRandomService(Generic[MODEL]):
|
|||||||
def database(cls) -> Database:
|
def database(cls) -> Database:
|
||||||
return cls.model.Meta.database
|
return cls.model.Meta.database
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@property
|
||||||
|
def cache_prefix(cls) -> str:
|
||||||
|
return cls.CUSTOM_CACHE_PREFIX or cls.model.Meta.tablename
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_query_hash(query: frozenset[str]):
|
||||||
|
return hash(query)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_cache_key(cls, query: frozenset[str]) -> str:
|
||||||
|
model_class_name = cls.cache_prefix
|
||||||
|
query_hash = cls._get_query_hash(query)
|
||||||
|
return f"random_{model_class_name}_{query_hash}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@property
|
@property
|
||||||
def objects_id_query(cls) -> str:
|
def objects_id_query(cls) -> str:
|
||||||
@@ -258,15 +275,59 @@ class GetRandomService(Generic[MODEL]):
|
|||||||
return cls.GET_OBJECTS_ID_QUERY
|
return cls.GET_OBJECTS_ID_QUERY
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_objects(cls, allowed_langs: frozenset[str]) -> list[int]:
|
async def _get_objects_from_db(cls, allowed_langs: frozenset[str]) -> list[int]:
|
||||||
objects = await cls.database.fetch_all(
|
objects = await cls.database.fetch_all(
|
||||||
cls.objects_id_query, {"langs": allowed_langs}
|
cls.objects_id_query, {"langs": allowed_langs}
|
||||||
)
|
)
|
||||||
return [obj["id"] for obj in objects]
|
return [obj["id"] for obj in objects]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_random_id(cls, allowed_langs: frozenset[str]) -> int:
|
async def _get_objects_from_cache(
|
||||||
object_ids = await cls.get_objects(allowed_langs)
|
cls, allowed_langs: frozenset[str], redis: aioredis.Redis
|
||||||
|
) -> Optional[list[int]]:
|
||||||
|
try:
|
||||||
|
key = cls.get_cache_key(allowed_langs)
|
||||||
|
data = await redis.get(key)
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return orjson.loads(data)
|
||||||
|
except aioredis.RedisError as e:
|
||||||
|
print(e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def _cache_object_ids(
|
||||||
|
cls, object_ids: list[int], allowed_langs: frozenset[str], redis: aioredis.Redis
|
||||||
|
) -> bool:
|
||||||
|
try:
|
||||||
|
key = cls.get_cache_key(allowed_langs)
|
||||||
|
await redis.set(key, orjson.dumps(object_ids), ex=cls.CACHE_TTL)
|
||||||
|
return True
|
||||||
|
except aioredis.RedisError as e:
|
||||||
|
print(e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def get_objects(
|
||||||
|
cls, allowed_langs: frozenset[str], redis: aioredis.Redis
|
||||||
|
) -> list[int]:
|
||||||
|
cached_object_ids = await cls._get_objects_from_cache(allowed_langs, redis)
|
||||||
|
|
||||||
|
if cached_object_ids is not None:
|
||||||
|
return cached_object_ids
|
||||||
|
|
||||||
|
object_ids = await cls._get_objects_from_db(allowed_langs)
|
||||||
|
await cls._cache_object_ids(object_ids, allowed_langs, redis)
|
||||||
|
|
||||||
|
return object_ids
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def get_random_id(
|
||||||
|
cls, allowed_langs: frozenset[str], redis: aioredis.Redis
|
||||||
|
) -> int:
|
||||||
|
object_ids = await cls.get_objects(allowed_langs, redis)
|
||||||
return choice(object_ids)
|
return choice(object_ids)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class SequenceTGRMSearchService(TRGMSearchService):
|
|||||||
GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY
|
GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY
|
||||||
|
|
||||||
|
|
||||||
GET_OBJECT_ID_QUERY = """
|
GET_OBJECTS_ID_QUERY = """
|
||||||
WITH filtered_sequences AS (
|
WITH filtered_sequences AS (
|
||||||
SELECT id FROM sequences
|
SELECT id FROM sequences
|
||||||
WHERE EXISTS (
|
WHERE EXISTS (
|
||||||
@@ -55,7 +55,7 @@ ORDER BY RANDOM() LIMIT 1;
|
|||||||
|
|
||||||
class GetRandomSequenceService(GetRandomService):
|
class GetRandomSequenceService(GetRandomService):
|
||||||
MODEL_CLASS = Sequence
|
MODEL_CLASS = Sequence
|
||||||
GET_OBJECT_ID_QUERY = GET_OBJECT_ID_QUERY
|
GET_OBJECTS_ID_QUERY = GET_OBJECTS_ID_QUERY
|
||||||
|
|
||||||
|
|
||||||
class SequenceMeiliSearchService(MeiliSearchService):
|
class SequenceMeiliSearchService(MeiliSearchService):
|
||||||
|
|||||||
@@ -54,8 +54,12 @@ async def create_author(data: CreateAuthor):
|
|||||||
|
|
||||||
|
|
||||||
@author_router.get("/random", response_model=Author)
|
@author_router.get("/random", response_model=Author)
|
||||||
async def get_random_author(allowed_langs: frozenset[str] = Depends(get_allowed_langs)):
|
async def get_random_author(
|
||||||
author_id = await GetRandomAuthorService.get_random_id(allowed_langs)
|
request: Request, allowed_langs: frozenset[str] = Depends(get_allowed_langs)
|
||||||
|
):
|
||||||
|
author_id = await GetRandomAuthorService.get_random_id(
|
||||||
|
allowed_langs, request.app.state.redis
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
await AuthorDB.objects.select_related(SELECT_RELATED_FIELDS)
|
await AuthorDB.objects.select_related(SELECT_RELATED_FIELDS)
|
||||||
|
|||||||
@@ -56,8 +56,12 @@ async def create_book(data: Union[CreateBook, CreateRemoteBook]):
|
|||||||
|
|
||||||
|
|
||||||
@book_router.get("/random", response_model=BookDetail)
|
@book_router.get("/random", response_model=BookDetail)
|
||||||
async def get_random_book(allowed_langs: frozenset[str] = Depends(get_allowed_langs)):
|
async def get_random_book(
|
||||||
book_id = await GetRandomBookService.get_random_id(allowed_langs)
|
request: Request, allowed_langs: frozenset[str] = Depends(get_allowed_langs)
|
||||||
|
):
|
||||||
|
book_id = await GetRandomBookService.get_random_id(
|
||||||
|
allowed_langs, request.app.state.redis
|
||||||
|
)
|
||||||
|
|
||||||
book = (
|
book = (
|
||||||
await BookDB.objects.select_related(SELECT_RELATED_FIELDS + ["sequences"])
|
await BookDB.objects.select_related(SELECT_RELATED_FIELDS + ["sequences"])
|
||||||
|
|||||||
@@ -28,9 +28,12 @@ async def get_sequences():
|
|||||||
|
|
||||||
@sequence_router.get("/random", response_model=Sequence)
|
@sequence_router.get("/random", response_model=Sequence)
|
||||||
async def get_random_sequence(
|
async def get_random_sequence(
|
||||||
|
request: Request,
|
||||||
allowed_langs: frozenset[str] = Depends(get_allowed_langs),
|
allowed_langs: frozenset[str] = Depends(get_allowed_langs),
|
||||||
):
|
):
|
||||||
sequence_id = await GetRandomSequenceService.get_random_id(allowed_langs)
|
sequence_id = await GetRandomSequenceService.get_random_id(
|
||||||
|
allowed_langs, request.app.state.redis
|
||||||
|
)
|
||||||
|
|
||||||
return await SequenceDB.objects.get(id=sequence_id)
|
return await SequenceDB.objects.get(id=sequence_id)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user