Кеши, PR-юнит 4 (пакет E): хелпер путей медиа-кеша + in-memory MIME-кеш на горячем пути /media #26

Open
opened 2026-07-05 19:16:09 +03:00 by agent_vscode · 0 comments
Collaborator

ТЗ: горячий путь /media

Часть принятой спеки plans/cache-optimization-spec.md. Независим от других юнитов, можно делать параллельно. Один PR. Комментарии в коде — только английские.

Задание 15 — константа и хелпер путей

os.path.abspath("./data/cache") + ручной join собираются в семи местах (api_server.py ~207, 532–536, 667, 757–766, 817, 851, 1172–1175) — формат пути является неявным контрактом между ними. Ввести на уровне модуля:

MEDIA_CACHE_DIR = os.path.abspath(os.path.join("data", "cache"))  # resolved once at import

def media_cache_path(channel: str, post_id: int, file_unique_id: str | None = None) -> str:
    """Single source of truth for the on-disk layout: <root>/<channel>/<post_id>[/<fid>]."""

Заменить все семь мест. remove_old_cached_files_sync/download_new_files сохраняют параметр cache_dir (тестируемость), вызыватели передают константу.

Задание 16 — in-memory кеш MIME

Запись access-time уже вынесена с горячего пути в аккумулятор (_access_updates), а чтение MIME до сих пор делает threadpool-hop + НОВОЕ SQLite-соединение на каждый хит (prepare_file_response, ~389–404). MIME по ключу file_unique_id иммутабелен.

# MIME types are immutable per file_unique_id, so a process-lifetime dict in front of
# SQLite removes a to_thread + connect from every cache-hit response.
_mime_types: dict[tuple[str, int, str], str] = {}
_MIME_CACHE_MAX = 50_000  # crude bound; clear-all on overflow is fine at this size

Порядок в prepare_file_response: dict → (miss) SQLite → (miss) python-magic → записать и в SQLite, и в dict. При успешном чтении из SQLite — заполнить dict. При переполнении — clear().

Тесты

  1. media_cache_path строит ровно текущий layout (регресс формата пути).
  2. Замокать get_mime_type_sync: два запроса подряд к одному файлу — второй его НЕ вызывает.
  3. Переполнение _mime_types (> _MIME_CACHE_MAX) → clear() без исключений, следующий запрос снова заполняет.

Критерии приёмки

  • Повторный /media-хит не создаёт SQLite-соединений (ни на чтение MIME, ни на запись access-time). Рецепт проверки: monkeypatch file_io._open_db счётчиком соединений; два последовательных запроса TestClient к одному файлу — на втором счётчик не растёт.
  • pytest зелёный.
# ТЗ: горячий путь /media Часть принятой спеки `plans/cache-optimization-spec.md`. Независим от других юнитов, можно делать параллельно. Один PR. Комментарии в коде — только английские. ## Задание 15 — константа и хелпер путей `os.path.abspath("./data/cache")` + ручной `join` собираются в **семи** местах (`api_server.py` ~207, 532–536, 667, 757–766, 817, 851, 1172–1175) — формат пути является неявным контрактом между ними. Ввести на уровне модуля: ```python MEDIA_CACHE_DIR = os.path.abspath(os.path.join("data", "cache")) # resolved once at import def media_cache_path(channel: str, post_id: int, file_unique_id: str | None = None) -> str: """Single source of truth for the on-disk layout: <root>/<channel>/<post_id>[/<fid>].""" ``` Заменить все семь мест. `remove_old_cached_files_sync`/`download_new_files` сохраняют параметр `cache_dir` (тестируемость), вызыватели передают константу. ## Задание 16 — in-memory кеш MIME Запись access-time уже вынесена с горячего пути в аккумулятор (`_access_updates`), а чтение MIME до сих пор делает threadpool-hop + НОВОЕ SQLite-соединение на каждый хит (`prepare_file_response`, ~389–404). MIME по ключу `file_unique_id` иммутабелен. ```python # MIME types are immutable per file_unique_id, so a process-lifetime dict in front of # SQLite removes a to_thread + connect from every cache-hit response. _mime_types: dict[tuple[str, int, str], str] = {} _MIME_CACHE_MAX = 50_000 # crude bound; clear-all on overflow is fine at this size ``` Порядок в `prepare_file_response`: dict → (miss) SQLite → (miss) python-magic → записать и в SQLite, и в dict. При успешном чтении из SQLite — заполнить dict. При переполнении — `clear()`. ## Тесты 1. `media_cache_path` строит ровно текущий layout (регресс формата пути). 2. Замокать `get_mime_type_sync`: два запроса подряд к одному файлу — второй его НЕ вызывает. 3. Переполнение `_mime_types` (> `_MIME_CACHE_MAX`) → `clear()` без исключений, следующий запрос снова заполняет. ## Критерии приёмки - Повторный `/media`-хит не создаёт SQLite-соединений (ни на чтение MIME, ни на запись access-time). Рецепт проверки: monkeypatch `file_io._open_db` счётчиком соединений; два последовательных запроса TestClient к одному файлу — на втором счётчик не растёт. - `pytest` зелёный.
Sign in to join this conversation.