merged
Some checks failed
Deploy on push / deploy (push) Failing after 11s

This commit is contained in:
Untone 2024-08-12 11:00:01 +03:00
parent 208de158bc
commit c5ee827230
11 changed files with 103 additions and 71 deletions

8
cache/cache.py vendored
View File

@ -1,13 +1,15 @@
import asyncio import asyncio
import json import json
from typing import List from typing import List
from sqlalchemy import select, join, and_
from sqlalchemy import and_, join, select
from orm.author import Author, AuthorFollower from orm.author import Author, AuthorFollower
from orm.topic import Topic, TopicFollower
from orm.shout import Shout, ShoutAuthor, ShoutTopic from orm.shout import Shout, ShoutAuthor, ShoutTopic
from orm.topic import Topic, TopicFollower
from services.db import local_session from services.db import local_session
from utils.encoders import CustomJSONEncoder
from services.redis import redis from services.redis import redis
from utils.encoders import CustomJSONEncoder
from utils.logger import root_logger as logger from utils.logger import root_logger as logger
DEFAULT_FOLLOWS = { DEFAULT_FOLLOWS = {

10
cache/precache.py vendored
View File

@ -1,15 +1,17 @@
import json
import asyncio import asyncio
import json
from sqlalchemy import and_, join, select from sqlalchemy import and_, join, select
from cache.cache import cache_author, cache_topic
from orm.author import Author, AuthorFollower from orm.author import Author, AuthorFollower
from orm.shout import Shout, ShoutAuthor, ShoutTopic, ShoutReactionsFollower from orm.shout import Shout, ShoutAuthor, ShoutReactionsFollower, ShoutTopic
from orm.topic import Topic, TopicFollower from orm.topic import Topic, TopicFollower
from resolvers.stat import get_with_stat from resolvers.stat import get_with_stat
from cache.cache import cache_author, cache_topic
from services.db import local_session from services.db import local_session
from services.redis import redis
from utils.encoders import CustomJSONEncoder from utils.encoders import CustomJSONEncoder
from utils.logger import root_logger as logger from utils.logger import root_logger as logger
from services.redis import redis
# Предварительное кеширование подписчиков автора # Предварительное кеширование подписчиков автора

View File

@ -13,7 +13,6 @@ from services.exception import ExceptionHandlerMiddleware
from services.redis import redis from services.redis import redis
from services.schema import resolvers from services.schema import resolvers
from services.search import search_service from services.search import search_service
from services.sentry import start_sentry
from services.viewed import ViewedStorage from services.viewed import ViewedStorage
from services.webhook import WebhookEndpoint from services.webhook import WebhookEndpoint
from settings import DEV_SERVER_PID_FILE_NAME, MODE from settings import DEV_SERVER_PID_FILE_NAME, MODE
@ -42,7 +41,7 @@ app = Starlette(
precache_data, precache_data,
ViewedStorage.init, ViewedStorage.init,
search_service.info, search_service.info,
start_sentry, # start_sentry,
start, start,
revalidation_manager.start, revalidation_manager.start,
], ],

View File

@ -3,11 +3,6 @@ import time
from sqlalchemy import desc, select, text from sqlalchemy import desc, select, text
from orm.author import Author
from orm.shout import ShoutAuthor, ShoutTopic
from orm.topic import Topic
from resolvers.stat import get_with_stat
from services.auth import login_required
from cache.cache import ( from cache.cache import (
cache_author, cache_author,
get_cached_author, get_cached_author,
@ -16,9 +11,14 @@ from cache.cache import (
get_cached_follower_authors, get_cached_follower_authors,
get_cached_follower_topics, get_cached_follower_topics,
) )
from orm.author import Author
from orm.shout import ShoutAuthor, ShoutTopic
from orm.topic import Topic
from resolvers.stat import get_with_stat
from services.auth import login_required
from services.db import local_session from services.db import local_session
from utils.logger import root_logger as logger
from services.schema import mutation, query from services.schema import mutation, query
from utils.logger import root_logger as logger
@mutation.field("update_author") @mutation.field("update_author")

View File

@ -1,5 +1,6 @@
import time import time
from sqlalchemy import and_, case, desc, func, select, asc
from sqlalchemy import and_, asc, case, desc, func, select
from sqlalchemy.orm import aliased from sqlalchemy.orm import aliased
from orm.author import Author from orm.author import Author
@ -11,9 +12,9 @@ from resolvers.follower import follow
from resolvers.stat import update_author_stat from resolvers.stat import update_author_stat
from services.auth import add_user_role, login_required from services.auth import add_user_role, login_required
from services.db import local_session from services.db import local_session
from utils.logger import root_logger as logger
from services.notify import notify_reaction from services.notify import notify_reaction
from services.schema import mutation, query from services.schema import mutation, query
from utils.logger import root_logger as logger
def query_reactions(): def query_reactions():

View File

@ -280,35 +280,39 @@ async def get_shout(_, info, slug: str):
""" """
try: try:
with local_session() as session: with local_session() as session:
q, aliased_reaction = query_shouts(slug) # Отключение автосохранения
results = session.execute(q).first() with session.no_autoflush:
if results: q, aliased_reaction = query_shouts(slug)
[ results = session.execute(q).first()
shout, if results:
commented_stat, [
followers_stat, shout,
rating_stat, commented_stat,
last_reaction_at, followers_stat,
authors, rating_stat,
topics, last_reaction_at,
main_topic_slug, authors,
] = results topics,
main_topic_slug,
] = results
shout.stat = { shout.stat = {
"viewed": ViewedStorage.get_shout(shout.id), "viewed": ViewedStorage.get_shout(shout.id),
"commented": commented_stat, "commented": commented_stat,
"rating": rating_stat, "rating": rating_stat,
"last_reacted_at": last_reaction_at, "last_reacted_at": last_reaction_at,
} }
# Используем класс модели Author для преобразования строк в объекты
shout.authors = parse_aggregated_string(authors, Author)
# Используем класс модели Topic для преобразования строк в объекты
shout.topics = parse_aggregated_string(topics, Topic)
# Добавляем основной топик, если он существует # Преобразование строк в объекты Author без их создания
shout.main_topic = main_topic_slug shout.authors = parse_aggregated_string(authors, Author)
return shout # Преобразование строк в объекты Topic без их создания
shout.topics = parse_aggregated_string(topics, Topic)
# Добавляем основной топик, если он существует
shout.main_topic = main_topic_slug
return shout
except Exception as _exc: except Exception as _exc:
import traceback import traceback

View File

@ -3,11 +3,11 @@ import asyncio
from sqlalchemy import and_, distinct, func, join, select from sqlalchemy import and_, distinct, func, join, select
from sqlalchemy.orm import aliased from sqlalchemy.orm import aliased
from cache.cache import cache_author
from orm.author import Author, AuthorFollower from orm.author import Author, AuthorFollower
from orm.reaction import Reaction, ReactionKind from orm.reaction import Reaction, ReactionKind
from orm.shout import Shout, ShoutAuthor, ShoutTopic from orm.shout import Shout, ShoutAuthor, ShoutTopic
from orm.topic import Topic, TopicFollower from orm.topic import Topic, TopicFollower
from cache.cache import cache_author
from services.db import local_session from services.db import local_session
from utils.logger import root_logger as logger from utils.logger import root_logger as logger

View File

@ -1,15 +1,19 @@
from sqlalchemy import distinct, func, select from sqlalchemy import distinct, func, select
from cache.cache import (
get_cached_topic_authors,
get_cached_topic_by_slug,
get_cached_topic_followers,
)
from cache.memorycache import cache_region
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_with_stat
from services.auth import login_required from services.auth import login_required
from cache.cache import get_cached_topic_authors, get_cached_topic_by_slug, get_cached_topic_followers
from services.db import local_session from services.db import local_session
from utils.logger import root_logger as logger
from cache.memorycache import cache_region
from services.schema import mutation, query from services.schema import mutation, query
from utils.logger import root_logger as logger
# Запрос на получение всех тем # Запрос на получение всех тем

View File

@ -1,6 +1,7 @@
import subprocess import subprocess
from granian.constants import Interfaces from granian.constants import Interfaces
from granian.log import LogLevels
from granian.server import Granian from granian.server import Granian
from settings import PORT from settings import PORT
@ -21,8 +22,9 @@ if __name__ == "__main__":
"main:app", "main:app",
address="0.0.0.0", # noqa S104 address="0.0.0.0", # noqa S104
port=PORT, port=PORT,
interface=Interfaces.ASGI,
threads=4, threads=4,
websockets=False, websockets=False,
interface=Interfaces.ASGI, log_level=LogLevels.debug,
) )
granian_instance.serve() granian_instance.serve()

