feat(tradein/deactivate-stale): страховочные рельсы объёма снятия (PR-B) #3066

Merged
lekss361 merged 1 commit from feat/deactivate-stale-safety-rails into main 2026-08-24 08:44:33 +00:00
Owner

Продолжение #2659 после PR-A (#3056).

НЕ мержить до утра 24.08 — и это не формальность, а суть

Гейт ниже опирается на floor_n_pairs. Проверка на проде: ни один прогон ещё не выдал этот счётчик

SELECT ... FROM scrape_runs WHERE source LIKE 'deactivate_stale%' AND counters ? 'floor_n_pairs'
-> (0 rows)

PR-A задеплоен (ccdd7553 на проде, floor_n_pairs есть в работающем контейнере), а расписания деактивации ежедневные, последний прогон 23.08 06:28-07:55 UTC, следующий 24.08 06:31-07:58 UTC.

Значит порог min_floor_pairs=30 сейчас выставлен против нуля наблюдений. Если у какого-то источника здоровый floor_n_pairs окажется, скажем, 12 — гейт заморозит его деактивацию с первого же дня, и выглядеть это будет как «гейт корректно отработал» (skipped_floor_degraded). Это ровно тот класс тихого отказа, ради которого весь PR и делается.

Через ~9 часов появятся первые четыре реальных значения. Ждать один цикл стоит ноль, а порог из догадки становится замером:

SELECT source, counters->>'floor_n_pairs' AS n_pairs, counters->>'confirmations' AS confirms
FROM scrape_runs
WHERE source LIKE 'deactivate_stale%' AND started_at > CURRENT_DATE
ORDER BY id DESC;

Если у всех четырёх (avito / cian / domklik / yandex) n_pairs заметно выше 30 — мержить как есть. Если у кого-то ниже или около — правится одна константа.

Что было

У джобы деактивации не было ни одного ограничителя ОБЪЁМА снятия. min_confirmations может пропустить прогон целиком, пол переобхода поднимает TTL, cap_mult ограничивает сам пол — но ни один не смотрит на размер выборки, из которой percentile_disc посчитал квантиль, и ни один не смотрит на то, сколько строк снимет UPDATE. UPDATE идёт одним statement'ом без LIMIT: при обвале любого гейта выше снимается сколько снимется.

Рельс 1 — гейт деградации пола

floor_n_pairs ниже min_floor_pairs (30) либо упал более чем в floor_drop_ratio (5) раз относительно предыдущего успешного прогона того же расписанияskipped_floor_degraded, ни одна строка не тронута.

Почему самокалибрующийся, а не ручной порог: у floor_n_pairs истории почти нет (счётчик из PR-A), поэтому главный сигнал — относительный, «прогон сам с собой». Абсолютный порог — страховка на случай, когда истории ещё нет или она сама уже вырождена (иначе относительный сравнивал бы вырожденное с вырожденным и молчал бы вечно).

Сравнение идёт по scrape_runs.source, не по listing_source: у deactivate_stale_cian и deactivate_stale_cian_null_segment listing_source один и тот же ('cian'), но это две независимые серии с несопоставимым масштабом выборки — сравнивать их друг с другом было бы категориальной ошибкой.

floor_drop_ratio=5 заимствован по порядку величины у соседнего, уже проверенного на реальном инциденте гейта: здоровый разброс avito по суткам — confirmations 2542..6079, то есть ~2.4×. Штатный шум заведомо не достигает 5×, а провал 10.07-26.07 (падение в 5-10+ раз) ловится.

Рельс 2 — аварийный потолок объёма

max_deactivated=15000. Preflight count(*) по тому же предикату, что исполнит UPDATE → abort до записи.

Это не рабочий порог, а предохранитель последней инстанции: исторический максимум легитимного снятия — 9 300 (avito 06.06), далее 6 531 / 6 131 / 4 959 / 3 909 (последнее owner явно подтвердил как здоровую чистку). Запас 1.6× над максимумом, но катастрофу на порядок крупнее ловит.

Порога по ДОЛЕ пула сознательно нет. Пул, с которым эта джоба реально работает, никогда не записывался: восстановить постфактум можно только по listing_sources, а deactivated считает строки listings — разная гранулярность. Проверено на историческом ряду: доля деактивированного от восстановленного пула — 40 %, 97 %, 317 %, 1652 %. Ряд не осмысленный, калибровать не на чем. Поэтому deactivation_candidates, active_pool, deactivated_pct пока только пишутся; долевой порог — когда накопится собственная история.

