fix(tradein/proxy): упавшая проба присваивала себе бан боевого сбора (#2800) #2805

Merged
bot-backend merged 1 commit from fix/probe-must-not-steal-ban into main 2026-08-09 20:13:11 +00:00

1 commit

Author SHA1 Message Date
6e50d267ba fix(tradein/proxy): упавшая проба присваивала себе бан боевого сбора (#2800)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 7s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-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 3m51s
Прод 09.08.2026, пара (1, cian): строка `banned:cian, ban_count=1, до 00:21`
после упавшей пробы стала `probe:browser, ban_count=2, до 07:43`. ON CONFLICT
DO UPDATE в mark_banned переписывал reason безусловно, поэтому проба забирала
чужую строку себе.

Фильтр «снимаю только своё» (clear_source_bans(only_reason=...)) при этом цел и
работает — удалить чужую строку проба не может. Но присвоенная строка уже
«своя», и следующий зелёный robots.txt снял бы ею бан, поставленный боевым
сбором по настоящему отказу площадки. Плюс метка перестаёт быть свидетельством:
'banned:cian' («площадка нас отбила») и 'probe:browser' («наша проба не смогла»)
— разные факты (ловушка #2764), а ban_count складывает события разного рода в
одну эскалацию: отдых пары вырос с 6 ч до 12 ч на счётчике, который заработан
не был.

Правило теперь в WHERE у DO UPDATE: строку берём, только если она истекла
(живого владельца нет), либо она уже наша, либо мы боевой сбор. Продление чужого
бана вместо «ничего не делать» отвергнуто: срок пересчитывается от now() по
НАШЕЙ эскалации и способен укоротить уже эскалированный чужой бан.

Асимметрия намеренная: боевой сбор строку пробы перехватывает. Его вердикт
сильнее, пара остаётся забаненной, метка становится точнее. Запрет и ему оставил
бы строку за пробой — и её же успех снёс бы настоящий бан площадки, то есть тот
же дефект зеркально. ban_count при перехвате наследуется: обнуление на смене
владельца стирало бы память об эскалации боевых банов.

Диагностика: 0 rows у апсерта теперь означает три разные вещи (чужой владелец /
защита последнего узла / нет узла) — mark_banned возвращает исход строкой и
пишет отдельный лог на каждый, а счётчик pair_banned больше не объявляет баном
то, чего не записал.
2026-08-10 01:07:23 +05:00