Как устроена структура backend микросервиса для формирования документа по запросу UI и как показывать состояние готовности системы
Что проверяют
Умеете ли вы вынести долгую операцию из HTTP-запроса и честно сообщать интерфейсу, что с ней происходит. Слово «готовность» тут двойное: готовность конкретного документа и готовность самого сервиса принимать запросы. Разведите оба смысла сами, иначе об этом спросят следом.
Как отвечать
Каркас асинхронной задачи:
- приём:
POST /documentsвалидирует вход (в FastAPI через Pydantic), создаёт запись в PostgreSQL со статусомpendingи сразу отвечает202 Acceptedс идентификатором задачи. ЗаголовокIdempotency-Keyзащищает от двойного клика: повтор вернёт ту же задачу; - очередь и воркер: задача уходит в брокер (RabbitMQ или Redis), воркер Celery, arq или Dramatiq ставит статус
processing, собирает документ (WeasyPrint для PDF, docxtpl для DOCX) и кладёт файл в S3; - статус:
GET /documents/{id}отдаётpending,processing,doneилиfailed, при готовности ссылку на скачивание, лучше presigned с коротким сроком. Интерфейс опрашивает адрес раз в пару секунд или получает событие через SSE или WebSocket. Для одного статуса SSE проще: это обычный HTTP; - сбои: повторы с экспоненциальной задержкой и лимитом попыток, после него
failedс понятной причиной. В Celery включаютacks_late, иначе задача пропадёт при падении воркера посреди работы; - готовность сервиса: отдельные пробы для Kubernetes. Liveness отвечает, жив ли процесс, readiness проверяет базу и брокер и решает, пускать ли под в балансировку.
Добавьте метрики: длина очереди, время генерации, доля failed. По ним нехватка воркеров видна раньше жалоб пользователей.
Где спотыкаются
Генерируют документ прямо в обработчике. На больших документах упираются в тайм-аут балансировщика, пользователь жмёт кнопку снова и запускает вторую генерацию.
Берут BackgroundTasks из FastAPI для тяжёлой работы. Задача выполняется в том же процессе и теряется при рестарте пода, а статус навсегда застревает в processing.
Проверяют базу в liveness-пробе. Короткий сбой базы перезапускает все поды разом, хотя хватило бы вывести их из балансировки через readiness.