Архитектура бэкенда
Graceful shutdown: ноль потерянных запросов на деплое
Читать про гонку deregistration — не то же самое, что смотреть, как твой собственный деплой сбрасывает 502, а потом заставить их исчезнуть. Построй маленький сервис, теряющий запросы на каждом rollout, прогони под нагрузкой и примени дисциплину юнита — сигнал, deregister, drain, disposition, координация — пока rolling-деплой не станет невидим клиенту, с доказательством на каждом шаге.
Преврати ментальную модель юнита в воспроизводимый инженерный цикл: воспроизведи потерю запросов на деплое под нагрузкой, затем добавь корректный путь shutdown и координацию флота, и докажи нулевую (или почти нулевую) потерю на rolling-деплое метриками до/после.
Возьми маленький HTTP-сервис с одним медленным эндпоинтом и одним воркером очереди, задеплой его на Kubernetes (kind/minikube подойдут) и сведи потерю запросов на деплое к нулю — или к задокументированному ограниченному минимуму — доказывая каждый шаг измерениями, а не утверждениями.
- Таблица до/после: error rate в окне деплоя, счётчики 502/reset/refused, p99 latency запросов во время roll и задвоенные/потерянные side effects задач — измеренные под идентичной нагрузкой, не оценочно.
- Логи, доказывающие, что SIGTERM-обработчик срабатывает, readiness переключается до закрытия листенера, и ресурсы закрываются в reverse dependency order; путь guardian timeout прогнан хотя бы раз и делает чистый force-exit.
- Демонстрация kill-посреди-задачи, показывающая, что side effect применяется ровно один раз после передоставки, доказывая идемпотентность consumer.
- Числа после показывают нулевую (или задокументированную ограниченную почти-нулевую) потерю запросов на rolling-деплое, при этом всплеск connection-refused и проседание capacity оба исчезли.
- Параграф разбора, называющий, какой рычаг починил каждый failure mode (PID 1, readiness+preStop, drain в reverse-order, requeue+идемпотентность, surge+jitter) и почему порядок имел значение.
- Добавь on-call runbook: как читать всплеск ошибок в окне деплоя, пять failure mode и их исправления, и чеклист проверки перед деплоем.
- Измерь и подстрой реальный deregistration delay: инструментируй время от провала readiness до последнего отмаршрутизированного запроса и размерь preStop sleep и окно drain под ≈3× наблюдаемого p99 вместо угаданной константы.
- Воспроизведи и затем почини thundering reconnect явно: прогони тысячи keep-alive клиентов, покажи, как неджиттерный синхронный close опрокидывает холодные поды, затем покажи, как jitter сглаживает кривую reconnect.
- Повтори эксперимент с долгоживущими соединениями (WebSocket/SSE или большие загрузки): подними deregistration delay до 600s+ и покажи чистое отклонение через 503 + Retry-After для операций, не влезающих в бюджет.
Это цикл, который ты прогонишь для каждого реального сервиса, прежде чем доверять ему под churn: сначала воспроизведи потерю на деплое, затем чини по порядку — заставь SIGTERM дойти до обработчика в PID 1, провали readiness и выжди deregistration до того, как перестанешь принимать, сдрейнируй keep-alive и сделай teardown в reverse dependency order под guardian timeout, отклони или идемпотентно поставь в requeue работу, не влезающую в бюджет, и подними это до флота с surge-перед-drain и jittered closes. Докажи каждый шаг числами до/после под идентичной нагрузкой. Сделав это раз на игрушечном сервисе, ты доводишь продакшн-версию до мышечной памяти — и превращаешь «мы теряем пару запросов на каждом деплое» в деплой, которого никто не замечает.