Область действия

Оба рельса живут внутри revisit_floor_quantile > 0. Проверено на проде:

Расписание revisit_floor_quantile Гейт применится
avito / cian / domklik / yandex не задан → код-дефолт да
cian_null_segment / yandex_null_segment 0 явно нет

То есть null-сегментные джобы (чей пул на 2-3 порядка меньше потолка) гейт не затрагивает — это подтверждено конфигом, а не только докстрингом.

Валидация параметров

min_floor_pairs / floor_drop_ratio / max_deactivated приходят из default_params расписания, то есть из jsonb. Отбивается тот же класс опечатки, что у ttl_days/cap_mult: bool проверяется до числового сравнения (иначе True < 30 тихо прошло бы как 1 < 30).

Test plan

  • 249 passed, 1 skipped (-k "deactivate or stale")
  • Новые тесты: test_deactivate_stale_floor_degradation.py, test_deactivate_stale_deactivation_cap.py — включая test_candidates_predicate_matches_update_predicate, который стережёт синхронность preflight-предиката с UPDATE (предикат продублирован текстуально: рефакторинг уже протестированных UPDATE-builder'ов вне скоупа)
  • psycopg v3: только CAST(:x AS type), :x::type в диффе нет
  • Замер floor_n_pairs на прогоне 24.08 утром — до мержа (см. блок вверху)

Побочное

uv.lock — две строки requires-dist подтянуты к pyproject (curl-cffi >=0.7.0>=0.15.0). Устранение устаревшей записи, не смена зависимости: разрешённые версии не изменились, лок расходился с pyproject уже на main и регенерировался при любом вызове uv.

Refs #2659

Продолжение #2659 после PR-A (#3056). > ## ⛔ НЕ мержить до утра 24.08 — и это не формальность, а суть > > Гейт ниже опирается на `floor_n_pairs`. Проверка на проде: **ни один прогон ещё не выдал этот счётчик** — > ``` > SELECT ... FROM scrape_runs WHERE source LIKE 'deactivate_stale%' AND counters ? 'floor_n_pairs' > -> (0 rows) > ``` > PR-A задеплоен (`ccdd7553` на проде, `floor_n_pairs` есть в работающем контейнере), а расписания деактивации ежедневные, последний прогон 23.08 06:28-07:55 UTC, следующий **24.08 06:31-07:58 UTC**. > > Значит порог `min_floor_pairs=30` сейчас выставлен **против нуля наблюдений**. Если у какого-то источника здоровый `floor_n_pairs` окажется, скажем, 12 — гейт заморозит его деактивацию с первого же дня, и выглядеть это будет как «гейт корректно отработал» (`skipped_floor_degraded`). Это ровно тот класс тихого отказа, ради которого весь PR и делается. > > Через ~9 часов появятся первые четыре реальных значения. Ждать один цикл стоит ноль, а порог из догадки становится замером: > ```sql > SELECT source, counters->>'floor_n_pairs' AS n_pairs, counters->>'confirmations' AS confirms > FROM scrape_runs > WHERE source LIKE 'deactivate_stale%' AND started_at > CURRENT_DATE > ORDER BY id DESC; > ``` > Если у всех четырёх (avito / cian / domklik / yandex) `n_pairs` заметно выше 30 — мержить как есть. Если у кого-то ниже или около — правится одна константа. ## Что было У джобы деактивации не было **ни одного** ограничителя ОБЪЁМА снятия. `min_confirmations` может пропустить прогон целиком, пол переобхода поднимает TTL, `cap_mult` ограничивает сам пол — но ни один не смотрит на **размер выборки**, из которой `percentile_disc` посчитал квантиль, и ни один не смотрит на то, **сколько строк снимет UPDATE**. UPDATE идёт одним statement'ом без LIMIT: при обвале любого гейта выше снимается сколько снимется. ## Рельс 1 — гейт деградации пола `floor_n_pairs` ниже `min_floor_pairs` (30) **либо** упал более чем в `floor_drop_ratio` (5) раз относительно предыдущего успешного прогона **того же расписания** → `skipped_floor_degraded`, ни одна строка не тронута. Почему самокалибрующийся, а не ручной порог: у `floor_n_pairs` истории почти нет (счётчик из PR-A), поэтому главный сигнал — **относительный**, «прогон сам с собой». Абсолютный порог — страховка на случай, когда истории ещё нет или она сама уже вырождена (иначе относительный сравнивал бы вырожденное с вырожденным и молчал бы вечно). Сравнение идёт по `scrape_runs.source`, **не** по `listing_source`: у `deactivate_stale_cian` и `deactivate_stale_cian_null_segment` `listing_source` один и тот же (`'cian'`), но это две независимые серии с несопоставимым масштабом выборки — сравнивать их друг с другом было бы категориальной ошибкой. `floor_drop_ratio=5` заимствован по порядку величины у соседнего, уже проверенного на реальном инциденте гейта: здоровый разброс avito по суткам — `confirmations` 2542..6079, то есть ~2.4×. Штатный шум заведомо не достигает 5×, а провал 10.07-26.07 (падение в 5-10+ раз) ловится. ## Рельс 2 — аварийный потолок объёма `max_deactivated=15000`. Preflight `count(*)` по **тому же** предикату, что исполнит UPDATE → abort **до** записи. Это не рабочий порог, а предохранитель последней инстанции: исторический максимум легитимного снятия — 9 300 (avito 06.06), далее 6 531 / 6 131 / 4 959 / 3 909 (последнее owner явно подтвердил как здоровую чистку). Запас 1.6× над максимумом, но катастрофу на порядок крупнее ловит. **Порога по ДОЛЕ пула сознательно нет.** Пул, с которым эта джоба реально работает, никогда не записывался: восстановить постфактум можно только по `listing_sources`, а `deactivated` считает строки `listings` — разная гранулярность. Проверено на историческом ряду: доля деактивированного от восстановленного пула — 40 %, 97 %, 317 %, 1652 %. Ряд не осмысленный, калибровать не на чем. Поэтому `deactivation_candidates`, `active_pool`, `deactivated_pct` пока **только пишутся**; долевой порог — когда накопится собственная история. ## Область действия Оба рельса живут внутри `revisit_floor_quantile > 0`. Проверено на проде: | Расписание | `revisit_floor_quantile` | Гейт применится | |---|---|---| | avito / cian / domklik / yandex | не задан → код-дефолт | **да** | | cian_null_segment / yandex_null_segment | `0` явно | нет | То есть null-сегментные джобы (чей пул на 2-3 порядка меньше потолка) гейт не затрагивает — это подтверждено конфигом, а не только докстрингом. ## Валидация параметров `min_floor_pairs` / `floor_drop_ratio` / `max_deactivated` приходят из `default_params` расписания, то есть из jsonb. Отбивается тот же класс опечатки, что у `ttl_days`/`cap_mult`: `bool` проверяется **до** числового сравнения (иначе `True < 30` тихо прошло бы как `1 < 30`). ## Test plan - [x] `249 passed, 1 skipped` (`-k "deactivate or stale"`) - [x] Новые тесты: `test_deactivate_stale_floor_degradation.py`, `test_deactivate_stale_deactivation_cap.py` — включая `test_candidates_predicate_matches_update_predicate`, который стережёт синхронность preflight-предиката с UPDATE (предикат продублирован текстуально: рефакторинг уже протестированных UPDATE-builder'ов вне скоупа) - [x] psycopg v3: только `CAST(:x AS type)`, `:x::type` в диффе нет - [ ] **Замер `floor_n_pairs` на прогоне 24.08 утром — до мержа** (см. блок вверху) ## Побочное `uv.lock` — две строки `requires-dist` подтянуты к `pyproject` (`curl-cffi >=0.7.0` → `>=0.15.0`). Устранение устаревшей записи, не смена зависимости: разрешённые версии не изменились, лок расходился с pyproject уже на main и регенерировался при любом вызове `uv`. Refs #2659
lekss361 added 1 commit 2026-08-23 21:25:52 +00:00
feat(tradein/deactivate-stale): страховочные рельсы объёма снятия
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
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 10s
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
CI Trade-In / backend-tests (pull_request) Successful in 4m34s
22ccc1050c
Продолжение #2659 после PR-A. У джобы деактивации не было НИ ОДНОГО
ограничителя ОБЪЁМА: min_confirmations может пропустить прогон целиком,
пол переобхода поднимает TTL, cap_mult ограничивает сам пол — но ни один
не смотрит на РАЗМЕР ВЫБОРКИ, из которой посчитан квантиль, и ни один не
смотрит на то, сколько строк UPDATE снимет. UPDATE идёт одним statement'ом
без LIMIT: при обвале любого гейта выше снимается сколько снимется.

Добавлено два рельса, оба внутри revisit_floor_quantile > 0:

1. Гейт деградации пола. floor_n_pairs (счётчик из PR-A) ниже
   min_floor_pairs=30 ЛИБО упал более чем впятеро относительно предыдущего
   успешного прогона ТОГО ЖЕ РАСПИСАНИЯ -> skipped_floor_degraded, ни одна
   строка не тронута. Сравнение идёт «прогон сам с собой» по
   scrape_runs.source, а не по listing_source: у cian и cian_null_segment
   listing_source один, но это две серии с несопоставимым масштабом
   выборки — сравнивать их было бы категориальной ошибкой.

2. Аварийный потолок max_deactivated=15000. Preflight count(*) по ТОМУ ЖЕ
   предикату, что исполнит UPDATE — abort ДО записи. Не рабочий порог, а
   предохранитель: исторический максимум легитимного снятия 9 300 (avito
   06.06), запас 1.6x.

Порога по ДОЛЕ пула сознательно НЕТ: пул, с которым джоба реально работает,
никогда не записывался, а восстановленный постфактум ряд даёт 40 %, 97 %,
317 %, 1652 % — калибровать не на чем. Поэтому deactivation_candidates,
active_pool и deactivated_pct пока только ПИШУТСЯ.

Валидация параметров — тот же класс, что у ttl_days/cap_mult: bool
отбивается ДО числового сравнения (jsonb-опечатка true/false в
default_params расписания — единственный запланированный способ их задать).

uv.lock: две строки requires-dist подтянуты к pyproject (curl-cffi
>=0.7.0 -> >=0.15.0). Это устранение устаревшей записи, а не смена
зависимости — разрешённые версии не изменились; лок расходился с
pyproject на main и регенерировался при любом вызове uv.

Проверено: 249 passed, 1 skipped (-k "deactivate or stale").

Refs #2659
Author
Owner

Замер состоялся — снимаю стоп. Порог безопасен с запасом в 21–71 раз.

Первые реальные floor_n_pairs (прогоны 24.08 утром)

Расписание время UTC floor_n_pairs confirmations против порога 30 revisit_floor_days снято
deactivate_stale_yandex 07:34 2124 2319 ×71 55 0
deactivate_stale_cian 08:00 1464 1581 ×49 12 187
deactivate_stale_avito 06:32 747 1697 ×25 15 0
deactivate_stale_domklik 07:59 625 783 ×21 36 0
deactivate_stale_yandex_null_segment 07:21 гейт не применяется 0
deactivate_stale_cian_null_segment 07:53 гейт не применяется 0

Все шесть прогонов done.

Что это подтверждает

1. min_floor_pairs=30 никого не заморозит. Самое низкое реальное значение — domklik 625, это в 21 раз выше порога. Опасение из шапки PR («если у источника здоровое значение окажется 12, гейт заморозит его деактивацию, и выглядеть это будет как „гейт отработал“») не подтвердилось. Мержу как есть, без правки константы.

2. Область действия совпала с предсказанной по конфигу. У обоих null-сегментных расписаний floor_n_pairs пуст — пол у них выключен (revisit_floor_quantile: 0 в default_params), значит гейт их не касается. Это ровно то, что я записал в таблицу «область действия» до замера, и теперь оно подтверждено фактом, а не чтением конфига.

3. floor_drop_ratio=5 тоже выглядит разумно на этих числах. Падение впятеро означало бы, например, domklik с 625 до менее чем 125 — это обвал, а не суточный шум. Соотношение floor_n_pairs к confirmations при этом стабильное и осмысленное: 0.44 (avito), 0.80 (domklik), 0.92 (yandex/cian) — то есть счётчик считает подмножество, как и задумано, а не случайную величину.

Оговорка

Это один срез, а не ряд. Порог min_floor_pairs теперь опирается на четыре реальных наблюдения вместо нуля — этого достаточно, чтобы не заморозить источник сегодня, но недостаточно, чтобы считать 30 откалиброванным числом. Относительный гейт (floor_drop_ratio) остаётся главным механизмом именно поэтому: он сравнивает прогон сам с собой и не требует чужой калибровки.

Через неделю стоит пересмотреть обе константы уже по накопленному ряду — как и написано в комментарии у самих констант.

CI зелёный (8/8), mergeable=true. Мержу.

Замер состоялся — снимаю стоп. **Порог безопасен с запасом в 21–71 раз.** ## Первые реальные `floor_n_pairs` (прогоны 24.08 утром) | Расписание | время UTC | `floor_n_pairs` | `confirmations` | против порога 30 | `revisit_floor_days` | снято | |---|---|---|---|---|---|---| | `deactivate_stale_yandex` | 07:34 | **2124** | 2319 | **×71** | 55 | 0 | | `deactivate_stale_cian` | 08:00 | **1464** | 1581 | **×49** | 12 | 187 | | `deactivate_stale_avito` | 06:32 | **747** | 1697 | **×25** | 15 | 0 | | `deactivate_stale_domklik` | 07:59 | **625** | 783 | **×21** | 36 | 0 | | `deactivate_stale_yandex_null_segment` | 07:21 | — | — | гейт не применяется | — | 0 | | `deactivate_stale_cian_null_segment` | 07:53 | — | — | гейт не применяется | — | 0 | Все шесть прогонов `done`. ## Что это подтверждает **1. `min_floor_pairs=30` никого не заморозит.** Самое низкое реальное значение — domklik 625, это в 21 раз выше порога. Опасение из шапки PR («если у источника здоровое значение окажется 12, гейт заморозит его деактивацию, и выглядеть это будет как „гейт отработал“») не подтвердилось. Мержу как есть, без правки константы. **2. Область действия совпала с предсказанной по конфигу.** У обоих null-сегментных расписаний `floor_n_pairs` пуст — пол у них выключен (`revisit_floor_quantile: 0` в `default_params`), значит гейт их не касается. Это ровно то, что я записал в таблицу «область действия» до замера, и теперь оно подтверждено фактом, а не чтением конфига. **3. `floor_drop_ratio=5` тоже выглядит разумно на этих числах.** Падение впятеро означало бы, например, domklik с 625 до менее чем 125 — это обвал, а не суточный шум. Соотношение `floor_n_pairs` к `confirmations` при этом стабильное и осмысленное: 0.44 (avito), 0.80 (domklik), 0.92 (yandex/cian) — то есть счётчик считает подмножество, как и задумано, а не случайную величину. ## Оговорка Это **один** срез, а не ряд. Порог `min_floor_pairs` теперь опирается на четыре реальных наблюдения вместо нуля — этого достаточно, чтобы не заморозить источник сегодня, но недостаточно, чтобы считать 30 откалиброванным числом. Относительный гейт (`floor_drop_ratio`) остаётся главным механизмом именно поэтому: он сравнивает прогон сам с собой и не требует чужой калибровки. Через неделю стоит пересмотреть обе константы уже по накопленному ряду — как и написано в комментарии у самих констант. CI зелёный (8/8), `mergeable=true`. Мержу.
lekss361 merged commit 9810ae350f into main 2026-08-24 08:44:33 +00:00
lekss361 deleted branch feat/deactivate-stale-safety-rails 2026-08-24 08:44:34 +00:00
Author
Owner

Первые прогоны с рельсами в проде (25.08). Механизм работает целиком, ложных срабатываний нет.

Что показали живые прогоны

Расписание время floor_n_pairs ..._previous skipped_floor_degraded skipped_cap_exceeded снято deactivated_pct
deactivate_stale_avito 06:19 1063 747 0 0
deactivate_stale_domklik 07:06 862 625 0 0
deactivate_stale_yandex_null_segment 07:13 0
deactivate_stale_cian 07:37 1494 1464 281 1

Все done. Два оставшихся расписания (yandex, cian_null_segment) ещё не отработали на момент замера.

Главное: относительный гейт получил базу сравнения

floor_n_pairs_previous реально читается и записывается — 747, 625, 1464. Это был самый хрупкий узел всей правки: _PREVIOUS_FLOOR_N_PAIRS_SQL ищет предыдущий успешный прогон того же расписания по scrape_runs.source, и если бы запрос не находил строку (не тот ключ, не тот статус, counters ->> не тот тип), гейт молча деградировал бы до одного лишь абсолютного порога — и никто бы не заметил, потому что абсолютный порог сейчас не срабатывает.

Значения совпадают со вчерашним замером один в один (747 → вчерашний avito, 625 → domklik, 1464 → cian). То есть подхватывается именно предыдущий прогон, а не случайная строка.

Ложных срабатываний нет

skipped_floor_degraded и skipped_cap_exceeded пусты во всех прогонах. Это ожидаемо и правильно:

  • абсолютный порог 30 — самое низкое значение 862, запас ×29;
  • порог падения ×5 — все значения выросли (747→1063 это +42 %, 625→862 это +38 %, 1464→1494 это +2 %), падения не было, гейт промолчал.

Рост объясним: вчерашние прогоны шли на меньшей базе, плюс domclick_city_sweep за ночь добавил листингов (у domklik активных стало 2899 против 1351).

Наблюдательные счётчики тоже пишутся

deactivated_pct = 1 у cian при 281 снятых — то есть deactivation_candidates / active_pool считаются и preflight отрабатывает. Это те метрики, по которым позже можно будет откалибровать долевой порог, которого сейчас сознательно нет.

Первая точка ряда

В шапке PR я оговорил, что один срез — не ряд, и что через неделю константы стоит пересмотреть. Теперь точек две, и они уже дают полезное:

avito domklik cian
24.08 747 625 1464
25.08 1063 862 1494
изменение +42 % +38 % +2 %

Суточный разброс до +42 % — это и есть тот шум, относительно которого выбран порог падения ×5. Пока данные подтверждают, что запас выбран разумно: даже сорокапроцентные колебания далеки от пятикратного обвала. Но нужен ряд подлиннее, прежде чем считать это доказанным — колебание вверх и обвал вниз не обязаны быть симметричны.

Refs #2659

Первые прогоны **с рельсами в проде** (25.08). Механизм работает целиком, ложных срабатываний нет. ## Что показали живые прогоны | Расписание | время | `floor_n_pairs` | `..._previous` | `skipped_floor_degraded` | `skipped_cap_exceeded` | снято | `deactivated_pct` | |---|---|---|---|---|---|---|---| | `deactivate_stale_avito` | 06:19 | **1063** | **747** | — | — | 0 | 0 | | `deactivate_stale_domklik` | 07:06 | **862** | **625** | — | — | 0 | 0 | | `deactivate_stale_yandex_null_segment` | 07:13 | — | — | — | — | 0 | — | | `deactivate_stale_cian` | 07:37 | **1494** | **1464** | — | — | **281** | **1** | Все `done`. Два оставшихся расписания (`yandex`, `cian_null_segment`) ещё не отработали на момент замера. ## Главное: относительный гейт получил базу сравнения `floor_n_pairs_previous` **реально читается и записывается** — 747, 625, 1464. Это был самый хрупкий узел всей правки: `_PREVIOUS_FLOOR_N_PAIRS_SQL` ищет предыдущий успешный прогон **того же расписания** по `scrape_runs.source`, и если бы запрос не находил строку (не тот ключ, не тот статус, `counters ->>` не тот тип), гейт молча деградировал бы до одного лишь абсолютного порога — и никто бы не заметил, потому что абсолютный порог сейчас не срабатывает. Значения совпадают со вчерашним замером один в один (747 → вчерашний avito, 625 → domklik, 1464 → cian). То есть подхватывается именно предыдущий прогон, а не случайная строка. ## Ложных срабатываний нет `skipped_floor_degraded` и `skipped_cap_exceeded` пусты во всех прогонах. Это ожидаемо и правильно: - **абсолютный порог 30** — самое низкое значение 862, запас ×29; - **порог падения ×5** — все значения **выросли** (747→1063 это +42 %, 625→862 это +38 %, 1464→1494 это +2 %), падения не было, гейт промолчал. Рост объясним: вчерашние прогоны шли на меньшей базе, плюс `domclick_city_sweep` за ночь добавил листингов (у domklik активных стало 2899 против 1351). ## Наблюдательные счётчики тоже пишутся `deactivated_pct` = 1 у cian при 281 снятых — то есть `deactivation_candidates` / `active_pool` считаются и preflight отрабатывает. Это те метрики, по которым позже можно будет откалибровать долевой порог, которого сейчас сознательно нет. ## Первая точка ряда В шапке PR я оговорил, что один срез — не ряд, и что через неделю константы стоит пересмотреть. Теперь точек две, и они уже дают полезное: | | avito | domklik | cian | |---|---|---|---| | 24.08 | 747 | 625 | 1464 | | 25.08 | 1063 | 862 | 1494 | | изменение | +42 % | +38 % | +2 % | Суточный разброс до **+42 %** — это и есть тот шум, относительно которого выбран порог падения ×5. Пока данные подтверждают, что запас выбран разумно: даже сорокапроцентные колебания далеки от пятикратного обвала. Но нужен ряд подлиннее, прежде чем считать это доказанным — колебание вверх и обвал вниз не обязаны быть симметричны. Refs #2659
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#3066
No description provided.