better caching for get_alt_graph

pull/83/head
Aevann 2022-12-22 23:24:36 +02:00
parent 5d09ef1103
commit a54734c32f
3 changed files with 24 additions and 35 deletions

View File

@ -689,6 +689,9 @@ def admin_add_alt(v:User, username):
g.db.add(a)
g.db.flush()
cache.delete_memoized(get_alt_graph, user1.id)
cache.delete_memoized(get_alt_graph, user2.id)
check_for_alts(user1, include_current_session=False)
check_for_alts(user2, include_current_session=False)

View File

@ -2,8 +2,8 @@ import time
import secrets
from random import randint
from typing import Optional, Union, Callable
from sqlalchemy.orm import aliased, deferred, Query
from typing import Optional, Union, Callable, List
from sqlalchemy.orm import aliased, deferred
from sqlalchemy.sql import case, literal
from sqlalchemy.sql.expression import or_
@ -29,51 +29,38 @@ def validate_formkey(u:User, formkey:Optional[str]) -> bool:
if not formkey: return False
return validate_hash(get_raw_formkey(u), formkey)
@cache.memoize(timeout=3600)
def get_alt_graph(uid:int, alt_filter:Optional[Callable[[Query], Query]]=None, **kwargs) -> Query:
'''
Gets the full graph of alts (optionally filtering `Alt` objects by criteria using a callable,
such as by a date to only get alts from a certain date) as a query of users that can be filtered
further. This function filters alts marked as deleted by default, pass `include_deleted=True` to
disable this behavior and include delinked alts.
'''
if not alt_filter:
alt_filter = lambda q:q
if not kwargs.get('include_deleted', False):
deleted_filter = lambda q:q.filter(Alt.deleted == False)
else:
deleted_filter = lambda q:q
combined_filter = lambda q:deleted_filter(alt_filter(q))
@cache.memoize(timeout=604800)
def get_alt_graph(uid:int) -> List[User]:
print(uid, flush=True)
alt_graph_cte = g.db.query(literal(uid).label('user_id')).select_from(Alt).cte('alt_graph', recursive=True)
alt_graph_cte_inner = combined_filter(g.db.query(
alt_graph_cte_inner = g.db.query(
case(
(Alt.user1 == alt_graph_cte.c.user_id, Alt.user2),
(Alt.user2 == alt_graph_cte.c.user_id, Alt.user1),
)
).select_from(Alt, alt_graph_cte).filter(
or_(alt_graph_cte.c.user_id == Alt.user1, alt_graph_cte.c.user_id == Alt.user2)
))
)
alt_graph_cte = alt_graph_cte.union(alt_graph_cte_inner)
return g.db.query(User).filter(User.id == alt_graph_cte.c.user_id, User.id != uid).order_by(User.username).all()
def add_alt(user1:int, user2:int):
li = [user1, user2]
existing = g.db.query(Alt).filter(Alt.user1.in_(li), Alt.user2.in_(li)).one_or_none()
if not existing:
new_alt = Alt(user1=user1, user2=user2)
g.db.add(new_alt)
g.db.flush()
cache.delete_memoized(get_alt_graph, user1)
cache.delete_memoized(get_alt_graph, user2)
def check_for_alts(current:User, include_current_session=True):
current_id = current.id
ids = [x[0] for x in g.db.query(User.id).all()]
past_accs = set(session.get("history", [])) if include_current_session else set()
def add_alt(user1:int, user2:int):
li = [user1, user2]
existing = g.db.query(Alt).filter(Alt.user1.in_(li), Alt.user2.in_(li)).one_or_none()
if not existing:
new_alt = Alt(user1=user1, user2=user2)
g.db.add(new_alt)
g.db.flush()
for past_id in list(past_accs):
if past_id not in ids:
past_accs.remove(past_id)

View File

@ -21,7 +21,7 @@ from files.helpers.mail import *
from files.helpers.sanitize import *
from files.helpers.sorting_and_time import *
from files.helpers.useractions import badge_grant
from files.routes.routehelpers import check_for_alts
from files.routes.routehelpers import check_for_alts, add_alt
from files.routes.wrappers import *
from files.__main__ import app, cache, limiter
@ -1168,11 +1168,10 @@ def fp(v:User, fp):
li = [v.id, u.id]
existing = g.db.query(Alt).filter(Alt.user1.in_(li), Alt.user2.in_(li)).one_or_none()
if existing: continue
new_alt = Alt(user1=v.id, user2=u.id)
g.db.add(new_alt)
g.db.flush()
add_alt(user1=v.id, user2=u.id)
print(v.username + ' + ' + u.username, flush=True)
check_for_alts(v)
check_for_alts(v)
g.db.add(v)
return '', 204