Аналитика снова пишет события в Postgres
При проверке на сервере выяснилось, что psycopg-соединение не умеет executemany: батч событий уходил в fallback и терялся. Запись переведена на курсор, добавлен тест на этот путь; версия 0.7.2.
This commit is contained in:
parent
cf14dfa80d
commit
448ab837fa
|
|
@ -171,7 +171,7 @@ pytest
|
|||
|
||||
## Версионирование
|
||||
|
||||
- Единый источник версии — `common/version.py` (`__version__`), текущая — `0.7.1`.
|
||||
- Единый источник версии — `common/version.py` (`__version__`), текущая — `0.7.2`.
|
||||
- Схема — SemVer-подобная (`MAJOR.MINOR.PATCH`); git-тег = `v` + версия (`v0.1`).
|
||||
- Docker-образы тегируются версией (Блок 2), события аналитики получают `bot_version`.
|
||||
- Выпуск: изменить `common/version.py` → коммит → аннотированный тег `vX.Y` → `git push --tags`.
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@ Writer = Callable[[list[EventRow], MessageCounts], Awaitable[None]]
|
|||
|
||||
async def _write_db(events: list[EventRow], messages: MessageCounts) -> None:
|
||||
"""Записать батч в Postgres."""
|
||||
async with get_pool().connection() as conn:
|
||||
async with get_pool().connection() as conn, conn.cursor() as cur:
|
||||
if events:
|
||||
await conn.executemany(
|
||||
await cur.executemany(
|
||||
"""
|
||||
INSERT INTO analytics.events
|
||||
(event_time, event_type, user_id, username, chat_id, payload, bot_version, prod)
|
||||
|
|
@ -71,7 +71,7 @@ async def _write_db(events: list[EventRow], messages: MessageCounts) -> None:
|
|||
)
|
||||
|
||||
for (day, user_id, chat_id), (username, count) in messages.items():
|
||||
await conn.execute(
|
||||
await cur.execute(
|
||||
"""
|
||||
INSERT INTO analytics.messages_daily (day, user_id, username, chat_id, count)
|
||||
VALUES (%s, %s, %s, %s, %s)
|
||||
|
|
|
|||
|
|
@ -7,4 +7,4 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
__version__ = "0.7.1"
|
||||
__version__ = "0.7.2"
|
||||
|
|
|
|||
|
|
@ -60,6 +60,51 @@ class FakeWriter:
|
|||
self.messages[key] = list(value)
|
||||
|
||||
|
||||
class FakeCursor:
|
||||
"""Курсор psycopg: запоминает вызовы."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.calls: list[tuple[str, Any, Any]] = []
|
||||
|
||||
async def executemany(self, query: str, rows: list[Any]) -> None:
|
||||
self.calls.append(("executemany", query, rows))
|
||||
|
||||
async def execute(self, query: str, params: Any) -> None:
|
||||
self.calls.append(("execute", query, params))
|
||||
|
||||
async def __aenter__(self) -> FakeCursor:
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *args: Any) -> None:
|
||||
return None
|
||||
|
||||
|
||||
class FakeConnection:
|
||||
"""Соединение psycopg с курсором."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.cursor_obj = FakeCursor()
|
||||
|
||||
def cursor(self) -> FakeCursor:
|
||||
return self.cursor_obj
|
||||
|
||||
async def __aenter__(self) -> FakeConnection:
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *args: Any) -> None:
|
||||
return None
|
||||
|
||||
|
||||
class FakePool:
|
||||
"""Пул с одним соединением."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.conn = FakeConnection()
|
||||
|
||||
def connection(self) -> FakeConnection:
|
||||
return self.conn
|
||||
|
||||
|
||||
def _settings(**overrides: Any) -> Settings:
|
||||
return Settings(_env_file=None, bot_token="1:a", **overrides)
|
||||
|
||||
|
|
@ -230,6 +275,26 @@ async def test_disabled_analytics_ignores_events() -> None:
|
|||
assert fake.messages == {}
|
||||
|
||||
|
||||
# --- запись в Postgres ---
|
||||
|
||||
|
||||
async def test_write_db_uses_cursor_executemany(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
from common.analytics import client as analytics_client
|
||||
|
||||
pool = FakePool()
|
||||
monkeypatch.setattr(analytics_client, "get_pool", lambda: pool)
|
||||
|
||||
await analytics_client._write_db(
|
||||
[[datetime.now(UTC), "command", 1, "vasya", 100, {"command": "инфо"}, "0.7.2", False]],
|
||||
{(datetime.now(UTC).date(), 1, 100): ["vasya", 3]},
|
||||
)
|
||||
|
||||
calls = pool.conn.cursor_obj.calls
|
||||
assert calls[0][0] == "executemany"
|
||||
assert calls[1][0] == "execute"
|
||||
assert "messages_daily" in calls[1][1]
|
||||
|
||||
|
||||
# --- деградация без Postgres ---
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,4 +7,4 @@ from common import __version__
|
|||
|
||||
def test_version_is_set():
|
||||
assert __version__
|
||||
assert __version__ == "0.7.1"
|
||||
assert __version__ == "0.7.2"
|
||||
|
|
|
|||
Loading…
Reference in New Issue