Тревоги печатали не ту величину, которую называли: «2.684e+11% от mem_limit» и «доля HOT 75.21%» при пороге 20% #3464

Merged
bot-backend merged 1 commit from fix/alert-value-is-not-the-ratio into main 2026-09-12 10:12:53 +00:00
Collaborator

Найдено по боевым сообщениям в Telegram 12.09.2026 (скриншот от владельца).

Что приходило человеку

🔴 ContainerNearMemoryLimit · apps
   apps / tradein-browser: 2.684e+11% от mem_limit. Дальше OOM-kill.

🔴 PostgresLowHotUpdateRatio · apps
   apps / listings: доля HOT 75.21%.   ← при пороге срабатывания «доля < 20%»

Причина — форма выражения, а не условие

В PromQL A and B возвращает значения ЛЕВОЙ части, отфильтрованные правой. Значит в $value попадает A, а не то отношение, ради которого правило написано.

правило что стояло слева что уходило в текст
ContainerNearMemoryLimit container_spec_memory_limit_bytes лимит в байтах (2 684 354 560), отрендеренный humanizePercentage2.684e+11%
PostgresLowHotUpdateRatio rate(tup_upd[6h]) апдейтов в секунду (0.7521) → 75.21%

Условия срабатывания были верны — врал только текст. Поэтому дефект и прожил незамеченным: у памяти число абсурдно и его списывали на «глюк экспортёра», а у HOT — правдоподобно и притом противоречило собственному порогу правила.

Замеры на живом Prometheus (только чтение, 12.09)

  • container_spec_memory_limit_bytes{name="tradein-browser"} = 2 684 354 560humanizePercentage даёт ровно 2.684e+11 %, то самое число из сообщения. Настоящая доля в тот момент — 1.2 %.
  • rate(pg_table_write_amplification_tup_upd{table="listings"}[6h]) = 0.0411 → сообщение сказало бы «4.11 %». Настоящая доля HOT по listings = 0.00 %.

Правка

Отношение вынесено влево, побочное условие — внутрь знаменателя, где оно и фильтрует серии, и защищает от деления на ноль:

container_memory_working_set_bytes{name!=""}
  / (container_spec_memory_limit_bytes{name!=""} > 0) > 0.90

rate(pg_table_write_amplification_tup_hot_upd[6h])
  / (rate(pg_table_write_amplification_tup_upd[6h]) > 0.5) < 0.2

Проверено на живом Prometheus: новое выражение памяти отдаёт доли 0.35–0.71 (топ — gendesign-infra-postgres 70.8 %), новое выражение HOT — доли 0.00–1.00. promtool check rulesSUCCESS, 16 rules.

Третье правило того же семейства, PostgresDeadTuplesHigh, верно — но верно случайно: печатаемая величина совпала с левым операндом. Помечено комментарием, чтобы его не «причесали» по образцу двух других.

Гейт

backend/tests/ops/test_alert_value_is_the_described_quantity.py — инвариант: если описание рендерит $value как долю (humanizePercentage), левая часть выражения (до первого and/unless) обязана содержать деление. Плюс две именные проверки для правил, которые уже соврали.

Фальсификация: вернул файл правил с origin/main → красные test_percentage_annotations_come_from_a_ratio и test_known_two_rules_are_fixed; с правкой — 3 passed.

Чего этот PR НЕ делает

  • Не трогает пороги и условия — сработки остаются прежними.
  • Не заводит promtool в CI: сейчас правила не валидируются вообще ничем, но это отдельная задача (здесь promtool check прогнан руками на боевом контейнере).

Приёмка

Следующая сработка любого из двух правил приходит с числом в диапазоне 0–100 %, согласованным с порогом правила.

