fix(caddy): окно деплоя — 503 «Сервис обновляется» с Retry-After вместо голого 502 на публичных путях МЕРЫ #3348

Merged
bot-backend merged 2 commits from fix/3274-deploy-window-error-page into main 2026-09-05 18:12:37 +00:00
Collaborator

Refs #3274, пункт 2 (дешёвая мера S). НЕ закрывает issue — см. честную оговорку ниже.

Что добавлено

  • caddy/deploy-window.caddy.snippet (по образцу users.caddy.snippet): /trade-in/api/* → JSON {"detail":"Сервис обновляется…","error":"service_unavailable","retry_after":30}, всё прочее → инлайн-HTML «Сервис обновляется. Это занимает около минуты…». Оба — 503 + Retry-After: 30 (не индексируется как поломка, клиенты понимают временность). Инлайн-стили вместо <style> — фигурные скобки в теле respond Caddy разбирает как плейсхолдеры.
  • apps.caddy: handle_errors 502 503 504 в meraocenka.ru (весь хост) и в gendsgn.ru только под @tradeinScope path /trade-in /trade-in/* — ПТИЦА не затронута (проверено на стенде: gendsgn / и /api/v1/x по-прежнему 502). Периметр из #3340 не тронут (+93/−0).

Проверено на стенде (caddy 2.11.3, dead-upstream)

GET /503, Retry-After: 30, HTML с текстом; GET /trade-in/api/x503, JSON. Матчер в снипете сверяется с исходным путём — /trade-in/api/* матчится, хотя uri strip_prefix на основном маршруте отрабатывает раньше падения апстрима (замер, не чтение документации).

Честно про критерий приёмки issue

Записанный критерий («grep '"status":502' → 0 за сутки с ≥3 деплоями») эта правка закроет механически, но не по смыслу: тот же отказ теперь пишется как 503, окно 30-90 с на каждый деплой остаётся. Закрыт только п.2 (вид отказа + семантика статуса), не п.1 (zero-downtime — решение владельца) и не п.3 (healthcheck под него). Осмысленный критерий п.2: «0×502 на публичных путях и каждый 503 несёт Retry-After»; п.1: «максимальная серия не-2xx на / в окне мержа < 2 с».

Приёмка после deploy-caddy

curl -i https://meraocenka.ru/ в момент следующего деплоя tradein → 503 + страница.

Refs #3274, пункт 2 (дешёвая мера S). **НЕ закрывает issue** — см. честную оговорку ниже. ## Что добавлено - `caddy/deploy-window.caddy.snippet` (по образцу users.caddy.snippet): `/trade-in/api/*` → JSON `{"detail":"Сервис обновляется…","error":"service_unavailable","retry_after":30}`, всё прочее → инлайн-HTML «Сервис обновляется. Это занимает около минуты…». Оба — **503 + `Retry-After: 30`** (не индексируется как поломка, клиенты понимают временность). Инлайн-стили вместо `<style>` — фигурные скобки в теле `respond` Caddy разбирает как плейсхолдеры. - `apps.caddy`: `handle_errors 502 503 504` в meraocenka.ru (весь хост) и в gendsgn.ru **только под `@tradeinScope path /trade-in /trade-in/*`** — ПТИЦА не затронута (проверено на стенде: `gendsgn /` и `/api/v1/x` по-прежнему 502). Периметр из #3340 не тронут (+93/−0). ## Проверено на стенде (caddy 2.11.3, dead-upstream) `GET /` → `503`, `Retry-After: 30`, HTML с текстом; `GET /trade-in/api/x` → `503`, JSON. Матчер в снипете сверяется с исходным путём — `/trade-in/api/*` матчится, хотя `uri strip_prefix` на основном маршруте отрабатывает раньше падения апстрима (замер, не чтение документации). ## Честно про критерий приёмки issue Записанный критерий («`grep '"status":502'` → 0 за сутки с ≥3 деплоями») эта правка закроет **механически, но не по смыслу**: тот же отказ теперь пишется как 503, окно 30-90 с на каждый деплой остаётся. Закрыт только п.2 (вид отказа + семантика статуса), не п.1 (zero-downtime — решение владельца) и не п.3 (healthcheck под него). Осмысленный критерий п.2: «0×502 на публичных путях **и** каждый 503 несёт Retry-After»; п.1: «максимальная серия не-2xx на `/` в окне мержа < 2 с». ## Приёмка после deploy-caddy `curl -i https://meraocenka.ru/` в момент следующего деплоя tradein → 503 + страница.
bot-backend added 1 commit 2026-09-05 17:51:22 +00:00
fix(caddy): страница «сервис обновляется» вместо голого 502 в окне деплоя
Some checks failed
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Failing after 12s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
d177b916e3
Каждый мерж в tradein оставляет 30-90 с, в которые контейнера нет и Caddy
набирает несуществующий апстрим: 15 x 502 на meraocenka.ru и 23 на gendsgn.ru
за 4 суток, все на окнах деплоя (duration < 2 мс = мгновенный отказ
соединения, а не перегрузка).

handle_errors 502/503/504 подменяет это на 503 + Retry-After: 30 и читаемое
тело. 502 читается как постоянная поломка — поисковик выкидывает страницу из
индекса, клиенты не ретраят; 503 + Retry-After означает ровно то, что
происходит. Для /trade-in/api/* тело JSON, а не HTML: эти ручки вызывают из JS
и внешних клиентов, HTML у них превращается в ошибку разбора.

На gendsgn.ru перехват гейтится по пути /trade-in* — Site Finder («Птица»)
деплоится отдельным пайплайном и в задачу не входит; вне матчера ошибка
остаётся необработанной и поведение прежнее.

Окно простоя это НЕ убирает — zero-downtime деплой (п.1 issue) требует
решения владельца. Меняется только то, что видно внутри окна.

Refs #3274
Light1YT added 1 commit 2026-09-05 18:07:07 +00:00
fix(caddy): смонтировать deploy-window сниппет — иначе Caddy не адаптирует конфиг
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI / changes (pull_request) Successful in 18s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
3e481b0276
Каталог caddy/ пробрасывается в контейнер ПОФАЙЛОВО (users + metrics-*, плюс
каталоги sites/ и local/). apps.caddy импортирует `../deploy-window.caddy.snippet`,
которого без этой строки в контейнере нет: Caddy падает на 'File to import not
found', уходит в restart-loop и роняет ВСЕ домены хоста — ровно постмортем #3102.
Гейт scripts/check-caddy-snippet-mounts.py на ветке был красный (две строки
apps.caddy + сам сниппет), после этой строки зелёный.

Смоук периметра: 503 на payments/notify перестал что-либо доказывать — тот же
код теперь отдаёт заглушка окна деплоя. check_post получил необязательный
шаблон-дискриминатор: код совпал, но в ответе `Retry-After: 30` или
`service_unavailable` — это Caddy, а не приложение, и это FAIL.

Refs #3274
bot-backend merged commit 2d4f669d4b into main 2026-09-05 18:12:37 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3348
No description provided.