REDIS кэширование, на локалке
parent
291c61f873
commit
43eca19d91
|
@ -60,8 +60,7 @@ class MenuRepository:
|
|||
updated_menu = await self.db.execute(qr)
|
||||
return updated_menu.scalar_one()
|
||||
|
||||
async def delete_menu_item(self, menu_id: UUID) -> int:
|
||||
async def delete_menu_item(self, menu_id: UUID):
|
||||
query = delete(Menu).where(Menu.id == menu_id)
|
||||
await self.db.execute(query)
|
||||
await self.db.commit()
|
||||
return 200
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import pickle
|
||||
from typing import Any
|
||||
|
||||
import redis.asyncio as redis # type: ignore
|
||||
|
@ -6,19 +7,59 @@ from fastapi import BackgroundTasks, Depends
|
|||
from fastfood.dbase import get_redis_pool
|
||||
|
||||
|
||||
def get_key(level: str, **kwargs) -> str:
|
||||
match level:
|
||||
case 'menus':
|
||||
return 'MENUS'
|
||||
case 'menu':
|
||||
return f"{kwargs.get('menu_id')}"
|
||||
case 'submenus':
|
||||
return f"{kwargs.get('menu_id')}:SUBMENUS"
|
||||
case 'submenu':
|
||||
return f"{kwargs.get('menu_id')}:{kwargs.get('submenu_id')}"
|
||||
case 'dishes':
|
||||
return f"{kwargs.get('menu_id')}:{kwargs.get('submenu_id')}:DISHES"
|
||||
case 'dish':
|
||||
return f"{kwargs.get('menu_id')}:{kwargs.get('submenu_id')}:{kwargs.get('dish_id')}"
|
||||
|
||||
return 'abracadabra'
|
||||
|
||||
|
||||
class RedisRepository:
|
||||
def __init__(
|
||||
self,
|
||||
redis_pool: redis.Redis = Depends(get_redis_pool),
|
||||
pool: redis.Redis = Depends(get_redis_pool),
|
||||
) -> None:
|
||||
self.redis_pool = redis_pool
|
||||
self.pool = pool
|
||||
self.ttl = 1800
|
||||
|
||||
async def get(self, key: str) -> Any | None:
|
||||
|
||||
data = await self.pool.get(key)
|
||||
if data is not None:
|
||||
return pickle.loads(data)
|
||||
return None
|
||||
|
||||
async def set(
|
||||
self, key: str, value: Any, background_tasks: BackgroundTasks
|
||||
) -> None:
|
||||
pass
|
||||
async def set(self, key: str, value: Any, bg_task: BackgroundTasks) -> None:
|
||||
data = pickle.dumps(value)
|
||||
bg_task.add_task(self._set_cache, key, data)
|
||||
|
||||
async def _set_cache(self, key: str, data: Any) -> None:
|
||||
await self.pool.setex(key, self.ttl, data)
|
||||
|
||||
async def delete(self, key: str, bg_task: BackgroundTasks) -> None:
|
||||
bg_task.add_task(self._delete_cache, key)
|
||||
|
||||
async def _delete_cache(self, key: str) -> None:
|
||||
await self.pool.delete(key)
|
||||
|
||||
async def clear_cache(self, pattern: str, bg_task: BackgroundTasks) -> None:
|
||||
keys = [key async for key in self.pool.scan_iter(pattern)]
|
||||
if keys:
|
||||
bg_task.add_task(self._clear_keys, keys)
|
||||
|
||||
async def _clear_keys(self, keys: list[str]) -> None:
|
||||
await self.pool.delete(*keys)
|
||||
|
||||
async def invalidate(self, key: str, bg_task: BackgroundTasks) -> None:
|
||||
await self.clear_cache(f'{key}*', bg_task)
|
||||
await self.clear_cache(f'{get_key("menus")}*', bg_task)
|
||||
|
|
|
@ -5,7 +5,7 @@ from fastapi import BackgroundTasks, Depends
|
|||
|
||||
from fastfood.dbase import get_async_redis_client
|
||||
from fastfood.repository.dish import DishRepository
|
||||
from fastfood.repository.redis import RedisRepository
|
||||
from fastfood.repository.redis import RedisRepository, get_key
|
||||
from fastfood.schemas import Dish, Dish_db, DishBase
|
||||
|
||||
|
||||
|
@ -17,16 +17,32 @@ class DishService:
|
|||
background_tasks: BackgroundTasks = None,
|
||||
) -> None:
|
||||
self.dish_repo = dish_repo
|
||||
self.cache_client = RedisRepository(redis_client)
|
||||
self.background_tasks = background_tasks
|
||||
self.cache = RedisRepository(redis_client)
|
||||
self.bg_tasks = background_tasks
|
||||
self.key = get_key
|
||||
|
||||
async def read_dishes(self, menu_id: UUID, submenu_id: UUID) -> list[Dish]:
|
||||
cached_dishes = await self.cache.get(
|
||||
self.key('dishes', menu_id=str(menu_id), submenu_id=str(submenu_id))
|
||||
)
|
||||
if cached_dishes is not None:
|
||||
return cached_dishes
|
||||
|
||||
data = await self.dish_repo.get_dishes(menu_id, submenu_id)
|
||||
response = []
|
||||
for row in data:
|
||||
dish = row.__dict__
|
||||
dish['price'] = str(dish['price'])
|
||||
response.append(Dish(**dish))
|
||||
await self.cache.set(
|
||||
self.key(
|
||||
'dishes',
|
||||
menu_id=str(menu_id),
|
||||
submenu_id=str(submenu_id),
|
||||
),
|
||||
response,
|
||||
self.bg_tasks,
|
||||
)
|
||||
return response
|
||||
|
||||
async def create_dish(
|
||||
|
@ -35,34 +51,79 @@ class DishService:
|
|||
submenu_id: UUID,
|
||||
dish_data: DishBase,
|
||||
) -> Dish:
|
||||
dish = Dish_db(**dish_data.model_dump())
|
||||
dish_db = Dish_db(**dish_data.model_dump())
|
||||
data = await self.dish_repo.create_dish_item(
|
||||
menu_id,
|
||||
submenu_id,
|
||||
dish,
|
||||
dish_db,
|
||||
)
|
||||
response = data.__dict__
|
||||
response['price'] = str(response['price'])
|
||||
return Dish(**response)
|
||||
dish = data.__dict__
|
||||
dish['price'] = str(dish['price'])
|
||||
dish = Dish(**dish)
|
||||
await self.cache.set(
|
||||
self.key('dish', menu_id=str(menu_id), submenu_id=str(submenu_id)),
|
||||
dish,
|
||||
self.bg_tasks,
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
|
||||
return dish
|
||||
|
||||
async def read_dish(
|
||||
self, menu_id: UUID, submenu_id: UUID, dish_id: UUID
|
||||
) -> Dish | None:
|
||||
cached_dish = await self.cache.get(
|
||||
self.key(
|
||||
'dish',
|
||||
menu_id=str(menu_id),
|
||||
submenu_id=str(submenu_id),
|
||||
dish_id=str(dish_id),
|
||||
)
|
||||
)
|
||||
if cached_dish is not None:
|
||||
return cached_dish
|
||||
|
||||
data = await self.dish_repo.get_dish_item(menu_id, submenu_id, dish_id)
|
||||
if data is None:
|
||||
return None
|
||||
response = data.__dict__
|
||||
response['price'] = str(response['price'])
|
||||
return Dish(**response)
|
||||
dish = data.__dict__
|
||||
dish['price'] = str(dish['price'])
|
||||
dish = Dish(**dish)
|
||||
await self.cache.set(
|
||||
self.key(
|
||||
'dish',
|
||||
menu_id=str(menu_id),
|
||||
submenu_id=str(submenu_id),
|
||||
dish_id=str(dish_id),
|
||||
),
|
||||
dish,
|
||||
self.bg_tasks,
|
||||
)
|
||||
return dish
|
||||
|
||||
async def update_dish(
|
||||
self, menu_id: UUID, submenu_id: UUID, dish_id, dish_data: DishBase
|
||||
) -> Dish:
|
||||
dish = Dish_db(**dish_data.model_dump())
|
||||
data = await self.dish_repo.update_dish_item(menu_id, submenu_id, dish_id, dish)
|
||||
response = data.__dict__
|
||||
response['price'] = str(response['price'])
|
||||
return Dish(**response)
|
||||
dish_db = Dish_db(**dish_data.model_dump())
|
||||
data = await self.dish_repo.update_dish_item(
|
||||
menu_id, submenu_id, dish_id, dish_db
|
||||
)
|
||||
dish = data.__dict__
|
||||
dish['price'] = str(dish['price'])
|
||||
dish = Dish(**dish)
|
||||
await self.cache.set(
|
||||
self.key(
|
||||
'dish',
|
||||
menu_id=str(menu_id),
|
||||
submenu_id=str(submenu_id),
|
||||
dish_id=str(dish_id),
|
||||
),
|
||||
dish,
|
||||
self.bg_tasks,
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
|
||||
return dish
|
||||
|
||||
async def del_dish(self, menu_id: UUID, submenu_id: UUID, dish_id: UUID) -> int:
|
||||
response = await self.dish_repo.delete_dish_item(
|
||||
|
@ -70,4 +131,7 @@ class DishService:
|
|||
submenu_id,
|
||||
dish_id,
|
||||
)
|
||||
await self.cache.delete(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
|
||||
return response
|
||||
|
|
|
@ -5,7 +5,7 @@ from fastapi import BackgroundTasks, Depends
|
|||
|
||||
from fastfood.dbase import get_async_redis_client
|
||||
from fastfood.repository.menu import MenuRepository
|
||||
from fastfood.repository.redis import RedisRepository
|
||||
from fastfood.repository.redis import RedisRepository, get_key
|
||||
from fastfood.schemas import MenuBase, MenuRead
|
||||
|
||||
|
||||
|
@ -17,10 +17,15 @@ class MenuService:
|
|||
background_tasks: BackgroundTasks = None,
|
||||
) -> None:
|
||||
self.menu_repo = menu_repo
|
||||
self.cache_client = RedisRepository(redis_client)
|
||||
self.background_tasks = background_tasks
|
||||
self.cache = RedisRepository(redis_client)
|
||||
self.key = get_key
|
||||
self.bg_tasks = background_tasks
|
||||
|
||||
async def read_menus(self) -> list[MenuRead]:
|
||||
cached_menus = await self.cache.get(self.key('menus'))
|
||||
if cached_menus is not None:
|
||||
return cached_menus
|
||||
|
||||
data = await self.menu_repo.get_menus()
|
||||
menus = []
|
||||
for r in data:
|
||||
|
@ -34,6 +39,8 @@ class MenuService:
|
|||
menu['dishes_count'] = dishes_conter
|
||||
menu = MenuRead(**menu)
|
||||
menus.append(menu)
|
||||
|
||||
await self.cache.set(self.key('menus'), menus, self.bg_tasks)
|
||||
return menus
|
||||
|
||||
async def create_menu(self, menu_data: MenuBase) -> MenuRead:
|
||||
|
@ -46,10 +53,23 @@ class MenuService:
|
|||
dishes_conter += len(sub.dishes)
|
||||
menu['submenus_count'] = len(menu.pop('submenus'))
|
||||
menu['dishes_count'] = dishes_conter
|
||||
|
||||
return MenuRead(**menu)
|
||||
await self.cache.set(
|
||||
key=get_key('menu', menu_id=str(menu.get('id'))),
|
||||
value=menu,
|
||||
bg_task=self.bg_tasks,
|
||||
)
|
||||
menu = MenuRead(**menu)
|
||||
await self.cache.set(
|
||||
self.key('menu', menu_id=str(menu.id)), menu, self.bg_tasks
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu.id), bg_task=self.bg_tasks)
|
||||
return menu
|
||||
|
||||
async def read_menu(self, menu_id: UUID) -> MenuRead | None:
|
||||
cached_menu = await self.cache.get(self.key('menu', menu_id=str(menu_id)))
|
||||
if cached_menu is not None:
|
||||
return cached_menu
|
||||
|
||||
data = await self.menu_repo.get_menu_item(menu_id)
|
||||
if data is None:
|
||||
return None
|
||||
|
@ -61,8 +81,11 @@ class MenuService:
|
|||
dishes_conter += len(sub.dishes)
|
||||
menu['submenus_count'] = len(menu.pop('submenus'))
|
||||
menu['dishes_count'] = dishes_conter
|
||||
|
||||
return MenuRead(**menu)
|
||||
menu = MenuRead(**menu)
|
||||
await self.cache.set(
|
||||
self.key('menu', menu_id=str(menu.id)), menu, self.bg_tasks
|
||||
)
|
||||
return menu
|
||||
|
||||
async def update_menu(self, menu_id: UUID, menu_data) -> MenuRead:
|
||||
data = await self.menu_repo.update_menu_item(menu_id, menu_data)
|
||||
|
@ -74,9 +97,15 @@ class MenuService:
|
|||
dishes_conter += len(sub.dishes)
|
||||
menu['submenus_count'] = len(menu.pop('submenus'))
|
||||
menu['dishes_count'] = dishes_conter
|
||||
menu = MenuRead(**menu)
|
||||
await self.cache.set(
|
||||
self.key('menu', menu_id=str(menu.id)), menu, self.bg_tasks
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
return menu
|
||||
|
||||
return MenuRead(**menu)
|
||||
|
||||
async def del_menu(self, menu_id: UUID) -> int:
|
||||
async def del_menu(self, menu_id: UUID):
|
||||
data = await self.menu_repo.delete_menu_item(menu_id)
|
||||
await self.cache.delete(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
return data
|
||||
|
|
|
@ -4,7 +4,7 @@ import redis.asyncio as redis # type: ignore
|
|||
from fastapi import BackgroundTasks, Depends
|
||||
|
||||
from fastfood.dbase import get_async_redis_client
|
||||
from fastfood.repository.redis import RedisRepository
|
||||
from fastfood.repository.redis import RedisRepository, get_key
|
||||
from fastfood.repository.submenu import SubMenuRepository
|
||||
from fastfood.schemas import MenuBase, SubMenuRead
|
||||
|
||||
|
@ -18,10 +18,17 @@ class SubmenuService:
|
|||
) -> None:
|
||||
|
||||
self.submenu_repo = submenu_repo
|
||||
self.cache_client = RedisRepository(redis_client)
|
||||
self.background_tasks = background_tasks
|
||||
self.cache = RedisRepository(redis_client)
|
||||
self.bg_tasks = background_tasks
|
||||
self.key = get_key
|
||||
|
||||
async def read_submenus(self, menu_id: UUID) -> list[SubMenuRead]:
|
||||
cached_submenus = await self.cache.get(
|
||||
self.key('submenus', menu_id=str(menu_id))
|
||||
)
|
||||
if cached_submenus is not None:
|
||||
return cached_submenus
|
||||
|
||||
data = await self.submenu_repo.get_submenus(menu_id=menu_id)
|
||||
submenus = []
|
||||
for r in data:
|
||||
|
@ -31,6 +38,10 @@ class SubmenuService:
|
|||
submenu['dishes_count'] = len(subq.dishes)
|
||||
submenu = SubMenuRead(**submenu)
|
||||
submenus.append(submenu)
|
||||
|
||||
await self.cache.set(
|
||||
self.key('submenus', menu_id=str(menu_id)), submenus, self.bg_tasks
|
||||
)
|
||||
return submenus
|
||||
|
||||
async def create_submenu(
|
||||
|
@ -40,20 +51,40 @@ class SubmenuService:
|
|||
menu_id,
|
||||
submenu_data,
|
||||
)
|
||||
menu = data.__dict__
|
||||
menu = {k: v for k, v in menu.items() if not k.startswith('_')}
|
||||
menu['dishes_count'] = len(menu.pop('dishes'))
|
||||
menu = SubMenuRead(**menu)
|
||||
return menu
|
||||
submenu = data.__dict__
|
||||
submenu = {k: v for k, v in submenu.items() if not k.startswith('_')}
|
||||
submenu['dishes_count'] = len(submenu.pop('dishes'))
|
||||
submenu = SubMenuRead(**submenu)
|
||||
await self.cache.set(
|
||||
self.key('submenu', menu_id=str(menu_id)), submenu, self.bg_tasks
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
|
||||
return submenu
|
||||
|
||||
async def read_menu(self, menu_id: UUID, submenu_id: UUID) -> SubMenuRead | None:
|
||||
cached_submenu = await self.cache.get(
|
||||
self.key(
|
||||
'submenu',
|
||||
menu_id=str(menu_id),
|
||||
submenu_id=str(submenu_id),
|
||||
)
|
||||
)
|
||||
if cached_submenu is not None:
|
||||
return cached_submenu
|
||||
|
||||
data = await self.submenu_repo.get_submenu_item(menu_id, submenu_id)
|
||||
if data is None:
|
||||
return None
|
||||
menu = data.__dict__
|
||||
menu = {k: v for k, v in menu.items() if not k.startswith('_')}
|
||||
menu['dishes_count'] = len(menu.pop('dishes'))
|
||||
menu = SubMenuRead(**menu)
|
||||
submenu = data.__dict__
|
||||
submenu = {k: v for k, v in submenu.items() if not k.startswith('_')}
|
||||
submenu['dishes_count'] = len(submenu.pop('dishes'))
|
||||
menu = SubMenuRead(**submenu)
|
||||
await self.cache.set(
|
||||
self.key('submenu', menu_id=str(menu_id), submenu_id=str(submenu_id)),
|
||||
submenu,
|
||||
self.bg_tasks,
|
||||
)
|
||||
return menu
|
||||
|
||||
async def update_submenu(
|
||||
|
@ -62,11 +93,28 @@ class SubmenuService:
|
|||
data = await self.submenu_repo.update_submenu_item(
|
||||
menu_id, submenu_id, submenu_data
|
||||
)
|
||||
menu = data.__dict__
|
||||
menu = {k: v for k, v in menu.items() if not k.startswith('_')}
|
||||
menu['dishes_count'] = len(menu.pop('dishes'))
|
||||
menu = SubMenuRead(**menu)
|
||||
return menu
|
||||
submenu = data.__dict__
|
||||
submenu = {k: v for k, v in submenu.items() if not k.startswith('_')}
|
||||
submenu['dishes_count'] = len(submenu.pop('dishes'))
|
||||
submenu = SubMenuRead(**submenu)
|
||||
await self.cache.set(
|
||||
self.key('submenu', menu_id=str(menu_id), submenu_id=str(submenu_id)),
|
||||
submenu,
|
||||
self.bg_tasks,
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
|
||||
return submenu
|
||||
|
||||
async def del_menu(self, menu_id: UUID, submenu_id: UUID) -> int:
|
||||
return await self.submenu_repo.delete_submenu_item(menu_id, submenu_id)
|
||||
code = await self.submenu_repo.delete_submenu_item(menu_id, submenu_id)
|
||||
await self.cache.delete(
|
||||
key=self.key(
|
||||
'submenu',
|
||||
menu_id=str(menu_id),
|
||||
submenu_id=str(submenu_id),
|
||||
),
|
||||
bg_task=self.bg_tasks,
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
return code
|
||||
|
|
Loading…
Reference in New Issue