"""Проверка состояния миграции 028_matching_tables.sql на прод tradein-postgres.""" from __future__ import annotations import os import psycopg dsn = os.environ["DATABASE_URL"].replace("postgresql+psycopg://", "postgresql://") conn = psycopg.connect(dsn) cur = conn.cursor() EXPECTED_COLUMNS = [ ("houses", "address_fingerprint"), ("listings", "canonical"), ("listings", "merged_into"), ] EXPECTED_INDEXES = [ "houses_addr_fp_idx", "listings_merged_into_idx", "house_sources_house_idx", "house_sources_low_conf_idx", "listing_sources_listing_idx", "listing_sources_low_conf_idx", "listing_sources_scraped_idx", "haa_house_idx", "haa_fingerprint_idx", ] EXPECTED_TABLES = ["house_sources", "listing_sources", "house_address_aliases"] print("=== Columns ===") for tbl, col in EXPECTED_COLUMNS: cur.execute( "SELECT 1 FROM information_schema.columns WHERE table_name=%s AND column_name=%s", (tbl, col), ) status = "✓" if cur.fetchone() else "✗ MISSING" print(f" {tbl}.{col:<25} {status}") print("\n=== Tables ===") for tbl in EXPECTED_TABLES: cur.execute( "SELECT 1 FROM information_schema.tables WHERE table_name=%s AND table_schema='public'", (tbl,), ) status = "✓" if cur.fetchone() else "✗ MISSING" print(f" {tbl:<30} {status}") print("\n=== Indexes ===") for idx in EXPECTED_INDEXES: cur.execute( "SELECT 1 FROM pg_indexes WHERE indexname=%s", (idx,), ) status = "✓" if cur.fetchone() else "✗ MISSING" print(f" {idx:<35} {status}") # Проверим есть ли активные транзакции/locks от deploy которые ещё висят print("\n=== Active transactions (locks on relevant tables) ===") cur.execute( """ SELECT pid, usename, application_name, state, wait_event_type, wait_event, query_start, substring(query, 1, 80) AS query_head FROM pg_stat_activity WHERE state IS DISTINCT FROM 'idle' AND pid != pg_backend_pid() ORDER BY query_start NULLS LAST """ ) rows = cur.fetchall() if not rows: print(" (none — clean)") else: for r in rows: print(f" pid={r[0]} user={r[1]} app={r[2]} state={r[3]} wait={r[4]}/{r[5]} started={r[6]}") print(f" query: {r[7]!r}")