fix(tradein/db): widen offer_price_history.diff_percent (5,2)→(8,2) + clamp в триггере (краш full_load) #1827

Merged
lekss361 merged 1 commit from fix/tradein-diff-percent-overflow into main 2026-06-20 08:46:59 +00:00
Owner

Проблема (прод-инцидент, run_id=260, 2026-06-20)

avito_full_load упал с psycopg.errors.NumericValueOutOfRange: numeric field overflow (precision 5, scale 2) на бакете 2-комнатных. Источник — DB-триггер record_listing_price_change() (116_offer_price_history_change_trigger.sql) на listings AFTER UPDATE OF price_rub: пишет offer_price_history.diff_percent = round((NEW.price−OLD.price)/OLD.price*100, 2) в колонку numeric(5,2) (макс ±999.99%). У листинга дельта цены оказалась >1000% (вероятно OLD.price_rub мусорно-мал) → overflow → INSERT в listings падает → краш всего свипа.

Триггер общий для всех источников (кроме cian-skip) → ронял бы и cian_full_load/yandex_full_load при большой дельте. Латентный баг, Avito-load на него наткнулся (анти-бан фикс #1796 при этом отработал — 1356 листингов собрано до краша).

Фикс — миграция data/sql/131_fix_diff_percent_overflow.sql

  1. Расширение колонки: ALTER TABLE offer_price_history ALTER COLUMN diff_percent TYPE numeric(8,2) (макс ±999999.99%, покрывает реалистичные + glitch-дельты; расширение precision не теряет данные).
  2. Кламп в триггере (CREATE OR REPLACE FUNCTION record_listing_price_change(), belt-and-suspenders): LEAST(GREATEST(round(...), -999999.99), 999999.99) — даже будущая экстремальная дельта не вызовет overflow.

Все guard'ы из 116 сохранены без регрессий: same-value skip, NULL skip, source='cian' backfill-exclusion (+ комментарий), OLD.price_rub>0 гейт, ON CONFLICT DO NOTHING.

Деплой / идемпотентность

  • Авто-применится на деплое tradein через _schema_migrations. После мержа+деплоя full_load всех источников перестанет падать на overflow.
  • Идемпотентно: ALTER ... TYPE numeric(8,2) повторно = no-op; CREATE OR REPLACE FUNCTION идемпотентно.
  • Номер 131 свободен (проверено). DDL, литеральный SQL.

Связано: #1796 (avito rotation — отдельно зафикшен), 116 (триггер), 023 (колонка).

## Проблема (прод-инцидент, run_id=260, 2026-06-20) `avito_full_load` упал с `psycopg.errors.NumericValueOutOfRange: numeric field overflow` (precision 5, scale 2) на бакете 2-комнатных. Источник — DB-триггер `record_listing_price_change()` (`116_offer_price_history_change_trigger.sql`) на `listings AFTER UPDATE OF price_rub`: пишет `offer_price_history.diff_percent = round((NEW.price−OLD.price)/OLD.price*100, 2)` в колонку **`numeric(5,2)` (макс ±999.99%)**. У листинга дельта цены оказалась >1000% (вероятно OLD.price_rub мусорно-мал) → overflow → INSERT в `listings` падает → **краш всего свипа**. Триггер общий для всех источников (кроме `cian`-skip) → ронял бы и `cian_full_load`/`yandex_full_load` при большой дельте. Латентный баг, Avito-load на него наткнулся (анти-бан фикс #1796 при этом отработал — 1356 листингов собрано до краша). ## Фикс — миграция `data/sql/131_fix_diff_percent_overflow.sql` 1. **Расширение колонки:** `ALTER TABLE offer_price_history ALTER COLUMN diff_percent TYPE numeric(8,2)` (макс ±999999.99%, покрывает реалистичные + glitch-дельты; расширение precision не теряет данные). 2. **Кламп в триггере** (`CREATE OR REPLACE FUNCTION record_listing_price_change()`, belt-and-suspenders): `LEAST(GREATEST(round(...), -999999.99), 999999.99)` — даже будущая экстремальная дельта не вызовет overflow. Все guard'ы из 116 сохранены без регрессий: same-value skip, NULL skip, `source='cian'` backfill-exclusion (+ комментарий), `OLD.price_rub>0` гейт, `ON CONFLICT DO NOTHING`. ## Деплой / идемпотентность - Авто-применится на деплое tradein через `_schema_migrations`. После мержа+деплоя `full_load` всех источников перестанет падать на overflow. - Идемпотентно: `ALTER ... TYPE numeric(8,2)` повторно = no-op; `CREATE OR REPLACE FUNCTION` идемпотентно. - Номер 131 свободен (проверено). DDL, литеральный SQL. Связано: #1796 (avito rotation — отдельно зафикшен), 116 (триггер), 023 (колонка).
lekss361 added 1 commit 2026-06-20 08:44:06 +00:00
fix(tradein/db): widen offer_price_history.diff_percent numeric(5,2)→(8,2) + clamp в триггере (краш full_load)
All checks were successful
CI / changes (pull_request) Successful in 6s
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
89cbddf318
lekss361 merged commit 89590424f5 into main 2026-06-20 08:46:59 +00:00
lekss361 deleted branch fix/tradein-diff-percent-overflow 2026-06-20 08:46:59 +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#1827
No description provided.