Метки в комментариях SQL¶
hare.instrumentation.QueryTags помечает каждую команду SQL, выполненную внутри блока, комментарием в
конце в стиле sqlcommenter — чтобы по тексту SQL из
pg_stat_statements/auto_explain/журнала медленных запросов найти место в приложении, откуда он
пришёл, без зависимости hare от какой-либо системы трассировки.
class QueryTags:
@classmethod
def scope(cls, **tags: str) -> AbstractContextManager[None]: ...
@classmethod
def set(cls, tags: dict[str, str]) -> contextvars.Token[dict[str, str] | None]: ...
@classmethod
def reset(cls, token: contextvars.Token[dict[str, str] | None]) -> None: ...
Ночная задача, которая напоминает о просроченных выдачах, помечает свои запросы:
from hare.instrumentation import QueryTags
with QueryTags.scope(application="ops", job="remind_overdue_loans"):
await Loan.objects.filter(status="overdue").all()
# SQL здесь заканчивается так: ... /*application='ops',job='remind_overdue_loans'*/
Комментарий¶
- Комментарий добавляется в конец, никогда в начало: комментарий в начале мешал бы кэшу запросов
PostgreSQL, который опирается на начало текста, и путал бы инструменты, читающие только первые N
символов запроса. После запроса, который кончается строчным комментарием (
-- ...), он ставится на отдельную строку. - Ключи и значения кодируются как в URL — ключ должен быть непустой строкой, а значение не None, иначе
ConfigurationError; ключи сортируются, поэтому один и тот же набор меток всегда даёт один и тот же текст комментария, в каком бы порядке его ни передали. - Без активных меток SQL возвращается без изменений (тот же объект) — в обычном случае без меток накладных расходов нет.
Вложенные блоки¶
Вложенные блоки не сливаются: блок QueryTags.scope() внутри другого добавляет к своим запросам только
свои метки — метки внешнего блока снова действуют после выхода из внутреннего.
QueryTags.set(tags)/QueryTags.reset(token) — низкоуровневые примитивы, на которых построен
QueryTags.scope(), — для случаев, когда один блок with не может охватить всё время жизни меток
(например, через границу await, охватывающую несколько вызовов обработчиков).
Какие команды получают комментарий¶
- Каждый вызов, выполняющий запрос, включая DDL-сценарий через
execute_script(): сценарий из нескольких запросов получает ровно один комментарий в конце, а не по одному на запрос. - Массовая загрузка
COPYв PostgreSQL (bulk_create(use_copy=True)): наasyncpg, который сам пишет команду, комментарий идёт послеWHERE TRUE, которому отвечает каждая строка. QueryExecuted.sqlиQueryCall.sqlсодержат уже помеченный текст: метки добавляются до того, как запрос уходит в драйвер.