fix(stand): поправлены находки ревью — причина дрейфа памяти и отзыв довода ADR 0001

Зачем

Двухосевое ревью нашло в PR фактическую ошибку и одну процессную дыру. Ошибка
того же класса, что уже снималась по ходу разбора: в ADR было записано, будто
RSS ноды полз вверх из-за страниц её бинарника. Замер на живой ноде это
опроверг.

Что

- ADR 0004: причина дрейфа переписана по замеру. За обычную сессию страницы
  бинарника 523 -> 531 МиБ, то есть стоят на месте, а рабочая память
  489 -> 723 МиБ. Бинарник объясняет постоянную часть расхода, а не рост;
  отчего растёт рабочая память, для этого решения знать не нужно. Вывод не
  меняется: одна только постоянная часть занимала больше половины гигабайтной
  коробки.
- ADR 0001: ресурсный довод отозван прямо в файле — и строкой статуса, и
  абзацем после самого довода. Обе оси ревью нашли это независимо друг от
  друга: строка «удерживает стенд в пределе 3,4 ГБ» читалась как действующая,
  хотя предела уже нет.
- stand-smoke.sh: OOMKilled поднимается и тогда, когда ядро убило процесс
  внутри живого контейнера, поэтому сообщение говорит про процесс, а не про
  контейнер. Флаг hurt переименован в problems и считает находки — как passed
  и failed по соседству.
- Формулировки ADR 0004 упрощены: «коробка» объясняется при первом упоминании,
  а метафоры «вход в самонастройку», «предохранители», «бронь», «полка» и
  «бюджет в новой одежде» заменены обычными словами. Правило AGENTS.md —
  сложную мысль пояснять при первом упоминании.

Проверка

make config-test — зелено. make smoke — 25 из 25, проверка выживания отработала
с новым сообщением.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-01 15:18:43 +03:00
co-authored by Claude Opus 5
parent a9d66ed74a
commit 5a21684dc4
4 changed files with 75 additions and 49 deletions
+16 -11
View File
@@ -592,15 +592,18 @@ check_superset() {
# Контейнер, убитый ядром за нехватку памяти, Docker поднимает сам, и через
# полминуты его проверка состояния снова зелёная: о смерти она не расскажет.
# Поэтому спрашиваем у Docker два факта — убивало ли контейнер ядро и включалась
# ли политика перезапуска. Ручной `docker compose restart` счётчик не трогает,
# так что документированный перезапуск нод проверку не роняет. Порога здесь
# нет: это «да или нет», а не бюджет памяти (ADR 0004).
# Поэтому спрашиваем у Docker два факта — убивало ли ядро что-нибудь в контейнере
# за память и включалась ли политика перезапуска. Ручной `docker compose restart`
# счётчик не трогает, так что документированный перезапуск нод проверку не
# роняет. Порога здесь нет: это «да или нет», а не бюджет памяти (ADR 0004).
#
# Два факта берутся одним `--format` и разбираются образцом — тем же приёмом,
# что и состояние с проверкой здоровья выше.
check_containers_survived() {
local container_id
local service
local state
local hurt=0
local problems=0
for service in "${LONG_LIVED_SERVICES[@]}"; do
container_id="$(compose ps --all --quiet "$service" 2>/dev/null || true)"
@@ -611,22 +614,24 @@ check_containers_survived() {
state="$(docker inspect --format '{{.State.OOMKilled}}/{{.RestartCount}}' "$container_id" 2>/dev/null || true)"
case "$state" in
false/0) ;;
# OOMKilled поднимается и когда ядро убило процесс внутри живого
# контейнера, поэтому говорим про процесс, а не про контейнер.
true/*)
fail "контейнер ${service} был убит из-за нехватки памяти"
hurt=1
fail "в контейнере ${service} ядро убило процесс из-за нехватки памяти"
problems=$((problems + 1))
;;
false/*)
fail "контейнер ${service} перезапускался, счётчик Docker — ${state#*/}"
hurt=1
problems=$((problems + 1))
;;
*)
fail "Docker не рассказал о состоянии контейнера ${service}"
hurt=1
problems=$((problems + 1))
;;
esac
done
if [[ "$hurt" -eq 0 ]]; then
pass 'ни один долгоживущий контейнер не был убит по памяти и не перезапускался сам'
if [[ "$problems" -eq 0 ]]; then
pass 'ни в одном долгоживущем контейнере ядро не убивало процессы за память, и никто не перезапускался сам'
fi
}