Commit graph

2908 commits

Author SHA1 Message Date
bot-backend
4c9f5b6e9f Merge remote-tracking branch 'forgejo/main' into fix/avito-detail-fields
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 11s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 4m42s
2026-08-21 23:45:40 +03:00
ee19254a41 Merge pull request 'fix(tradein/scraper-kit): актуализация отпечатка Авито — impersonate, Sec-Fetch-Site, referer, проверка антибот-кук' (#3038) from fix/3034-avito-fingerprint into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m48s
Deploy Trade-In / build-backend (push) Successful in 1m49s
Deploy Trade-In / deploy (push) Successful in 2m19s
Deploy Trade-In / deploy-status (push) Successful in 2s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
2026-08-21 20:45:25 +00:00
f34edeb512 chore(tradein/avito): ссылки на реальный issue #3047 вместо выдуманного номера
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 4m48s
Воркер сослался на #3086, которого не существует — тест с таким именем
остался бы загадкой для следующего читателя. Заведён настоящий issue #3047
с замерами покрытия и разбором дефекта; все ссылки и имя файла приведены
к нему.

Refs #3047
2026-08-21 23:40:22 +03:00
bot-backend
efce189c8b fix(tradein/avito): починить чтение параметров, добавить metro/is_homeowner/days_on_market
Разобрались по эталонной разметке (2 живые detail-карточки, 2026-08-21), что реально
парсится и что нет:

- sale_type: item-view/item-params — Avito отдаёт "О квартире" и "О доме" как ДВА
  ОТДЕЛЬНЫХ <div> с ОДНИМ И ТЕМ ЖЕ маркером, а не один div с двумя <ul>, как
  предполагал старый код. css_first брал только ПЕРВЫЙ div — sale_type это не
  задевало (он в первом блоке), но house_type/total_floors_house/лифты из второго
  блока терялись молча через мёртвую ветку `len(ul_els) >= 2`. Теперь читаем <ul>
  из ВСЕХ блоков с этим маркером — устойчиво к порядку блоков.
- metro_stations: раньше только эвристика по тексту описания (28 строк из 54 855).
  Основной источник теперь — структурная разметка (#item-view-address, иконка
  "Пешком до метро"), покрывает станции без ограничения на суффикс имени
  (METRO_RE ловил только "-ская"/"-инская"). Описание — фолбэк.
- is_homeowner: не парсился вовсе. [data-marker='seller-info/label'] — "Частное
  лицо" -> True, "Агентство" -> False. Подтверждено разными значениями на двух
  эталонах.
- days_on_market: на странице явно нет, но есть publish_date, из которого честно
  выводится. Заодно чинит сам publish_date — искали дату ВНУТРИ item-id-блока, а
  она в СОСЕДНЕМ [data-marker='item-view/item-date'] ("сегодня в HH:MM").
- cadastral_number: подтверждено отсутствие на странице (не парсим, не выдумываем).

Тесты на реальной разметке (backend/tests/test_avito_detail_fields_3086.py) на
обеих эталонных фикстурах (большие embedded JS-блобы вырезаны из фикстур —
не используются DOM-based парсером, экономят место). estimator.py не тронут —
ни одно поле не влияет на цену.
2026-08-21 23:36:19 +03:00
bot-backend
b3a39699d1 Merge remote-tracking branch 'forgejo/main' into fix/3034-avito-fingerprint
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
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
2026-08-21 23:34:34 +03:00
e80c9e08df Merge pull request 'ops(backup): missed-run detection + Forgejo code/DB backup to S3' (#3025) from feat/2203-backup-staleness-and-forgejo into main
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy Trade-In / changes (push) Successful in 12s
Deploy / build-backend (push) Has been skipped
Deploy / build-worker (push) Has been skipped
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 49s
Deploy / deploy (push) Successful in 1m45s
Deploy / deploy-status (push) Successful in 2s
Deploy / perimeter-smoke (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Successful in 2m44s
Deploy Trade-In / test (push) Successful in 3m59s
Deploy Trade-In / build-backend (push) Successful in 32s
Deploy Trade-In / deploy (push) Successful in 6m2s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
2026-08-21 17:12:03 +00:00
1202804268 Merge pull request 'fix(ops): дрилл восстановления ждал БД по сокету и умирал на старте настоящего сервера' (#3026) from fix/2203-restore-drill-readiness into main
Some checks failed
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
Deploy / deploy-caddy (push) Blocked by required conditions
Deploy / perimeter-smoke (push) Blocked by required conditions
Deploy / deploy-status (push) Blocked by required conditions
Deploy / changes (push) Has been cancelled
2026-08-21 17:11:59 +00:00
1d479d0150 Merge pull request 'fix(tradein/browser): дожидаться QRATOR PoW-челленджа Авито вместо тихой заглушки' (#3046) from fix/3045-browser-pow-challenge-wait into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 2m18s
Deploy Trade-In / deploy (push) Successful in 2m6s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
Reviewed-on: #3046
2026-08-21 17:01:24 +00:00
5c0fd78c1f fix(tradein/browser): цикл ожидания челленджа падал ровно на успешном исходе
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 / browser-tests (pull_request) Successful in 53s
Найдено при ревью ветки. `_wait_out_pow_challenge` опрашивал `page.content()`
без защиты, а челлендж перезагружает страницу САМ (`window.location =
location.href`). Вызов content(), попавший в момент этой перезагрузки, кидает
«Execution context was destroyed, most likely because of a navigation» — то
есть цикл ронял фетч ровно тогда, когда проверка успешно пройдена и мы
дождались того, ради чего ждали.

На моках дефект не воспроизводился: поддельная page навигацию не рвёт.

Добавлено:
  - `_content_during_navigation()` — content(), возвращающий None вместо
    исключения, если текст ошибки указывает на гонку с навигацией. Различаем
    по тексту, а не по типу: сервис не импортирует playwright, page приходит
    готовым. Всё прочее (закрытая страница, упавший браузер) пробрасывается.
  - Цикл трактует None как «ещё не устоялось, опроси снова».
  - Финальная догидрация тоже защищена: один короткий добор, затем внятная
    ошибка вместо падения на гонке.
  - Поддельная page в тестах умеет поднимать исключение из content();
    три теста на гонку — прохождение, вечная навигация, посторонняя ошибка.

Фальсификация: без правки два новых теста падают именно с
`Execution context was destroyed`. Полный сьют сервиса — 123 passed.

Refs #3045
2026-08-21 19:57:51 +03:00
bot-backend
0f6523f852 fix(tradein/browser): дожидаться QRATOR PoW-челленджа Авито вместо тихой заглушки
Живой замер 2026-08-21 (#3045): фиксированной паузы BROWSER_WAIT_MS (6с) не
хватает на цепочку «PoW-расчёт в JS → таймер 3с → self-reload → гидрация» —
4 из 6 карточек с органической навигацией отдавали 7891-байтную challenge-
страницу вместо контента (не бан, "проверка безопасности"). caller считал её
валидным HTML — парсер либо падал, либо молча ничего не находил.

_fetch_once теперь опрашивает page.content() (шаг ~1с, бюджет
BROWSER_CHALLENGE_WAIT_MS=30000) пока маркеры челленджа (startPow / "проверка
безопасности") не исчезнут, затем догидрируется тем же BROWSER_WAIT_MS. По
истечении бюджета — ChallengeTimeoutError вместо заглушки. wait_for_url не
годится: страница перезагружает саму себя, URL не меняется.

Бан-страница ("проблема с IP") распознаётся отдельно и падает сразу
(BanPageDetectedError), без траты бюджета ожидания — это не то же самое, что
челлендж, и ждать там нечего.

Провайдер-агностично по форме: включается только по факту маркеров в HTML,
cian/yandex/generic их никогда не отдают.
2026-08-21 19:52:00 +03:00
bebeb0c8f0 feat(tradein/avito): путь вторички для якорного и citywide-обхода (#3033, follow-up) (#3042)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m41s
Deploy Trade-In / build-backend (push) Successful in 1m40s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / deploy (push) Successful in 2m21s
2026-08-21 15:33:22 +00:00
17a6dae3a4 feat(tradein/avito): house-поля с детальной страницы доезжают в houses (#3036) (#3040)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m37s
Deploy Trade-In / build-backend (push) Successful in 2m55s
Deploy Trade-In / deploy (push) Successful in 3m20s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 10s
2026-08-21 14:48:36 +00:00
28f5e8079e Merge pull request 'fix(tradein/avito): страница SERP отдаёт 60 карточек, а не 50 — лишние 17% запросов' (#3041) from fix/avito-offers-per-page into main
Some checks failed
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Has been cancelled
2026-08-21 14:46:32 +00:00
6ffcd78d35 fix(tradein/avito): страница SERP отдаёт 60 карточек, а не 50
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 4m27s
Замер живьём 2026-08-21 через tradein-browser (camoufox, JS исполняется):
59-60 уникальных `data-item-id` на странице выдачи. «Лишние» сверх 50 —
обычные объявления с платным продвижением (`vas-icon_type-promoted`), они
лежат в том же списке под `page-title/count`, а не отдельным рекламным
блоком, и собираются наравне с остальными.

Направление эффекта важно понимать правильно. Константа участвует ТОЛЬКО
в `ceil(total / PAGE)`, поэтому занижение размера страницы ЗАВЫШАЛО
расчётное число страниц, а не занижало:

  - запрашивали примерно на 17 % страниц больше, чем нужно; при доле банов
    37-83 % по avito-заданиям лишние запросы — основная цена ошибки;
  - `tail_loss` считался как `total - cap * 50` и завышал потерю;
  - флаг `complete` в пагинации листа чаще ложно показывал «неполно».

Тихой потери данных НЕ было: условия «страница вернула меньше PAGE
карточек, значит последняя» в коде нет, пагинация ограничена только
`max_pages`. Тест закрепляет и значение, и направление арифметики, чтобы
неверная трактовка не вернулась при следующем рефакторинге.

Найдено при разборе Авито сверкой живого браузера со скраппером; полный
разбор — в волте `research/Avito_Live_Browser_Recon_0821.md`.

Refs #3033
2026-08-21 17:38:55 +03:00
bot-backend
fc2930fbb6 Merge remote-tracking branch 'forgejo/main' into fix/3034-avito-fingerprint
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 4m28s
2026-08-21 17:27:52 +03:00
7382ae123d feat(tradein/avito): полный обход качает вторичку родным фильтром пути (#3033) (#3039)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m42s
Deploy Trade-In / build-backend (push) Successful in 1m48s
Deploy Trade-In / deploy (push) Successful in 2m28s
Deploy Trade-In / deploy-status (push) Successful in 2s
Deploy Trade-In / perimeter-smoke (push) Successful in 10s
2026-08-21 14:22:48 +00:00
4ec3b79704 fix(tradein/scraper-kit): поднять пол curl-cffi до 0.15.0 — chrome146 в 0.7.0 не существует
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 11s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 4m27s
DEFAULT_IMPERSONATE переведён на chrome146, но оба pyproject продолжали
объявлять `curl-cffi>=0.7.0`. В 0.7.0 такого профиля нет: установка по
нижней границе — и curl_cffi роняет КАЖДЫЙ запрос скраппера на невалидном
impersonate, причём одинаково для avito, cian и yandex.

На проде стоит 0.15.0 (проверено в контейнере tradein-scraper: профили
chrome99 … chrome146 плюс алиас chrome), поэтому текущий деплой бы не
пострадал — но любая пересборка окружения с резолвом к нижней границе
воспроизвела бы отказ, и диагностировался бы он как «скрапперы легли»,
а не как несогласованная зависимость.

Refs #3034
2026-08-21 17:06:13 +03:00
bot-backend
fe628a5e17 fix(tradein/scraper-kit): actualize avito fingerprint - impersonate/Sec-Fetch-Site/referer/cookie-check
Живой замер браузера 2026-08-21 разошёлся с тем, что шлёт прогретая сессия:

- impersonate="chrome120" был захардкожен в 9+ местах (avito/{serp,detail,imv,
  houses}.py, pipeline.py x4, cian/valuation.py, yandex/valuation.py) вместо
  единой DEFAULT_IMPERSONATE (providers/_base.py). Обновление до chrome146
  (макс. доступный профиль curl_cffi 0.15.0; alias "chrome" НЕ используется -
  едет сам при апгрейде библиотеки без ревью) теперь меняется в одном месте.
  Guard-тест backend/tests/test_impersonate_single_source.py грепает всё
  дерево scraper_kit на литерал "chrome120".

- DOCUMENT_HEADERS ставил Sec-Fetch-Site="none" на уровне сессии, а Referer
  добавлялся per-request (curl_cffi мёржит per-request headers поверх
  session-level) - живой Referer с "none" рядом не бывает у настоящего
  Chrome. Добавлен referer_headers() (Sec-Fetch-Site="cross-site") для
  caller'ов с чужедоменным Referer; avito warm-up (yandex/ya.ru -> avito)
  теперь его использует. Внутренний avito-search -> avito-detail Referer
  (fetch_detail, same-origin) НЕ тронут - отдельный явный комментарий почему.

- Referer прогрева заменён с yandex.ru на ya.ru (живой переход из выдачи
  даёт короткий домен). ysclid сознательно не добавлен - значение выпускает
  Яндекс, подделка хуже отсутствия.

- warm_up_session/research_in_session считали прогрев успешным по HTTP-
  статусу и отсутствию firewall-маркеров, не проверяя антибот-cookies
  (__zzatw-*/cfidsw-*, сняты с живого браузера) - detail-батч на такой
  "прогретой" сессии сжигал прокси на обречённых 403. Теперь поднимают
  AvitoWarmupCookiesMissingError (наследник AvitoBlockedError - существующие
  except-блоки/ban_kind/proxy-ротация в pipeline и backfill ловят без
  изменений).

Полный backend pytest suite зелёный (4672 passed), ruff check чист.

Refs #3034
2026-08-21 17:02:53 +03:00
e31c537205 Merge pull request 'fix(db/ci): чистый старт БД не падает на 077 и не может уехать на пустой схеме' (#3011) from fix/2990-clean-start-initdb into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-browser (push) Successful in 44s
Deploy Trade-In / build-frontend (push) Successful in 2m25s
Deploy Trade-In / test (push) Successful in 3m36s
Deploy Trade-In / build-backend (push) Successful in 49s
Deploy Trade-In / deploy (push) Successful in 1m53s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 10s
2026-08-21 12:55:48 +00:00
b588278923 fix(ops): дрилл восстановления ждал БД по сокету и умирал на старте настоящего сервера
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / frontend-checks (pull_request) Has been skipped
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
`ops/restore-drill.sh` проверял готовность через `pg_isready -U postgres`, то
есть по unix-сокету. Образ postgres во время инициализации поднимает временный
сервер, который на сокете уже отвечает "accepting connections". Проба зеленела
посреди initdb, восстановление начиналось в этот временный сервер и погибало,
как только entrypoint гасил его ради настоящего:

    [12:26:22Z] Restoring dump into 'drill' (ON_ERROR_STOP=1 ...)
    FATAL:  terminating connection due to administrator command
    server closed the connection unexpectedly

Воспроизведено на проде 2026-08-21 на живом дампе `tradein-20260821-013001`:
падение через 6 секунд после старта. С `-h 127.0.0.1` тот же дамп проходит
восстановление и доходит до сборки индексов — временный сервер TCP не слушает,
поэтому по TCP проба зеленеет только на настоящем сервере.

Ровно та же проба и ровно по той же причине уже стоит в
`.forgejo/workflows/ci-tradein.yml:137-141`. Тот же дефект живёт и в
`.forgejo/workflows/deploy-tradein.yml:718-721` — там его чинят в PR #3011,
здесь не трогаю.

Refs #2203, #2989
2026-08-21 15:42:36 +03:00
bot-backend
c6b408e483 fix(ops): restore +x on new ops/*.sh (exec bit lost in prior commit, Windows core.filemode=false)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 13s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
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
2026-08-21 15:38:31 +03:00
bot-backend
83be088263 feat(ops): missed-run detection for backups + Forgejo code/DB backup to S3
Deliverable 1: ops/backup.sh and tradein-mvp/deploy/backup-tradein-db.sh now
write a sentinel file on every verified-good run. A new ops/check-backup-
staleness.sh (separate cron entry, hourly) alerts via the existing Telegram
channel (same TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID idiom as ops/uptime-
healthcheck.sh, transition-tracked so it doesn't spam) if a sentinel goes
stale. Shared logic (notify/sentinel/state) factored into ops/lib-backup.sh
so it isn't triplicated; the two existing scripts' own hardening (integrity
checks, retention, etc.) is untouched.

Deliverable 2: ops/backup-forgejo.sh — Forgejo (git.gendsgn.ru) has no backup
today. Dumps the shared-postgres `forgejo` DB + tars the bare-repo tree, both
off-box to s3://gendsgn-backups/forgejo/ under a SEPARATE, narrower S3 key
(root-of-bucket writer key stays out of this). The key doesn't exist yet —
the script refuses to run and exits non-zero, loudly, until the four
FORGEJO_S3_* vars are filled in (see the example env file and PR description
for the exact bucket policy JSON to create it with).

Refs #2203, #2989
2026-08-21 15:37:19 +03:00
bot-backend
4cb8f32dcc Merge remote-tracking branch 'forgejo/main' into fix/2990-clean-start-initdb
All checks were successful
CI / changes (pull_request) Successful in 10s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Successful in 57s
CI Trade-In / frontend-checks (pull_request) Successful in 1m40s
CI / openapi-codegen-check (pull_request) Successful in 2m45s
CI Trade-In / backend-tests (pull_request) Successful in 5m15s
CI / backend-tests (pull_request) Successful in 18m13s
2026-08-21 15:35:32 +03:00
bot-backend
a3ccbbd045 fix(db/ci): 077 гардится по USER MAPPING, деплой ждёт готовности БД по TCP
Deep-review BLOCK на PR #3011: обе правки чинили заявленный симптом только
частично.

077: гард считал pending-строки по source='rosreestr' AND dedup_hash ~
md5-паттерн и пропускал backfill, только если таких строк 0. На чистой БД
они есть — 003_seed_deals.sql сеет синтетические сделки с тем же паттерном,
значит pending > 0 уже на пустом томе, и миграция всё равно падала на
"user mapping not found" (воспроизведено в CI run 8257). Первичный гард
теперь проверяет напрямую наличие USER MAPPING для gendesign_remote
(идиома из app/core/fdw.py:57-62), счётчик pending оставлен вторым —
экономит обращение к FDW, когда мигрировать уже нечего.

deploy-tradein.yml: цикл ожидания готовности postgres ходил по unix-сокету
(pg_isready без -h). На пустом томе временный init-сервер отвечает на
сокете, пока docker-entrypoint-initdb.d ещё прогоняет цепочку миграций —
проба зеленела посреди initdb. Добавлен -h 127.0.0.1 (тот же приём уже
есть в ci-tradein.yml:157) — TCP открывается только после полного
завершения initdb.d.

Отдельно ужесточён sentinel baseline-детекции: раньше «схема уже накачена»
проверялась одной таблицей listings (миграция 002, почти голова цепочки).
Если бы гонка готовности когда-нибудь вернулась, listings был бы уже
создан, а хвост цепочки — ещё нет, и baseline тихо пометил бы недостающие
миграции применёнными без прогона. Теперь проверяются оба конца — listings
(голова) и houses_geog_gist_idx, индекс из миграции 270 (хвост); при
несовпадении (ровно один конец на месте) деплой падает громко с explicit
ошибкой вместо угадывания.
2026-08-21 15:35:26 +03:00
4ae14055ec feat(tradein/v2): позиция квартиры на рынке — плашка на карточке рекомендованной цены (#2899) (#3024)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m33s
Deploy Trade-In / deploy (push) Successful in 1m46s
Deploy Trade-In / deploy-status (push) Successful in 2s
Deploy Trade-In / perimeter-smoke (push) Successful in 18s
2026-08-21 12:34:07 +00:00
f2945b7157 fix(ci): деплой падает громко, если :latest отстаёт от головы по компоненту (#2950) (#3023)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy Trade-In / changes (push) Successful in 15s
Deploy / deploy-caddy (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 57s
Deploy / build-backend (push) Successful in 1m2s
Deploy / build-frontend (push) Successful in 1m8s
Deploy / build-worker (push) Successful in 1m9s
Deploy / deploy (push) Successful in 1m58s
Deploy / deploy-status (push) Successful in 3s
Deploy Trade-In / build-frontend (push) Successful in 3m14s
Deploy / perimeter-smoke (push) Successful in 14s
Deploy Trade-In / test (push) Successful in 4m12s
Deploy Trade-In / build-backend (push) Successful in 33s
Deploy Trade-In / deploy (push) Successful in 2m16s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
2026-08-21 12:19:07 +00:00
cdf493f345 chore(format): нормализация под ruff 0.15.20 — 161 файл, только формат (#2864) (#3022)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy Trade-In / changes (push) Successful in 13s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy / build-backend (push) Successful in 2m23s
Deploy Trade-In / test (push) Successful in 3m56s
Deploy / build-worker (push) Successful in 4m16s
Deploy Trade-In / build-backend (push) Successful in 1m19s
Deploy / deploy (push) Successful in 1m49s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 12s
Deploy Trade-In / deploy (push) Successful in 2m25s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-08-21 12:01:52 +00:00
1782dae0e2 chore(tooling): pre-commit, pyproject и uv.lock — один ruff 0.15.20 (#2864) (#3021)
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy Trade-In / changes (push) Successful in 11s
Deploy / deploy-caddy (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy / build-backend (push) Successful in 3m57s
Deploy Trade-In / test (push) Successful in 4m5s
Deploy / build-worker (push) Successful in 4m59s
Deploy Trade-In / build-backend (push) Successful in 1m53s
Deploy / deploy (push) Successful in 1m58s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 11s
Deploy Trade-In / deploy (push) Successful in 2m27s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
2026-08-21 11:37:23 +00:00
95db3f44c8 fix(ops): бэкапы — +x на deploy-скриптах при деплое tradein, тихий s3 cp (#3005) (#3019)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy / build-backend (push) Has been skipped
Deploy / build-worker (push) Has been skipped
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-browser (push) Successful in 41s
Deploy / deploy (push) Successful in 1m38s
Deploy / deploy-status (push) Successful in 2s
Deploy / perimeter-smoke (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Successful in 2m38s
Deploy Trade-In / test (push) Successful in 3m47s
Deploy Trade-In / build-backend (push) Successful in 34s
Deploy Trade-In / deploy (push) Successful in 1m38s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
2026-08-21 10:35:10 +00:00
ccf84b4adc fix(best-layouts): avg_area_m2 = NULL вместо 0 при пустом окне сделок (#2867) (#3018)
Some checks failed
Deploy / perimeter-smoke (push) Blocked by required conditions
Deploy / deploy-status (push) Blocked by required conditions
Deploy / changes (push) Successful in 11s
Deploy / build-backend (push) Successful in 2m48s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-worker (push) Successful in 5m23s
Deploy / build-frontend (push) Successful in 4m18s
Deploy / deploy (push) Has been cancelled
2026-08-21 10:27:02 +00:00
fa32db299d feat(db): GiST по (geom::geography) на houses — матчинг идёт по индексу (#2997) (#3020)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m54s
Deploy Trade-In / build-backend (push) Successful in 43s
Deploy Trade-In / deploy (push) Successful in 2m35s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-08-21 10:24:35 +00:00
bb74a0e5e1 fix(tradein): неудача резолва yandex_jk_slug помечает дом — очередь не упирается в одни и те же пять (#2924)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m27s
Deploy Trade-In / build-backend (push) Successful in 1m9s
Deploy Trade-In / deploy (push) Successful in 2m28s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 8s
Закрывает #2924. Миграция 269 применится штатным деплоем.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 09:51:27 +00:00
bf119de7bb fix(db): миграция 269 — SET LOCAL lock_timeout внутри BEGIN/COMMIT (#2752)
All checks were successful
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 11s
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 4m24s
Вне транзакции SET LOCAL молча ничего не делает — гейт это ловит, и он
поймал меня второй раз подряд, теперь локально, до CI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:46:17 +05:00
8de9831e25 fix(db): миграция 269 — SET LOCAL lock_timeout перед ALTER TABLE houses (#2752)
Some checks failed
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Failing after 10s
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 4m27s
Гейт CI «блокирующий DDL без lock_timeout» покраснел по делу: ALTER TABLE
берёт ACCESS EXCLUSIVE на houses и при живом писателе ждал бы бесконечно,
копя очередь. 5с — миграция падает честно и деплой перезапускается.
Предыдущий пустой коммит «перезапуск — флуктуация раннера» был ошибкой
диагноза: я не дочитал лог changes до ::error и приписал падение
checkout'у. Единственный источник истины — строка ::error в логе самого
changes; «Job 'changes' failed» у зависимых джобов и «'runs-on' key not
defined» в act_runner v6.3.1 — шум.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:45:29 +05:00
ebb26b9382 ci: перезапуск — CI/changes упал на checkout (git clone: some refs were not updated) в 09:38 UTC, одновременно с деплоем tradein
Some checks failed
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Failing after 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 4m30s
Единственный реально упавший джоб — changes (задача 22320): лог обрывается на
'Setting up auth' checkout'а после 'Non-terminating error while running git clone:
some refs were not updated'. Три зависимых джоба помечены «Job 'changes' failed»
как пропущенные, не упавшие. Строка «'runs-on' key not defined in CI/changes»
в логах act_runner v6.3.1 стоит у десятков успешных задач за день — шум при
разборе needs, не причина. YAML головы PR байт-идентичен origin/main.
Код не меняется.
2026-08-21 14:43:58 +05:00
507c8e0611 fix(tradein): неудача резолва yandex_jk_slug помечает дом — очередь не упирается в одни и те же пять (#2924)
Some checks failed
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Failing after 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 4m25s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
(полное описание — в PR)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:38:22 +05:00
95d348f3c3 fix(tradein): апсерт не переписывает неизменившуюся строку — гейт IS DISTINCT FROM + МСК-день (#2992)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m26s
Deploy Trade-In / build-backend (push) Successful in 1m37s
Deploy Trade-In / deploy (push) Successful in 1m48s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
Часть #2992. Замер «после» — 22.08 ~09:00 UTC.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 09:08:05 +00:00
6cdf56820d fix(tradein): апсерт не переписывает неизменившуюся строку — гейт IS DISTINCT FROM + МСК-день (#2992)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 4m15s
(полное описание — в PR)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 13:57:09 +05:00
ce466c6d03 fix(data): партиции rosreestr_deals на Q2–Q4 2026 + загрузчик доходит до файла (#2998)
All checks were successful
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / changes (push) Successful in 8s
Deploy / build-backend (push) Successful in 38s
Deploy / build-worker (push) Successful in 39s
Deploy / deploy (push) Successful in 1m15s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
Закрывает корень #2998 (партиции rosreestr_deals). Данные Q2 уже на проде.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 08:56:25 +00:00
6b4800c81d feat(tradein/estimator): штраф за неизвестный год/тип дома — флагом, default OFF (#2936)
All checks were successful
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m25s
Deploy Trade-In / build-backend (push) Successful in 1m10s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / changes (push) Successful in 9s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / deploy (push) Successful in 1m59s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
Часть #2936. Флаг default OFF — поведение прода не меняется.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 08:36:25 +00:00
9b54d64bd8 test(#2998): сторож партиций — герметично в схеме-песочнице, а не по живому проду
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / 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 / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m59s
CI / backend-tests (pull_request) Successful in 17m6s
(см. описание в PR #3014)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 13:33:57 +05:00
865e64b783 feat(tradein/estimator): штраф за неизвестный год/тип дома — флагом, default OFF (#2936)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 4m14s
В SQL-формуле relevance_score кандидат без year_built получает штраф 0 —
столько же, сколько точное попадание в год, и лучше, чем кандидат с
известным годом, отличающимся на 24 (2.0). То же с house_type. Отсутствие
данных выигрывает у знания и возвышает источник с худшей полнотой.

Флаг estimate_unknown_attr_penalty_enabled (default OFF): кандидат с NULL
получает МЕДИАННЫЙ по пулу штраф того же признака среди тех, у кого он
известен — не наказание и не награда; считается по тем же термам, что
SQL (abs(Δyear)/12.0, 1.5 за несовпадение типа). Лежит в Python-слое
после SQL, рядом с kitchen/ceiling (#2012), по тому же контракту:
включать — только по бэктесту.

Без чего флаг был бы мёртв (и был в первой редакции): _ANALOG_SELECT_COLS
не выбирал year_built/house_type — SQL считал по ним CASE, но в словарь
кандидата колонки не попадали, Python-слой видел None у ВСЕХ и не штрафовал
никого по построению. Probe-лог в прод-оверлее: pool=30 null_year=30.
Добавлены в _ANALOG_SELECT_COLS, во внешние SELECT тиров H/W и во
внутренний base Tier W (он строится явным списком). Контроль: флаг OFF с
колонками и без — метрики бэктеста идентичны до сотых. На этот инвариант
стоит тест по исходнику запросов.

Живой A/B (бэктест в прод-контейнере, оверлей /tmp/ab, 300 сделок ЕКБ
Q2 2026, одна и та же выборка в обоих прогонах — проверено по deal_id):
  состав топ-50: сменилось 96 слотов из 5 915 (1.6 %), 27 сделок из 300
  источники: avito 55.7→55.3 %, cian 24.5→24.8 %, yandex 12.7→12.6 %
  цена: MAPE 16.70→16.70, bias −4.52→−4.52, coverage 84.46→84.46 —
  идентично до сотых по всем срезам (сегменты, комнатность).
Нижний Тагил (300 сделок, пулы 21/p90 40): 0 из 5 666 слотов сменилось.

Почему эффект в разы меньше замера задачи (×0.20 avito): тот замер шёл
по SQL тира H без стратификации. В боевом пути 54.9 % слотов топ-50 —
гарантированная квота MIN_ANALOGS_PER_SOURCE=5, раздаётся ДО сортировки
остатка; и у avito в топ-50 NULL-год лишь у 25.7 % (задача мерила 56 %
по всем активным объявлениям). Обе величины измерены по фикстурам A/B.

Что это значит: артефакт в формуле есть, флаг его корректно лечит, но на
итоговую цену он не влияет измеримо. Включать по умолчанию оснований
нет — и это и есть ответ, ради которого флаг заводился вместо правки.

pytest tradein-mvp/backend: test_2936 7 passed; гейт фикстуры и
roundtrip 8 passed; -k "estimat or analog" 735 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 13:27:18 +05:00
ebb7b7d32d ci: перезапуск backend-tests — джоб завис с 07:49 без обновлений (#2998)
Some checks failed
CI Trade-In / changes (pull_request) Successful in 8s
CI / 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 / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m6s
CI / backend-tests (pull_request) Failing after 17m16s
Остальные семь чеков прошли за минуты, backend-tests стоял 1ч30м без
единого обновления статуса. Actions API на этом Forgejo недоступен
(/actions/tasks обрывает соединение), перезапуск — только новым
коммитом. Код не меняется.
2026-08-21 13:04:35 +05:00
e6e5bd962c fix(data): партиции rosreestr_deals на Q2–Q4 2026 + загрузчик доходит до файла (#2998)
Some checks failed
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m55s
CI / backend-tests (pull_request) Failing after 17m20s
Задача формулировала «импорт каждый день рапортует done с total_seen=0».
Проверка на проде показала другое: импорт исправен — ежедневно вычитывает
все 96 974 строки FDW-источника и честно их пропускает (rows_fetched =
rows_skipped = 96974), а `total_seen` — поле админ-витрины, не счётчик
импорта. Источник gendesign.rosreestr_deals стоял на Q1 2026 (загружен
30.04), хотя Q2 2026 опубликован Росреестром 10.07 и poll заметил его
14.08 (available=1). Оба сторожа — poll и deals_freshness_monitor —
сработали и семь событий ушли в GlitchTip, где 0 правил / 0 адресатов /
0 отправок.

Корень, которого в задаче не было: rosreestr_deals партиционирована по
period_start_date, партиции созданы списком в 01_schema «2024 Q3 — 2026
Q1», и ничто новые не создаёт. Загрузка Q2 21.08 упала:

  ERROR: no partition of relation "rosreestr_deals" found for row
  DETAIL: (period_start_date) = (2026-04-01)

То есть даже оператор, запустив загрузчик по подсказке poll, получил бы
отказ. Это и объясняет, почему poll сделан «только сообщить».

Что сделано:
• миграция 193 — партиции Q2, Q3, Q4 2026 с запасом, идемпотентно, с
  lock_timeout; индексы наследуются от родителя (проверено: 4 на 2026q2);
• JOBS загрузчика — 2026Q2–Q4 (квартал без CSV честно SKIP);
• ловушка set -e в загрузчике: resolve_csv сигналит «файла нет» кодом 1,
  и первый же квартал без CSV молча ронял ВЕСЬ прогон до строки SKIP — на
  проде с одним Q2-файлом скрипт завершался rc=0, не напечатав ни строки.
  `|| true` на вызове; после правки боевой прогон на VPS: 12 кварталов,
  2026Q2 «уже загружен (741874 строк)», остальные SKIP, rc=0;
• тест-сторож горизонта: партиция обязана существовать на последний
  публикуемый квартал (+20 дней лага после конца квартала; Q2 2026 вышел
  10.07) и на следующий — чтобы предупреждение приходило за квартал до
  отказа, а не в день публикации. Читает pg_inherits живого Postgres.
  Красная сторона воспроизводима на проде, где миграция уже применена:
  DETACH партиции в откатываемой транзакции → головная краснеет по
  значению («нет партиции на квартал 2026-04-01»), откат возвращает
  партицию (проверено: 12 партиций после теста). Без БД — skip с
  причиной, в allowlist; календарный тест идёт везде.

Сам Q2 загружен на прод по штатному пути: 741 874 строки в
rosreestr_deals (ЕКБ-фильтр 13 654), import-rosreestr.sh → tradein.deals
+11 649 сделок, max(deal_date) 2026-01-01 → 2026-04-01.
deals_freshness_monitor на следующем тике: alert 0, latest_quarter 2.

pytest backend/tests/sql — 55 passed (через туннель к проду).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 12:48:28 +05:00
a003fdd37c feat(tradein): ручка «дома, уехавшие от собственных объявлений» (#2996)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m27s
Deploy Trade-In / build-backend (push) Successful in 1m4s
Deploy Trade-In / deploy (push) Successful in 2m10s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 10s
Часть Находки 3 из #2996.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 05:54:33 +00:00
63bc576c40 Merge pull request 'fix(ops): проверка трейлера дампа падала на grep — ведущие -- принимались за опции (#2203)' (#3013) from fix/backup-trailer-grep-dashdash into main
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy Trade-In / changes (push) Successful in 11s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 41s
Deploy / build-worker (push) Successful in 42s
Deploy Trade-In / build-browser (push) Successful in 39s
Deploy / deploy (push) Successful in 1m56s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
Deploy Trade-In / build-frontend (push) Successful in 2m51s
Deploy Trade-In / test (push) Successful in 3m45s
Deploy Trade-In / build-backend (push) Successful in 33s
Deploy Trade-In / deploy (push) Successful in 6m31s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
2026-08-20 20:57:31 +00:00
1da1ba084d Merge pull request 'chore(db): Postgres tradein уходит со стоковых настроек — конфиг в compose' (#3012) from chore/2991-postgres-config into main
Some checks failed
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
Deploy Trade-In / build-browser (push) Successful in 40s
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Successful in 2m31s
Deploy Trade-In / test (push) Has been cancelled
2026-08-20 20:53:54 +00:00
bot-backend
70f3c0a88a fix(ops): проверка трейлера дампа падала на grep — ведущие -- принимались за опции (#2203)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / 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 / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m59s
CI / backend-tests (pull_request) Successful in 17m7s
verify_dump_integrity() в ops/backup.sh и tradein-mvp/deploy/backup-tradein-db.sh
делала `grep -qF "$trailer"`, где $trailer = "-- PostgreSQL database dump
complete". Ведущие -- в аргументе grep трактует как конец опций/флаг, без
разделителя команда падает: `grep: unrecognized option '-- PostgreSQL...'`.

Проверка из-за этого ВСЕГДА возвращала "трейлера нет" — не потому что дамп
оборван, а потому что сама проверка не могла выполниться. Вызывающий код
удалял только что созданный валидный дамп и завершался с ошибкой; ретеншен
не успевал отработать (ранний exit) — свежие бэкапы не создавались никогда,
старые копии оставались молча.

Воспроизведено вручную на проде: bash
/opt/gendesign/tradein-mvp/deploy/backup-tradein-db.sh удалил свежий дамп с
сообщением "дамп оборван?".

Фикс: `grep -qF -- "$trailer"` — `--` явно завершает список опций grep.

Регрессионный тест (backend/tests/ops/test_2203_backup_trailer_grep_dashdash.py)
исполняет РЕАЛЬНУЮ verify_dump_integrity() из обоих скриптов на настоящем
gzip-потоке через gunzip|tail|grep — не читает исходник текстом. Проверено
локально: падает на добаговой версии с тем же "unrecognized option", зелен
на исправленной.
2026-08-20 23:38:45 +03:00
bot-backend
d4692a6ff1 chore(db): Postgres tradein уходит со стоковых настроек — конфиг в compose
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 16s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
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
Блокер переезда (#2991). Контейнер шёл на ПОЛНОСТЬЮ стоковой конфигурации:
ни command:, ни смонтированного postgresql.conf. Замер прода 2026-08-20 —
169 млрд blks_read за 91,75 сут ≈ 175 МБ/с мимо кеша при shared_buffers 128 МБ.

Поправка к диагнозу из issue: 288 чекпойнтов в сутки — это ровно 24ч/288 =
5 минут, то есть дефолтный checkpoint_timeout, а НЕ max_wal_size. При WAL
7 ГБ/сут лимит в 1 ГБ дал бы 7 чекпойнтов, а не 288. Поэтому поднят именно
timeout до 30min — правка одного max_wal_size не изменила бы ничего.
Реже чекпойнты → реже full-page images, а они сейчас 55-86% объёма WAL.

Значения подобраны под ТЕКУЩИЙ сервер и не выходят за mem_limit=3g:
shared_buffers 768MB (6× от стоковых), work_mem 16MB, maintenance_work_mem
256MB (autovacuum по listings идёт 193 раза в сутки).

max_wal_size 4GB, а не 8GB: на диске 38 ГБ свободно, а pg_wal растёт до этого
значения. При timeout=30min между чекпойнтами копится ~0,15 ГБ — запас большой.

wal_compression=zstd — самая дешёвая победа при такой доле FPI.
random_page_cost 1.1 вместо стоковых 4: диск NVMe, а 4 — настройка под HDD,
из-за неё планировщик недооценивал индексные сканы.

pg_stat_statements подключён через shared_preload_libraries — расширение было
в образе, но не активировано, и прошлый разбор пришлось вести по косвенным
признакам.

shm_size 512m: дефолтные 64 МБ /dev/shm ронял параллельные воркеры на тяжёлых
PostGIS-сортировках.

mem_limit оставлен 3g. В комментарии зафиксировано, что при переезде его надо
поднимать ДО правки shared_buffers: лимит контейнера срабатывает раньше
postgresql.conf, и shared_buffers=16GB при mem_limit=3g даёт OOM на старте.

Refs #2991, #2989
2026-08-20 23:34:40 +03:00
bot-backend
efc965a257 fix(db/ci): чистый старт БД больше не падает на 077 и не может уехать на пустой схеме
Some checks failed
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Failing after 55s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Successful in 1m3s
CI Trade-In / frontend-checks (pull_request) Successful in 1m47s
CI / openapi-codegen-check (pull_request) Successful in 2m26s
CI / backend-tests (pull_request) Successful in 17m51s
Блокер переезда (#2990). Чистый старт на пустом томе падал: 077 читает foreign
table gendesign_rosreestr_deals, а USER MAPPING создаёт бэкенд при старте
(app/core/fdw.py), то есть ПОСЛЕ docker-entrypoint-initdb.d. Контейнер не
поднимался вообще. Путь «пустой том» на реальном железе не исполнялся ни разу,
а CI этот файл явно пропускал — гейт, который должен был поймать, был ослаблен.

Проверено по всем 14 миграциям, упоминающим FDW-таблицы: читает ровно одна —
077. Остальные только CREATE/DROP FOREIGN TABLE и COMMENT, им ни USER MAPPING,
ни связь с чужой БД не нужны.

077 не удалена, а сделана самозащитной: гард считает строки в md5-форме и
выходит раньше обращения к FDW, если мигрировать нечего. На чистой БД таких
строк нет по определению. Удаление файла было бы неверным — прод помнит
миграции по bare-filename в _schema_migrations, и
test_applied_migration_is_not_renamed_or_deleted падает на удалении.

Исключение в ci-tradein.yml снято: теперь цепочка применяется целиком, то есть
CI сам стал репетицией чистого старта.

Отдельно закрыт тихий отказ в deploy-tradein.yml. Ветка baseline срабатывала по
одному лишь отсутствию _schema_migrations, а это состояние неоднозначно: так
выглядит и наполненный прод до внедрения tracking, и пустая БД нового сервера.
Во втором случае baseline пометил бы все миграции применёнными, ни одной не
прогнав, и деплой уехал бы зелёным на пустой схеме. Добавлен sentinel по
listings: пусто → baseline пропускается, цепочка применяется с нуля.

Refs #2990, #2989
2026-08-20 23:31:46 +03:00