cache-reform

This commit is contained in:
Untone 2024-03-12 15:50:57 +03:00
parent d1a510b093
commit 9b7aa57a18
5 changed files with 50 additions and 30 deletions

View File

@ -44,20 +44,20 @@ async def get_author(_, _info, slug='', author_id=None):
try: try:
if slug: if slug:
q = select(Author).select_from(Author).filter(Author.slug == slug) q = select(Author).select_from(Author).filter(Author.slug == slug)
result = get_authors_with_stat_cached(q) result = await get_authors_with_stat_cached(q)
if result: if result:
[author] = result [author] = result
author_id = author.id author_id = author.id
if author_id: if author_id:
cache = await redis.execute('GET', f'id:{author_id}:author') cache = await redis.execute('GET', f'author:{author_id}')
logger.debug(f'result from cache: {cache}') logger.debug(f'result from cache: {cache}')
q = select(Author).where(Author.id == author_id) q = select(Author).where(Author.id == author_id)
author_dict = None author_dict = None
if cache: if cache:
author_dict = json.loads(cache) author_dict = json.loads(cache)
else: else:
result = get_authors_with_stat_cached(q) result = await get_authors_with_stat_cached(q)
if result: if result:
[author] = result [author] = result
author_dict = author.dict() author_dict = author.dict()
@ -77,7 +77,7 @@ async def get_author(_, _info, slug='', author_id=None):
async def get_author_by_user_id(user_id: str): async def get_author_by_user_id(user_id: str):
logger.info(f'getting author id for {user_id}') logger.info(f'getting author id for {user_id}')
redis_key = f'user:{user_id}:author' redis_key = f'user:{user_id}'
author = None author = None
try: try:
res = await redis.execute('GET', redis_key) res = await redis.execute('GET', redis_key)
@ -90,7 +90,7 @@ async def get_author_by_user_id(user_id: str):
return author return author
q = select(Author).filter(Author.user == user_id) q = select(Author).filter(Author.user == user_id)
result = get_authors_with_stat_cached(q) result = await get_authors_with_stat_cached(q)
if result: if result:
[author] = result [author] = result
await set_author_cache(author.dict()) await set_author_cache(author.dict())
@ -108,7 +108,7 @@ async def get_author_id(_, _info, user: str):
@query.field('load_authors_by') @query.field('load_authors_by')
def load_authors_by(_, _info, by, limit, offset): async def load_authors_by(_, _info, by, limit, offset):
logger.debug(f'loading authors by {by}') logger.debug(f'loading authors by {by}')
q = select(Author) q = select(Author)
if by.get('slug'): if by.get('slug'):
@ -137,7 +137,7 @@ def load_authors_by(_, _info, by, limit, offset):
# q = q.distinct() # q = q.distinct()
q = q.limit(limit).offset(offset) q = q.limit(limit).offset(offset)
authors = get_authors_with_stat_cached(q) authors = await get_authors_with_stat_cached(q)
return authors return authors
@ -266,7 +266,7 @@ async def get_author_followers(_, _info, slug: str):
author_follower_alias.follower == Author.id, author_follower_alias.follower == Author.id,
), ),
) )
results = get_authors_with_stat_cached(q) results = await get_authors_with_stat_cached(q)
_ = asyncio.create_task( _ = asyncio.create_task(
update_author_followers_cache( update_author_followers_cache(
author_id, [x.dict() for x in results] author_id, [x.dict() for x in results]
@ -286,6 +286,6 @@ async def get_author_followers(_, _info, slug: str):
@query.field('search_authors') @query.field('search_authors')
def search_authors(_, _info, what: str): async def search_authors(_, _info, what: str):
q = search(select(Author), what) q = search(select(Author), what)
return get_authors_with_stat_cached(q) return await get_authors_with_stat_cached(q)

View File

@ -12,7 +12,8 @@ from orm.community import Community
from orm.reaction import Reaction from orm.reaction import Reaction
from orm.shout import Shout, ShoutReactionsFollower from orm.shout import Shout, ShoutReactionsFollower
from orm.topic import Topic, TopicFollower from orm.topic import Topic, TopicFollower
from resolvers.stat import get_authors_with_stat_cached, author_follows_topics, author_follows_authors, get_with_stat from resolvers.stat import get_authors_with_stat_cached, author_follows_topics, author_follows_authors, get_with_stat, \
get_topics_with_stat_cached
from services.auth import login_required from services.auth import login_required
from services.db import local_session from services.db import local_session
from services.cache import ( from services.cache import (
@ -34,7 +35,7 @@ async def follow(_, info, what, slug):
user_id = info.context.get('user_id') user_id = info.context.get('user_id')
if not user_id: if not user_id:
return {"error": "unauthorized"} return {"error": "unauthorized"}
[follower] = get_authors_with_stat_cached(select(Author).select_from(Author).filter(Author.user == user_id)) [follower] = await get_authors_with_stat_cached(select(Author).select_from(Author).filter(Author.user == user_id))
if not follower: if not follower:
return {"error": "cant find follower"} return {"error": "cant find follower"}
@ -42,7 +43,7 @@ async def follow(_, info, what, slug):
error = author_follow(follower.id, slug) error = author_follow(follower.id, slug)
if not error: if not error:
logger.debug(f'@{follower.slug} followed @{slug}') logger.debug(f'@{follower.slug} followed @{slug}')
[author] = get_authors_with_stat_cached(select(Author).select_from(Author).where(Author.slug == slug)) [author] = await get_authors_with_stat_cached(select(Author).select_from(Author).where(Author.slug == slug))
if not author: if not author:
return {"error": "author is not found"} return {"error": "author is not found"}
follows = await update_follows_for_author(follower, 'author', author.dict(), True) follows = await update_follows_for_author(follower, 'author', author.dict(), True)
@ -52,7 +53,7 @@ async def follow(_, info, what, slug):
elif what == 'TOPIC': elif what == 'TOPIC':
error = topic_follow(follower.id, slug) error = topic_follow(follower.id, slug)
if not error: if not error:
[topic] = get_with_stat(select(Topic).where(Topic.slug == slug)) [topic] = await get_topics_with_stat_cached(select(Topic).where(Topic.slug == slug))
if not topic: if not topic:
return {"error": "topic is not found"} return {"error": "topic is not found"}
follows = await update_follows_for_author(follower, 'topic', topic.dict(), True) follows = await update_follows_for_author(follower, 'topic', topic.dict(), True)
@ -80,7 +81,7 @@ async def unfollow(_, info, what, slug):
if not user_id: if not user_id:
return {"error": "unauthorized"} return {"error": "unauthorized"}
follower_query = select(Author).filter(Author.user == user_id) follower_query = select(Author).filter(Author.user == user_id)
[follower] = get_authors_with_stat_cached(follower_query) [follower] = await get_authors_with_stat_cached(follower_query)
if not follower: if not follower:
return {"error": "follower profile is not found"} return {"error": "follower profile is not found"}
@ -88,7 +89,7 @@ async def unfollow(_, info, what, slug):
error = author_unfollow(follower.id, slug) error = author_unfollow(follower.id, slug)
if not error: if not error:
logger.info(f'@{follower.slug} unfollowing @{slug}') logger.info(f'@{follower.slug} unfollowing @{slug}')
[author] = get_authors_with_stat_cached(select(Author).where(Author.slug == slug)) [author] = await get_authors_with_stat_cached(select(Author).where(Author.slug == slug))
if not author: if not author:
return {"error": "cant find author"} return {"error": "cant find author"}
_followers = await update_followers_for_author(follower, author, False) _followers = await update_followers_for_author(follower, author, False)
@ -99,7 +100,7 @@ async def unfollow(_, info, what, slug):
error = topic_unfollow(follower.id, slug) error = topic_unfollow(follower.id, slug)
if not error: if not error:
logger.info(f'@{follower.slug} unfollowing §{slug}') logger.info(f'@{follower.slug} unfollowing §{slug}')
[topic] = get_with_stat(select(Topic).where(Topic.slug == slug)) [topic] = await get_topics_with_stat_cached(select(Topic).where(Topic.slug == slug))
if not topic: if not topic:
return {"error": "cant find topic"} return {"error": "cant find topic"}
follows = await update_follows_for_author(follower, 'topic', topic.dict(), False) follows = await update_follows_for_author(follower, 'topic', topic.dict(), False)
@ -123,7 +124,7 @@ async def unfollow(_, info, what, slug):
async def get_follows_by_user_id(user_id: str): async def get_follows_by_user_id(user_id: str):
if not user_id: if not user_id:
return {"error": "unauthorized"} return {"error": "unauthorized"}
author = await redis.execute('GET', f'user:{user_id}:author') author = await redis.execute('GET', f'user:{user_id}')
if isinstance(author, str): if isinstance(author, str):
author = json.loads(author) author = json.loads(author)
if not author: if not author:

View File

@ -204,7 +204,21 @@ async def get_authors_with_stat_cached(q):
records = [] records = []
with local_session() as session: with local_session() as session:
for x in session.execute(q): for x in session.execute(q):
stat_str = await redis.execute('GET', f'id:{x.id}:{'author'}') stat_str = await redis.execute('GET', f'author:{x.id}')
if isinstance(stat_str, str):
x.stat = json.loads(stat_str).get('stat')
records.append(x)
except Exception as exc:
raise Exception(exc)
return records
async def get_topics_with_stat_cached(q):
try:
records = []
with local_session() as session:
for x in session.execute(q):
stat_str = await redis.execute('GET', f'topic:{x.id}')
if isinstance(stat_str, str): if isinstance(stat_str, str):
x.stat = json.loads(stat_str).get('stat') x.stat = json.loads(stat_str).get('stat')
records.append(x) records.append(x)

View File

@ -3,25 +3,25 @@ from sqlalchemy import distinct, func, select
from orm.author import Author from orm.author import Author
from orm.shout import ShoutTopic from orm.shout import ShoutTopic
from orm.topic import Topic from orm.topic import Topic
from resolvers.stat import get_with_stat from resolvers.stat import get_topics_with_stat_cached
from services.auth import login_required from services.auth import login_required
from services.db import local_session from services.db import local_session
from services.schema import mutation, query from services.schema import mutation, query
@query.field('get_topics_all') @query.field('get_topics_all')
def get_topics_all(_, _info): async def get_topics_all(_, _info):
return get_with_stat(select(Topic)) return await get_topics_with_stat_cached(select(Topic))
@query.field('get_topics_by_community') @query.field('get_topics_by_community')
def get_topics_by_community(_, _info, community_id: int): async def get_topics_by_community(_, _info, community_id: int):
q = select(Topic).where(Topic.community == community_id) q = select(Topic).where(Topic.community == community_id)
return get_with_stat(q) return await get_topics_with_stat_cached(q)
@query.field('get_topics_by_author') @query.field('get_topics_by_author')
def get_topics_by_author(_, _info, author_id=0, slug='', user=''): async def get_topics_by_author(_, _info, author_id=0, slug='', user=''):
q = select(Topic) q = select(Topic)
if author_id: if author_id:
q = q.join(Author).where(Author.id == author_id) q = q.join(Author).where(Author.id == author_id)
@ -30,13 +30,13 @@ def get_topics_by_author(_, _info, author_id=0, slug='', user=''):
elif user: elif user:
q = q.join(Author).where(Author.user == user) q = q.join(Author).where(Author.user == user)
return get_with_stat(q) return await get_topics_with_stat_cached(q)
@query.field('get_topic') @query.field('get_topic')
def get_topic(_, _info, slug: str): async def get_topic(_, _info, slug: str):
q = select(Topic).filter(Topic.slug == slug) q = select(Topic).filter(Topic.slug == slug)
topics = get_with_stat(q) topics = await get_topics_with_stat_cached(q)
if topics: if topics:
return topics[0] return topics[0]

View File

@ -22,8 +22,13 @@ DEFAULT_FOLLOWS = {
async def set_author_cache(author: dict): async def set_author_cache(author: dict):
payload = json.dumps(author, cls=CustomJSONEncoder) payload = json.dumps(author, cls=CustomJSONEncoder)
await redis.execute("SET", f'user:{author.get("user")}:author', payload) await redis.execute("SET", f'user:{author.get("user")}', payload)
await redis.execute("SET", f'id:{author.get("id")}:author', payload) await redis.execute("SET", f'author:{author.get("id")}', payload)
async def set_topic_cache(topic: dict):
payload = json.dumps(topic, cls=CustomJSONEncoder)
await redis.execute("SET", f'topic:{topic.get("id")}', payload)
async def update_author_followers_cache(author_id: int, followers): async def update_author_followers_cache(author_id: int, followers):