View File

@ -1,19 +1,15 @@
import json import json
import math
import time import time
import traceback import traceback
import warnings import warnings
import math
from typing import Any, Callable, Dict, TypeVar from typing import Any, Callable, Dict, TypeVar
from sqlalchemy import JSON, Column, Engine, Integer, create_engine, event, exc, inspect from sqlalchemy import JSON, Column, Engine, Integer, create_engine, event, exc, inspect
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, configure_mappers from sqlalchemy.orm import Session, configure_mappers
from sqlalchemy.sql.schema import Table from sqlalchemy.sql.schema import Table
from settings import DB_URL
from utils.logger import root_logger as logger from utils.logger import root_logger as logger
from settings import DB_URL
# from sqlalchemy_searchable import make_searchable
# Подключение к базе данных SQLAlchemy # Подключение к базе данных SQLAlchemy
@ -96,23 +92,44 @@ warnings.showwarning = warning_with_traceback
warnings.simplefilter("always", exc.SAWarning) warnings.simplefilter("always", exc.SAWarning)
@event.listens_for(Engine, "before_cursor_execute") # Функция для извлечения SQL-запроса из контекста
def before_cursor_execute(conn, cursor, statement, parameters, context, executemany): def get_statement_from_context(context):
conn.query_start_time = time.time() query = None
conn.last_statement = None
@event.listens_for(Engine, "after_cursor_execute")
def after_cursor_execute(conn, cursor, statement, parameters, context, executemany):
compiled_statement = context.compiled.string compiled_statement = context.compiled.string
compiled_parameters = context.compiled.params compiled_parameters = context.compiled.params
if compiled_statement: if compiled_statement:
elapsed = time.time() - conn.query_start_time if compiled_parameters:
if compiled_parameters is not None: try:
query = compiled_statement.format(*compiled_parameters) # Безопасное форматирование параметров
query = compiled_statement % compiled_parameters
except Exception as e:
logger.error(f"Error formatting query: {e}")
else: else:
query = compiled_statement # or handle this case in a way that makes sense for your application query = compiled_statement
if query:
query = query.replace("\n", " ").replace(" ", " ").strip()
return query
if elapsed > 1 and conn.last_statement != query:
conn.last_statement = query # Обработчик события перед выполнением запроса
logger.debug(f"\n{query}\n{'*' * math.floor(elapsed)} {elapsed:.3f} s\n") @event.listens_for(Engine, "before_cursor_execute")
def before_cursor_execute(conn, cursor, statement, parameters, context, executemany):
conn.query_start_time = time.time()
conn.cursor_id = id(cursor) # Отслеживание конкретного курсора
# Обработчик события после выполнения запроса
@event.listens_for(Engine, "after_cursor_execute")
def after_cursor_execute(conn, cursor, statement, parameters, context, executemany):
if hasattr(conn, "cursor_id") and conn.cursor_id == id(cursor):
query = get_statement_from_context(context)
if query:
elapsed = time.time() - conn.query_start_time
if elapsed > 1:
query_end = query[-16:]
query = query.split(query_end)[0] + query_end
logger.debug(query)
elapsed_n = math.floor(elapsed)
logger.debug('*' * elapsed_n)
logger.debug(f"{elapsed_n:.3f} s")
del conn.cursor_id # Удаление идентификатора курсора после выполнения

View File

@ -63,8 +63,9 @@ stream.setFormatter(formatter)
# Set up the root logger with the same formatting # Set up the root logger with the same formatting
root_logger = logging.getLogger() root_logger = logging.getLogger()
root_logger.setLevel(logging.DEBUG) if not root_logger.hasHandlers():
root_logger.addHandler(stream) root_logger.setLevel(logging.DEBUG)
root_logger.addHandler(stream)
ignore_logs = ["_trace", "httpx", "_client", "_trace.atrace", "aiohttp", "_client", "base"] ignore_logs = ["_trace", "httpx", "_client", "_trace.atrace", "aiohttp", "_client", "base"]
for lgr in ignore_logs: for lgr in ignore_logs: