cache-upgrade
All checks were successful
Deploy on push / deploy (push) Successful in 22s

This commit is contained in:
Untone 2024-04-08 21:33:47 +03:00
parent d1cd69eb2a
commit b802bb029a
3 changed files with 76 additions and 11 deletions

View File

@ -307,6 +307,7 @@ async def delete_reaction(_, info, reaction_id: int):
session.delete(r) session.delete(r)
session.commit() session.commit()
await notify_reaction(reaction_dict, 'delete') await notify_reaction(reaction_dict, 'delete')
return {'error': None, 'reaction': reaction_dict} return {'error': None, 'reaction': reaction_dict}
except Exception as exc: except Exception as exc:
return {'error': f'cannot delete reaction: {exc}'} return {'error': f'cannot delete reaction: {exc}'}

View File

@ -17,9 +17,10 @@ def add_topic_stat_columns(q):
aliased_shout_author = aliased(ShoutAuthor) aliased_shout_author = aliased(ShoutAuthor)
aliased_topic_follower = aliased(TopicFollower) aliased_topic_follower = aliased(TopicFollower)
aliased_shout_topic = aliased(ShoutTopic) aliased_shout_topic = aliased(ShoutTopic)
aliased_shout = aliased(Shout)
q = ( q = (
q.outerjoin(aliased_shout_topic, aliased_shout_topic.topic == Topic.id) q.outerjoin(aliased_shout_topic, and_(aliased_shout_topic.topic == Topic.id, aliased_shout.published_at.is_not(None)))
.add_columns( .add_columns(
func.count(distinct(aliased_shout_topic.shout)).label('shouts_stat') func.count(distinct(aliased_shout_topic.shout)).label('shouts_stat')
) )

View File

@ -24,20 +24,67 @@ async def set_author_cache(author: dict):
await redis.execute('SET', f'user:{author.get("user")}', payload) await redis.execute('SET', f'user:{author.get("user")}', payload)
await redis.execute('SET', f'author:{author.get("id")}', payload) await redis.execute('SET', f'author:{author.get("id")}', payload)
# update stat all field for followers' caches in <authors> list
followers_str = await redis.execute('GET', f'author:{author.get("id")}:followers')
followers = []
if followers_str:
followers = json.loads(followers_str)
if isinstance(followers, list):
for follower in followers:
follower_follows_authors = []
follower_follows_authors_str = await redis.execute('GET', f'author:{author.get("id")}:follows-authors')
if follower_follows_authors_str:
follower_follows_authors = json.loads(follower_follows_authors_str)
c = 0
for old_author in follower_follows_authors:
if int(old_author.get('id')) == int(author.get('id', 0)):
follower_follows_authors[c] = author
break # exit the loop since we found and updated the author
c += 1
else:
# author not found in the list, so add the new author with the updated stat field
follower_follows_authors.append(author)
# update stat field for all authors' caches in <followers> list
follows_str = await redis.execute('GET', f'author:{author.get("id")}:follows-authors')
follows_authors = []
if follows_str:
follows_authors = json.loads(follows_str)
if isinstance(follows_authors, list):
for followed_author in follows_authors:
followed_author_followers = []
followed_author_followers_str = await redis.execute('GET', f'author:{author.get("id")}:followers')
if followed_author_followers_str:
followed_author_followers = json.loads(followed_author_followers_str)
c = 0
for old_follower in followed_author_followers:
if int(old_follower.get('id')) == int(author.get('id', 0)):
followed_author_followers[c] = author
break # exit the loop since we found and updated the author
c += 1
else:
# author not found in the list, so add the new author with the updated stat field
followed_author_followers.append(author)
async def update_author_followers_cache(author_id: int, followers):
updated_followers = [f.dict() if isinstance(f, Author) else f for f in followers]
payload = json.dumps(
updated_followers,
cls=CustomJSONEncoder,
)
await redis.execute('SET', f'author:{author_id}:followers', payload)
author_str = await redis.execute('GET', f'author:{author_id}')
if author_str:
author = json.loads(author_str)
author['stat']['followers'] = len(updated_followers)
await set_author_cache(author)
async def set_topic_cache(topic: dict): async def set_topic_cache(topic: dict):
payload = json.dumps(topic, cls=CustomJSONEncoder) payload = json.dumps(topic, cls=CustomJSONEncoder)
await redis.execute('SET', f'topic:{topic.get("id")}', payload) await redis.execute('SET', f'topic:{topic.get("id")}', payload)
async def update_author_followers_cache(author_id: int, followers):
payload = json.dumps(
[f.dict() if isinstance(f, Author) else f for f in followers],
cls=CustomJSONEncoder,
)
await redis.execute('SET', f'author:{author_id}:followers', payload)
async def set_follows_topics_cache(follows, author_id: int): async def set_follows_topics_cache(follows, author_id: int):
try: try:
payload = json.dumps( payload = json.dumps(
@ -54,12 +101,19 @@ async def set_follows_topics_cache(follows, author_id: int):
async def set_follows_authors_cache(follows, author_id: int): async def set_follows_authors_cache(follows, author_id: int):
updated_follows = [a.dict() if isinstance(a, Author) else a for a in follows]
try: try:
payload = json.dumps( payload = json.dumps(
[a.dict() if isinstance(a, Author) else a for a in follows], updated_follows,
cls=CustomJSONEncoder, cls=CustomJSONEncoder,
) )
await redis.execute('SET', f'author:{author_id}:follows-authors', payload) await redis.execute('SET', f'author:{author_id}:follows-authors', payload)
# update author everywhere
author_str = await redis.execute('GET', f'author:{author_id}')
if author_str:
author = json.loads(author_str)
author['stat']['authors'] = len(updated_follows)
await set_author_cache(author)
except Exception as exc: except Exception as exc:
import traceback import traceback
@ -110,12 +164,13 @@ async def update_followers_for_author(
def after_shout_update(_mapper, _connection, shout: Shout): def after_shout_update(_mapper, _connection, shout: Shout):
logger.info('after shout update')
# Main query to get authors associated with the shout through ShoutAuthor # Main query to get authors associated with the shout through ShoutAuthor
authors_query = ( authors_query = (
select(Author) select(Author)
.select_from(ShoutAuthor) # Select from ShoutAuthor .select_from(ShoutAuthor) # Select from ShoutAuthor
.join(Author, Author.id == ShoutAuthor.author) # Join with Author .join(Author, Author.id == ShoutAuthor.author) # Join with Author
.where(ShoutAuthor.shout == shout.id) # Filter by shout.id .filter(ShoutAuthor.shout == shout.id) # Filter by shout.id
) )
for author_with_stat in get_with_stat(authors_query): for author_with_stat in get_with_stat(authors_query):
@ -123,6 +178,7 @@ def after_shout_update(_mapper, _connection, shout: Shout):
def after_reaction_update(mapper, connection, reaction: Reaction): def after_reaction_update(mapper, connection, reaction: Reaction):
logger.info('after reaction update')
try: try:
author_subquery = select(Author).where(Author.id == reaction.created_by) author_subquery = select(Author).where(Author.id == reaction.created_by)
replied_author_subquery = ( replied_author_subquery = (
@ -157,6 +213,7 @@ def after_reaction_update(mapper, connection, reaction: Reaction):
def after_author_update(_mapper, _connection, author: Author): def after_author_update(_mapper, _connection, author: Author):
logger.info('after author update')
q = select(Author).where(Author.id == author.id) q = select(Author).where(Author.id == author.id)
result = get_with_stat(q) result = get_with_stat(q)
if result: if result:
@ -165,24 +222,28 @@ def after_author_update(_mapper, _connection, author: Author):
def after_topic_follower_insert(_mapper, _connection, target: TopicFollower): def after_topic_follower_insert(_mapper, _connection, target: TopicFollower):
logger.info(target)
asyncio.create_task( asyncio.create_task(
handle_topic_follower_change(target.topic, target.follower, True) handle_topic_follower_change(target.topic, target.follower, True)
) )
def after_topic_follower_delete(_mapper, _connection, target: TopicFollower): def after_topic_follower_delete(_mapper, _connection, target: TopicFollower):
logger.info(target)
asyncio.create_task( asyncio.create_task(
handle_topic_follower_change(target.topic, target.follower, False) handle_topic_follower_change(target.topic, target.follower, False)
) )
def after_author_follower_insert(_mapper, _connection, target: AuthorFollower): def after_author_follower_insert(_mapper, _connection, target: AuthorFollower):
logger.info(target)
asyncio.create_task( asyncio.create_task(
handle_author_follower_change(target.author, target.follower, True) handle_author_follower_change(target.author, target.follower, True)
) )
def after_author_follower_delete(_mapper, _connection, target: AuthorFollower): def after_author_follower_delete(_mapper, _connection, target: AuthorFollower):
logger.info(target)
asyncio.create_task( asyncio.create_task(
handle_author_follower_change(target.author, target.follower, False) handle_author_follower_change(target.author, target.follower, False)
) )
@ -191,6 +252,7 @@ def after_author_follower_delete(_mapper, _connection, target: AuthorFollower):
async def handle_author_follower_change( async def handle_author_follower_change(
author_id: int, follower_id: int, is_insert: bool author_id: int, follower_id: int, is_insert: bool
): ):
logger.info(author_id)
author_query = select(Author).select_from(Author).filter(Author.id == author_id) author_query = select(Author).select_from(Author).filter(Author.id == author_id)
[author] = get_with_stat(author_query) [author] = get_with_stat(author_query)
follower_query = select(Author).select_from(Author).filter(Author.id == follower_id) follower_query = select(Author).select_from(Author).filter(Author.id == follower_id)
@ -224,6 +286,7 @@ async def handle_author_follower_change(
async def handle_topic_follower_change( async def handle_topic_follower_change(
topic_id: int, follower_id: int, is_insert: bool topic_id: int, follower_id: int, is_insert: bool
): ):
logger.info(topic_id)
topic_query = select(Topic).filter(Topic.id == topic_id) topic_query = select(Topic).filter(Topic.id == topic_id)
[topic] = get_with_stat(topic_query) [topic] = get_with_stat(topic_query)
follower_query = select(Author).filter(Author.id == follower_id) follower_query = select(Author).filter(Author.id == follower_id)