feat(sf): cross-load ETL tradein→gendesign newbuilding_listings — вариант A (#976) #1185
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#1185
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/sf-newbuilding-crossload"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
docker-compose.prod.yml—backendиworkerподключены кgendesign_shared(networks: [default, shared]);tradein-postgresуже там.tradein-mvp/backend/data/sql/101_gendesign_reader_role.sql— идемпотентная read-only рольgendesign_reader(REVOKE-периметр, SELECT только на houses/house_sources/houses_price_dynamics/house_reliability_checks/house_reviews, пароля в SQL нет).deploy-tradein.yml+ новыйtradein-mvp/ops/db-bootstrap/set_gendesign_reader_password.sql— паттернset_config()/format('%L')через stdin (НЕpsql -c :'var'— тот самый задокументированный инцидент 2026-05-24; pre-push review поймал и это исправлено fixup-коммитом). ПустойTRADEIN_READER_PASSWORD→ warning, не падение.backend/app/services/etl/newbuilding_crossload.py— psycopg v3, батчи + SAVEPOINT, upsertON CONFLICT (source, ext_house_id) DO UPDATE, COALESCE-protect дляyandex_jk_id/cian_internal_house_id; маппинг 21 колонки houses→newbuilding_listings (commission_year/cadastral_numberв tradein отсутствуют → NULL, задокументировано). Celery task + beat 00:30 UTC (03:30 МСК), admin endpoint (503 при пустом url), disabled-режим без кредов.Pre-push review: 🔴 psql-интерполяция → исправлено (
04da30b); minorcommission_year— источника в tradein нет, follow-up при появлении.Test plan
TRADEIN_READER_PASSWORDв tradein.env.runtime+TRADEIN_DATABASE_URL=postgresql://gendesign_reader:***@tradein-postgres:5432/tradeinв gendesignbackend/.env.runtime→ admin-trigger ETL → счётчики source/target совпадают⚠️ Миграция 101 авто-применится на прод при деплое (строгий exit-1) — она только CREATE ROLE/GRANT, ревью на идемпотентность пройдено.
Refs #976
- docker-compose.prod.yml: backend + worker добавлены в сеть gendesign_shared (нужен прямой TCP к tradein-postgres для psycopg ETL) - tradein-mvp/data/sql/101_gendesign_reader_role.sql: роль gendesign_reader (LOGIN, SELECT на houses/house_sources/houses_price_dynamics/ house_reliability_checks/house_reviews; пароль через TRADEIN_READER_PASSWORD bootstrap в deploy-tradein.yml) - .forgejo/workflows/deploy-tradein.yml: bootstrap ALTER ROLE gendesign_reader PASSWORD из env после миграций; warn (не exit 1) если переменная не задана - backend/app/core/config.py: settings.tradein_database_url (env TRADEIN_DATABASE_URL) - backend/app/services/etl/newbuilding_crossload.py: run_crossload() — psycopg server-side cursor, batch 500, UPSERT ON CONFLICT (source, ext_house_id), SAVEPOINT per-row; disabled-режим если url пуст - backend/app/workers/tasks/etl_newbuilding_crossload.py: Celery task - backend/app/workers/beat_schedule.py: nightly 03:30 МСК (00:30 UTC) - backend/app/api/v1/admin_scrape.py: POST /admin/scrape/newbuilding-crossload - backend/tests/services/test_newbuilding_crossload.py: 10 тестов без реальной БД needs-human: TRADEIN_READER_PASSWORD → tradein .env.runtime; TRADEIN_DATABASE_URL → gendesign backend/.env.runtime- Создан tradein-mvp/ops/db-bootstrap/set_gendesign_reader_password.sql: set_config GUC + format('%L') + \o /dev/null — зеркальный паттерн ops/db-bootstrap/set_tradein_fdw_password.sql. Пароль не попадает в stdout/logs (leak-protected). - deploy-tradein.yml: шаг bootstrap переписан на stdin-redirect (< ops/db-bootstrap/set_gendesign_reader_password.sql) вместо сломанного -c "ALTER ROLE ... PASSWORD :'TRADEIN_READER_PASSWORD'", которое давало syntax error at ':' (psql не интерполирует :'var' внутри -c строки). - newbuilding_crossload.py ON CONFLICT DO UPDATE: yandex_jk_id и cian_internal_house_id → COALESCE(EXCLUDED.col, table.col), чтобы cian-запись не затирала yandex_jk_id NULL'ом и наоборот. - Тест test_upsert_sql_coalesce_external_ids проверяет наличие COALESCE в DO UPDATE секции для обоих id.