From 1592065a8c011232d39736694e5f69432dfc4aef Mon Sep 17 00:00:00 2001 From: Untone Date: Tue, 21 May 2024 02:01:18 +0300 Subject: [PATCH] postfixing-reimplemented-cache --- resolvers/author.py | 6 +++--- resolvers/follower.py | 2 +- resolvers/stat.py | 14 +++----------- resolvers/topic.py | 18 +++++++++++++++--- services/auth.py | 3 ++- services/cache.py | 11 +++++------ 6 files changed, 29 insertions(+), 25 deletions(-) diff --git a/resolvers/author.py b/resolvers/author.py index 56d0fb7e..eaa36ae0 100644 --- a/resolvers/author.py +++ b/resolvers/author.py @@ -73,7 +73,7 @@ async def get_author(_, _info, slug="", author_id=0): if found_author: logger.debug(f"found author id: {found_author.id}") author_id = found_author.id if found_author.id else author_id - author_dict = await get_cached_author(author_id) + author_dict = await get_cached_author(int(author_id), get_with_stat) # update stat from db if not author_dict or not author_dict.get("stat"): @@ -99,7 +99,7 @@ async def get_author_by_user_id(user_id: str): logger.info(f"getting author id for {user_id}") author = None try: - author = await get_cached_author_by_user_id(user_id) + author = await get_cached_author_by_user_id(user_id, get_with_stat) if author: return author @@ -150,7 +150,7 @@ async def load_authors_by(_, _info, by, limit, offset): for [a] in authors_nostat: author_dict = None if isinstance(a, Author): - author_dict = await get_cached_author(a.id) + author_dict = await get_cached_author(a.id, get_with_stat) if not author_dict or not isinstance(author_dict.get("shouts"), int): break diff --git a/resolvers/follower.py b/resolvers/follower.py index 939186ce..96d54b19 100644 --- a/resolvers/follower.py +++ b/resolvers/follower.py @@ -135,7 +135,7 @@ async def unfollow(_, info, what, slug): async def get_follows_by_user_id(user_id: str): if not user_id: return {"error": "unauthorized"} - author = await get_cached_author_by_user_id(user_id) + author = await get_cached_author_by_user_id(user_id, get_with_stat) if not author: with local_session() as session: author = session.query(Author).filter(Author.user == user_id).first() diff --git a/resolvers/stat.py b/resolvers/stat.py index 15829dd6..88003328 100644 --- a/resolvers/stat.py +++ b/resolvers/stat.py @@ -56,9 +56,9 @@ def get_topic_shouts_stat(topic_id: int): return result[0] if result else 0 -def get_topic_authors_query(topic_id): - return ( - select(ShoutAuthor.author) +def get_topic_authors_stat(topic_id: int): + count_query = ( + select(func.count(distinct(ShoutAuthor.author))) .select_from(join(ShoutTopic, Shout, ShoutTopic.shout == Shout.id)) .join(ShoutAuthor, ShoutAuthor.shout == Shout.id) .filter( @@ -70,14 +70,6 @@ def get_topic_authors_query(topic_id): ) ) - -def get_topic_authors_stat(topic_id: int): - # authors query - topic_authors_query = get_topic_authors_query(topic_id) - - # Оборачиваем запрос в другой запрос, чтобы посчитать уникальных авторов - count_query = select(func.count(distinct(topic_authors_query.subquery().c.author))) - # Выполняем запрос и получаем результат result = local_session().execute(count_query).scalar() return result if result is not None else 0 diff --git a/resolvers/topic.py b/resolvers/topic.py index 6e937171..5a80911e 100644 --- a/resolvers/topic.py +++ b/resolvers/topic.py @@ -1,7 +1,7 @@ -from sqlalchemy import distinct, func, select +from sqlalchemy import and_, distinct, func, join, select from orm.author import Author -from orm.shout import ShoutTopic +from orm.shout import Shout, ShoutAuthor, ShoutTopic from orm.topic import Topic from resolvers.stat import get_with_stat from services.auth import login_required @@ -144,5 +144,17 @@ async def get_topic_authors(_, _info, slug: str): topic_query = select(Topic.id).filter(Topic.slug == slug).first() topic_id_result = local_session().execute(topic_query) topic_id = topic_id_result[0] if topic_id_result else None - authors = await get_cached_topic_authors(topic_id) + topic_authors_query = ( + select(ShoutAuthor.author) + .select_from(join(ShoutTopic, Shout, ShoutTopic.shout == Shout.id)) + .join(ShoutAuthor, ShoutAuthor.shout == Shout.id) + .filter( + and_( + ShoutTopic.topic == topic_id, + Shout.published_at.is_not(None), + Shout.deleted_at.is_(None), + ) + ) + ) + authors = await get_cached_topic_authors(topic_id, topic_authors_query) return authors diff --git a/services/auth.py b/services/auth.py index 66c4e8fe..5a1d5066 100644 --- a/services/auth.py +++ b/services/auth.py @@ -2,6 +2,7 @@ from functools import wraps import httpx +from resolvers.stat import get_with_stat from services.cache import get_cached_author_by_user_id from services.logger import root_logger as logger from settings import ADMIN_SECRET, AUTH_URL @@ -86,7 +87,7 @@ def login_required(f): logger.info(f" got {user_id} roles: {user_roles}") info.context["user_id"] = user_id.strip() info.context["roles"] = user_roles - author = await get_cached_author_by_user_id(user_id) + author = await get_cached_author_by_user_id(user_id, get_with_stat) if not author: logger.error(f"author profile not found for user {user_id}") info.context["author"] = author diff --git a/services/cache.py b/services/cache.py index 34e1184a..5c78e7a9 100644 --- a/services/cache.py +++ b/services/cache.py @@ -4,7 +4,6 @@ from sqlalchemy import and_, join, select from orm.author import Author, AuthorFollower from orm.topic import Topic, TopicFollower -from resolvers.stat import get_topic_authors_query, get_with_stat from services.db import local_session from services.encoders import CustomJSONEncoder from services.logger import root_logger as logger @@ -115,7 +114,7 @@ async def cache_follows( return follows -async def get_cached_author(author_id: int): +async def get_cached_author(author_id: int, get_with_stat): if author_id: rkey = f"author:id:{author_id}" cached_result = await redis.execute("GET", rkey) @@ -128,10 +127,10 @@ async def get_cached_author(author_id: int): await cache_author(author.dict()) -async def get_cached_author_by_user_id(user_id: str): +async def get_cached_author_by_user_id(user_id: str, get_with_stat): author_id = await redis.execute("GET", f"user:id:{user_id}") if author_id: - return await get_cached_author(int(author_id)) + return await get_cached_author(int(author_id), get_with_stat) async def get_cached_author_follows_topics(author_id: int): @@ -224,7 +223,7 @@ async def get_cached_topic_followers(topic_id: int): return followers -async def get_cached_topic_authors(topic_id: int): +async def get_cached_topic_authors(topic_id: int, topic_authors_query): authors = [] rkey = f"topic:authors:{topic_id}" cached = await redis.execute("GET", rkey) @@ -233,7 +232,7 @@ async def get_cached_topic_authors(topic_id: int): if isinstance(authors, list): return authors - authors = local_session().execute(get_topic_authors_query(topic_id)) + authors = local_session().execute(topic_authors_query) # should be id list if authors: await redis.execute("SET", rkey, json.dumps(authors))