← К проектам

quotes-service

Витрина каталога цитат — снимок до 110 МБ читается потоком, тело парсится построчно, внешний каталог прикрыт circuit breaker'ом.

  • Python
  • FastAPI
  • uvicorn
  • orjson
  • httpx
  • Docker

Python2026-09-23Исходники

110МБ в снапшоте

Сервис-витрина каталога цитат: принимает полный снимок каталога и отдаёт цитаты читателям по одной. Всё состояние — в памяти одного процесса, внешних БД, кэшей и брокеров нет.

Что внутри

  • Снимок читается потоком — тело до 110 МБ разбирается по чанкам и строкам через orjson, целиком в память не поднимается. Битая запись пропускается со счётчиком dropped, битый конверт — 400.
  • Packed-буфер вместо объектов — тела записей лежат в одном bytearray с length-prefix, индекс id → offset, набор всех id отдельно. Поэтому витрина помнит id даже после вытеснения тела, а LRU по байтам работает предохранителем, а не штатным путём.
  • Circuit breaker на каталог — 5 подряд транспортных сбоев открывают цепь с backoff и jitter, half-open пускает одну пробу. А вот 503 breaker не открывает: это ответ каталога, а не обрыв связи, — уважается Retry-After.
  • Честные счётчики — X-Source: LOCAL | CATALOG на каждом ответе, а /stats показывает, сколько отдано из памяти, сколько дочитано из каталога и сколько тел вытеснено. 404 из каталога не кешируется, DELETE ставит tombstone до следующего снимка.
  • Укладывается в 256 МБ RAM — пик на снимке в 340 000 записей (114 МБ) около 220 МБ при лимите 0.5 CPU и одном воркере uvicorn.

Стек

Python 3.13, FastAPI, uvicorn, orjson, httpx и uvloop. Всё приложение — чуть меньше 18 КБ кода в четырёх модулях; сборка через pip, multi-stage образ, запуск под non-root.

Проверка

scripts/contract_test.py прогоняет контракт целиком против заглушки каталога: импорт, LOCAL/CATALOG, границы полей, tombstone’ы и форму /stats.