Найдено по боевым сообщениям в Telegram 12.09.2026 (скриншот от владельца). ## Что приходило человеку ``` 🔴 ContainerNearMemoryLimit · apps apps / tradein-browser: 2.684e+11% от mem_limit. Дальше OOM-kill. 🔴 PostgresLowHotUpdateRatio · apps apps / listings: доля HOT 75.21%. ← при пороге срабатывания «доля < 20%» ``` ## Причина — форма выражения, а не условие В PromQL `A and B` возвращает **значения ЛЕВОЙ части**, отфильтрованные правой. Значит в `$value` попадает `A`, а не то отношение, ради которого правило написано. | правило | что стояло слева | что уходило в текст | |---|---|---| | `ContainerNearMemoryLimit` | `container_spec_memory_limit_bytes` | лимит **в байтах** (2 684 354 560), отрендеренный `humanizePercentage` → `2.684e+11%` | | `PostgresLowHotUpdateRatio` | `rate(tup_upd[6h])` | **апдейтов в секунду** (0.7521) → `75.21%` | **Условия срабатывания были верны** — врал только текст. Поэтому дефект и прожил незамеченным: у памяти число абсурдно и его списывали на «глюк экспортёра», а у HOT — правдоподобно и притом противоречило собственному порогу правила. ## Замеры на живом Prometheus (только чтение, 12.09) - `container_spec_memory_limit_bytes{name="tradein-browser"}` = **2 684 354 560** → `humanizePercentage` даёт ровно `2.684e+11 %`, то самое число из сообщения. Настоящая доля в тот момент — **1.2 %**. - `rate(pg_table_write_amplification_tup_upd{table="listings"}[6h])` = **0.0411** → сообщение сказало бы «4.11 %». Настоящая доля HOT по listings = **0.00 %**. ## Правка Отношение вынесено влево, побочное условие — внутрь знаменателя, где оно и фильтрует серии, и защищает от деления на ноль: ```promql container_memory_working_set_bytes{name!=""} / (container_spec_memory_limit_bytes{name!=""} > 0) > 0.90 rate(pg_table_write_amplification_tup_hot_upd[6h]) / (rate(pg_table_write_amplification_tup_upd[6h]) > 0.5) < 0.2 ``` Проверено на живом Prometheus: новое выражение памяти отдаёт доли 0.35–0.71 (топ — `gendesign-infra-postgres` 70.8 %), новое выражение HOT — доли 0.00–1.00. `promtool check rules` → **SUCCESS, 16 rules**. Третье правило того же семейства, `PostgresDeadTuplesHigh`, **верно** — но верно случайно: печатаемая величина совпала с левым операндом. Помечено комментарием, чтобы его не «причесали» по образцу двух других. ## Гейт `backend/tests/ops/test_alert_value_is_the_described_quantity.py` — инвариант: если описание рендерит `$value` как долю (`humanizePercentage`), левая часть выражения (до первого `and`/`unless`) обязана содержать деление. Плюс две именные проверки для правил, которые уже соврали. **Фальсификация:** вернул файл правил с `origin/main` → красные `test_percentage_annotations_come_from_a_ratio` и `test_known_two_rules_are_fixed`; с правкой — `3 passed`. ## Чего этот PR НЕ делает - Не трогает пороги и условия — сработки остаются прежними. - Не заводит `promtool` в CI: сейчас правила не валидируются вообще ничем, но это отдельная задача (здесь `promtool check` прогнан руками на боевом контейнере). ## Приёмка Следующая сработка любого из двух правил приходит с числом в диапазоне 0–100 %, согласованным с порогом правила.
bot-backend added 1 commit 2026-09-12 09:27:43 +00:00
fix(alerts): в тексте тревоги печаталась не та величина, которую текст называет
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
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 / changes (pull_request) Successful in 13s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m30s
CI / backend-tests (pull_request) Successful in 17m50s
33714e464e
Боевые сообщения в Telegram 12.09:
  «apps / tradein-browser: 2.684e+11% от mem_limit. Дальше OOM-kill.»
  «apps / listings: доля HOT 75.21%.» — при пороге срабатывания «доля < 20%»

Причина общая и она про ФОРМУ выражения, а не про условие: в PromQL `A and B`
возвращает ЗНАЧЕНИЯ ЛЕВОЙ части, отфильтрованные правой. В `$value` попадало A:

  - ContainerNearMemoryLimit: слева стоял `container_spec_memory_limit_bytes` —
    в сообщение уходил лимит в байтах (2 684 354 560), отрендеренный как
    процент. Замер 12.09: настоящее потребление того контейнера — 1.2 % лимита.
  - PostgresLowHotUpdateRatio: слева стоял `rate(tup_upd[6h])` — апдейтов в
    секунду. Это опаснее: 0.7521 превращалось в «75.21%», попадало в
    правдоподобный диапазон и противоречило собственному порогу, но выглядело
    настоящим числом. Замер 12.09 по listings: rate(tup_upd[6h]) = 0.0411 →
    сообщение сказало бы «4.11%», настоящая доля HOT = 0.00%.

Условия срабатывания в обоих случаях были ВЕРНЫ — врал только текст, поэтому
дефект и прожил незамеченным.

Правка: отношение вынесено влево, а побочное условие — внутрь знаменателя
(`X / (Y > 0)`), где оно и фильтрует серии, и защищает от деления на ноль.
Проверено на живом Prometheus (только чтение): новое выражение памяти отдаёт
доли 0.35–0.71 (топ — gendesign-infra-postgres 70.8 %), новое выражение HOT —
доли 0.00–1.00. `promtool check rules` — SUCCESS, 16 rules.

Третье правило того же семейства (PostgresDeadTuplesHigh) верно, но верно
случайно: печатаемая величина совпала с левым операндом. Помечено комментарием,
чтобы его не «причесали» по образцу двух других.

Гейт: backend/tests/ops/test_alert_value_is_the_described_quantity.py — если
описание рендерит `$value` как долю (`humanizePercentage`), левая часть
выражения обязана содержать деление. Фальсификация: вернул файл правил с
origin/main → красные test_percentage_annotations_come_from_a_ratio и
test_known_two_rules_are_fixed; с правкой — 3 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bot-backend merged commit cfcdb9393c into main 2026-09-12 10:12:53 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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#3464
No description provided.