mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-09 03:04:54 -04:00
improve backend performance with caching
This commit is contained in:
parent
c13fb6743c
commit
51876508f3
@ -1,5 +1,7 @@
|
|||||||
|
from mealie.services.recipe.all_recipes import subscripte_to_recipe_events
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.gzip import GZipMiddleware
|
||||||
|
|
||||||
from mealie.core.config import APP_VERSION, settings
|
from mealie.core.config import APP_VERSION, settings
|
||||||
from mealie.core.root_logger import get_logger
|
from mealie.core.root_logger import get_logger
|
||||||
@ -24,6 +26,8 @@ app = FastAPI(
|
|||||||
redoc_url=settings.REDOC_URL,
|
redoc_url=settings.REDOC_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
app.add_middleware(GZipMiddleware, minimum_size=1000)
|
||||||
|
|
||||||
|
|
||||||
def start_scheduler():
|
def start_scheduler():
|
||||||
import mealie.services.scheduler.scheduled_jobs # noqa: F401
|
import mealie.services.scheduler.scheduled_jobs # noqa: F401
|
||||||
@ -78,6 +82,7 @@ def system_startup():
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
create_general_event("Application Startup", f"Mealie API started on port {settings.API_PORT}")
|
create_general_event("Application Startup", f"Mealie API started on port {settings.API_PORT}")
|
||||||
|
subscripte_to_recipe_events()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from random import randint
|
from random import randint
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
from mealie.db.db_base import BaseDocument
|
from mealie.db.db_base import BaseDocument
|
||||||
from mealie.db.models.event import Event, EventNotification
|
from mealie.db.models.event import Event, EventNotification
|
||||||
@ -20,11 +21,15 @@ from mealie.schema.admin import SiteTheme
|
|||||||
from mealie.schema.events import Event as EventSchema
|
from mealie.schema.events import Event as EventSchema
|
||||||
from mealie.schema.events import EventNotificationIn
|
from mealie.schema.events import EventNotificationIn
|
||||||
from mealie.schema.meal_plan import MealPlanOut, ShoppingListOut
|
from mealie.schema.meal_plan import MealPlanOut, ShoppingListOut
|
||||||
from mealie.schema.recipe import (CommentOut, Recipe, RecipeCategoryResponse,
|
from mealie.schema.recipe import (
|
||||||
RecipeIngredientFood, RecipeIngredientUnit,
|
CommentOut,
|
||||||
RecipeTagResponse)
|
Recipe,
|
||||||
from mealie.schema.user import (GroupInDB, LongLiveTokenInDB, SignUpOut,
|
RecipeCategoryResponse,
|
||||||
UserInDB)
|
RecipeIngredientFood,
|
||||||
|
RecipeIngredientUnit,
|
||||||
|
RecipeTagResponse,
|
||||||
|
)
|
||||||
|
from mealie.schema.user import GroupInDB, LongLiveTokenInDB, SignUpOut, UserInDB
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
logger = getLogger()
|
logger = getLogger()
|
||||||
@ -36,9 +41,9 @@ class _Recipes(BaseDocument):
|
|||||||
self.sql_model: RecipeModel = RecipeModel
|
self.sql_model: RecipeModel = RecipeModel
|
||||||
self.schema: Recipe = Recipe
|
self.schema: Recipe = Recipe
|
||||||
|
|
||||||
def get_all_not_private(
|
self.observers = []
|
||||||
self, session: Session, limit: int = None, order_by: str = None, start=0, override_schema=None
|
|
||||||
):
|
def get_all_public(self, session: Session, limit: int = None, order_by: str = None, start=0, override_schema=None):
|
||||||
eff_schema = override_schema or self.schema
|
eff_schema = override_schema or self.schema
|
||||||
|
|
||||||
if order_by:
|
if order_by:
|
||||||
@ -86,6 +91,15 @@ class _Recipes(BaseDocument):
|
|||||||
session, attribute_name=RecipeModel.tags, attr_match=None, count=count, override_schema=override_schema
|
session, attribute_name=RecipeModel.tags, attr_match=None, count=count, override_schema=override_schema
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def subscribe(self, func: Callable) -> None:
|
||||||
|
print("Subscripe", func)
|
||||||
|
self.observers.append(func)
|
||||||
|
|
||||||
|
def update_observers(self) -> None:
|
||||||
|
print("Updating Observers", self.observers)
|
||||||
|
for observer in self.observers:
|
||||||
|
observer()
|
||||||
|
|
||||||
|
|
||||||
class _IngredientFoods(BaseDocument):
|
class _IngredientFoods(BaseDocument):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
@ -16,20 +16,24 @@ class BaseDocument:
|
|||||||
self.store: str
|
self.store: str
|
||||||
self.sql_model: SqlAlchemyBase
|
self.sql_model: SqlAlchemyBase
|
||||||
self.schema: BaseModel
|
self.schema: BaseModel
|
||||||
|
self.observers: list = None
|
||||||
|
|
||||||
def get_all(
|
def get_all(
|
||||||
self, session: Session, limit: int = None, order_by: str = None, start=0, end=9999, override_schema=None
|
self, session: Session, limit: int = None, order_by: str = None, start=0, end=9999, override_schema=None
|
||||||
) -> list[dict]:
|
) -> list[dict]:
|
||||||
|
logger.info("Starting Query")
|
||||||
eff_schema = override_schema or self.schema
|
eff_schema = override_schema or self.schema
|
||||||
|
|
||||||
if order_by:
|
if order_by:
|
||||||
order_attr = getattr(self.sql_model, str(order_by))
|
order_attr = getattr(self.sql_model, str(order_by))
|
||||||
|
logger.info("Ending Query")
|
||||||
|
|
||||||
return [
|
return [
|
||||||
eff_schema.from_orm(x)
|
eff_schema.from_orm(x)
|
||||||
for x in session.query(self.sql_model).order_by(order_attr.desc()).offset(start).limit(limit).all()
|
for x in session.query(self.sql_model).order_by(order_attr.desc()).offset(start).limit(limit).all()
|
||||||
]
|
]
|
||||||
|
|
||||||
|
logger.info("Ending Query")
|
||||||
return [eff_schema.from_orm(x) for x in session.query(self.sql_model).offset(start).limit(limit).all()]
|
return [eff_schema.from_orm(x) for x in session.query(self.sql_model).offset(start).limit(limit).all()]
|
||||||
|
|
||||||
def get_all_limit_columns(self, session: Session, fields: list[str], limit: int = None) -> list[SqlAlchemyBase]:
|
def get_all_limit_columns(self, session: Session, fields: list[str], limit: int = None) -> list[SqlAlchemyBase]:
|
||||||
@ -129,6 +133,9 @@ class BaseDocument:
|
|||||||
session.add(new_document)
|
session.add(new_document)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
if hasattr(self, "update_observers"):
|
||||||
|
self.update_observers()
|
||||||
|
|
||||||
return self.schema.from_orm(new_document)
|
return self.schema.from_orm(new_document)
|
||||||
|
|
||||||
def update(self, session: Session, match_value: str, new_data: dict) -> BaseModel:
|
def update(self, session: Session, match_value: str, new_data: dict) -> BaseModel:
|
||||||
@ -146,6 +153,9 @@ class BaseDocument:
|
|||||||
entry = self._query_one(session=session, match_value=match_value)
|
entry = self._query_one(session=session, match_value=match_value)
|
||||||
entry.update(session=session, **new_data)
|
entry.update(session=session, **new_data)
|
||||||
|
|
||||||
|
if hasattr(self, "update_observers"):
|
||||||
|
self.update_observers()
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
return self.schema.from_orm(entry)
|
return self.schema.from_orm(entry)
|
||||||
|
|
||||||
@ -169,12 +179,18 @@ class BaseDocument:
|
|||||||
session.delete(result)
|
session.delete(result)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
if hasattr(self, "update_observers"):
|
||||||
|
self.update_observers()
|
||||||
|
|
||||||
return results_as_model
|
return results_as_model
|
||||||
|
|
||||||
def delete_all(self, session: Session) -> None:
|
def delete_all(self, session: Session) -> None:
|
||||||
session.query(self.sql_model).delete()
|
session.query(self.sql_model).delete()
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
if hasattr(self, "update_observers"):
|
||||||
|
self.update_observers()
|
||||||
|
|
||||||
def count_all(self, session: Session, match_key=None, match_value=None) -> int:
|
def count_all(self, session: Session, match_key=None, match_value=None) -> int:
|
||||||
if None in [match_key, match_value]:
|
if None in [match_key, match_value]:
|
||||||
return session.query(self.sql_model).count()
|
return session.query(self.sql_model).count()
|
||||||
|
@ -3,14 +3,15 @@ from mealie.db.database import db
|
|||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import is_logged_in
|
from mealie.routes.deps import is_logged_in
|
||||||
from mealie.schema.recipe import RecipeSummary
|
from mealie.schema.recipe import RecipeSummary
|
||||||
|
from mealie.services.recipe.all_recipes import get_all_recipes_public, get_all_recipes_user
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
router = APIRouter(tags=["Query All Recipes"])
|
router = APIRouter(tags=["Query All Recipes"])
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/recipes", response_model=list[RecipeSummary])
|
@router.get("/api/recipes")
|
||||||
async def get_recipe_summary(
|
def get_recipe_summary(
|
||||||
start=0, limit=9999, session: Session = Depends(generate_session), user: bool = Depends(is_logged_in)
|
start=0, limit=9999, session: Session = Depends(generate_session), user: bool = Depends(is_logged_in)
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@ -26,14 +27,10 @@ async def get_recipe_summary(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
return db.recipes.get_all(
|
return get_all_recipes_user(limit, start)
|
||||||
session, limit=limit, start=start, order_by="date_updated", override_schema=RecipeSummary
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return db.recipes.get_all_not_private(
|
return get_all_recipes_public(limit, start)
|
||||||
session, limit=limit, start=start, order_by="date_updated", override_schema=RecipeSummary
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/recipes/summary/untagged", response_model=list[RecipeSummary])
|
@router.get("/api/recipes/summary/untagged", response_model=list[RecipeSummary])
|
||||||
|
38
mealie/services/recipe/all_recipes.py
Normal file
38
mealie/services/recipe/all_recipes.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import json
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
from fastapi import Response
|
||||||
|
from fastapi.encoders import jsonable_encoder
|
||||||
|
from mealie.db.database import db
|
||||||
|
from mealie.db.db_setup import SessionLocal
|
||||||
|
from mealie.schema.recipe import RecipeSummary
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def get_all_recipes_user(limit, start):
|
||||||
|
with SessionLocal() as session:
|
||||||
|
all_recipes: list[RecipeSummary] = db.recipes.get_all(
|
||||||
|
session, limit=limit, start=start, order_by="date_updated", override_schema=RecipeSummary
|
||||||
|
)
|
||||||
|
all_recipes_json = [recipe.dict() for recipe in all_recipes]
|
||||||
|
return Response(content=json.dumps(jsonable_encoder(all_recipes_json)), media_type="application/json")
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def get_all_recipes_public(limit, start):
|
||||||
|
with SessionLocal() as session:
|
||||||
|
all_recipes: list[RecipeSummary] = db.recipes.get_all_public(
|
||||||
|
session, limit=limit, start=start, order_by="date_updated", override_schema=RecipeSummary
|
||||||
|
)
|
||||||
|
all_recipes_json = [recipe.dict() for recipe in all_recipes]
|
||||||
|
return Response(content=json.dumps(jsonable_encoder(all_recipes_json)), media_type="application/json")
|
||||||
|
|
||||||
|
|
||||||
|
def clear_all_cache():
|
||||||
|
print("Cache Cleared")
|
||||||
|
get_all_recipes_user.cache_clear()
|
||||||
|
get_all_recipes_public.cache_clear()
|
||||||
|
|
||||||
|
|
||||||
|
def subscripte_to_recipe_events():
|
||||||
|
db.recipes.subscribe(clear_all_cache)
|
Loading…
x
Reference in New Issue
Block a user