Use sets for random

This commit is contained in:
2022-04-02 17:30:12 +03:00
parent 2f6a4a192a
commit b13976d017
4 changed files with 39 additions and 27 deletions

View File

@@ -4,6 +4,8 @@ from concurrent.futures import ThreadPoolExecutor
from random import choice from random import choice
from typing import Optional, Generic, TypeVar, TypedDict, Union from typing import Optional, Generic, TypeVar, TypedDict, Union
from fastapi import BackgroundTasks
import aioredis import aioredis
from databases import Database from databases import Database
from fastapi_pagination.api import resolve_params from fastapi_pagination.api import resolve_params
@@ -282,17 +284,19 @@ class GetRandomService(Generic[MODEL]):
return [obj["id"] for obj in objects] return [obj["id"] for obj in objects]
@classmethod @classmethod
async def _get_objects_from_cache( async def _get_random_object_from_cache(
cls, allowed_langs: frozenset[str], redis: aioredis.Redis cls, allowed_langs: frozenset[str], redis: aioredis.Redis
) -> Optional[list[int]]: ) -> Optional[int]:
try: try:
key = cls.get_cache_key(allowed_langs) key = cls.get_cache_key(allowed_langs)
data = await redis.get(key) active_key = f"{key}_active"
if data is None: if not await redis.exists(active_key):
return None return None
return orjson.loads(data) data: bytes = await redis.srandmember(key)
return int(data.decode())
except aioredis.RedisError as e: except aioredis.RedisError as e:
print(e) print(e)
return None return None
@@ -303,31 +307,32 @@ class GetRandomService(Generic[MODEL]):
) -> bool: ) -> bool:
try: try:
key = cls.get_cache_key(allowed_langs) key = cls.get_cache_key(allowed_langs)
await redis.set(key, orjson.dumps(object_ids), ex=cls.CACHE_TTL) active_key = f"{key}_active"
await redis.set(active_key, 1, ex=cls.CACHE_TTL)
await redis.delete(key)
await redis.sadd(key, *object_ids)
return True return True
except aioredis.RedisError as e: except aioredis.RedisError as e:
print(e) print(e)
return False return False
@classmethod @classmethod
async def get_objects( async def get_random_id(
cls, allowed_langs: frozenset[str], redis: aioredis.Redis cls,
) -> list[int]: allowed_langs: frozenset[str],
cached_object_ids = await cls._get_objects_from_cache(allowed_langs, redis) redis: aioredis.Redis,
background_tasks: BackgroundTasks,
) -> int:
cached_object_id = await cls._get_random_object_from_cache(allowed_langs, redis)
if cached_object_ids is not None: if cached_object_id is not None:
return cached_object_ids return cached_object_id
object_ids = await cls._get_objects_from_db(allowed_langs) object_ids = await cls._get_objects_from_db(allowed_langs)
await cls._cache_object_ids(object_ids, allowed_langs, redis)
return object_ids background_tasks.add_task(cls._cache_object_ids, allowed_langs, redis)
@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)

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, Request, HTTPException, status from fastapi import APIRouter, BackgroundTasks, Depends, Request, HTTPException, status
from fastapi_pagination import Params from fastapi_pagination import Params
from fastapi_pagination.ext.ormar import paginate from fastapi_pagination.ext.ormar import paginate
@@ -55,10 +55,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( async def get_random_author(
request: Request, allowed_langs: frozenset[str] = Depends(get_allowed_langs) request: Request,
background_tasks: BackgroundTasks,
allowed_langs: frozenset[str] = Depends(get_allowed_langs),
): ):
author_id = await GetRandomAuthorService.get_random_id( author_id = await GetRandomAuthorService.get_random_id(
allowed_langs, request.app.state.redis allowed_langs, request.app.state.redis, background_tasks
) )
return ( return (

View File

@@ -1,6 +1,6 @@
from typing import Union from typing import Union
from fastapi import APIRouter, Depends, Request, HTTPException, status from fastapi import APIRouter, BackgroundTasks, Depends, Request, HTTPException, status
from fastapi_pagination import Params from fastapi_pagination import Params
@@ -57,10 +57,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( async def get_random_book(
request: Request, allowed_langs: frozenset[str] = Depends(get_allowed_langs) request: Request,
background_tasks: BackgroundTasks,
allowed_langs: frozenset[str] = Depends(get_allowed_langs),
): ):
book_id = await GetRandomBookService.get_random_id( book_id = await GetRandomBookService.get_random_id(
allowed_langs, request.app.state.redis allowed_langs, request.app.state.redis, background_tasks
) )
book = ( book = (

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, Request from fastapi import APIRouter, BackgroundTasks, Depends, Request
from fastapi_pagination import Params from fastapi_pagination import Params
from fastapi_pagination.ext.ormar import paginate from fastapi_pagination.ext.ormar import paginate
@@ -29,10 +29,13 @@ 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, request: Request,
background_tasks: BackgroundTasks,
allowed_langs: frozenset[str] = Depends(get_allowed_langs), allowed_langs: frozenset[str] = Depends(get_allowed_langs),
): ):
sequence_id = await GetRandomSequenceService.get_random_id( sequence_id = await GetRandomSequenceService.get_random_id(
allowed_langs, request.app.state.redis allowed_langs,
request.app.state.redis,
background_tasks,
) )
return await SequenceDB.objects.get(id=sequence_id) return await SequenceDB.objects.get(id=sequence_id)