122 lines
3.3 KiB
Python
122 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Drop and recreate idx_detect_job_items_release_node_job on mainland."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import time
|
|
from pathlib import Path
|
|
|
|
import psycopg2
|
|
from psycopg2 import sql
|
|
|
|
|
|
ENV_FILE = Path("/etc/default/domaincheck-worker")
|
|
INDEX_NAME = "idx_detect_job_items_release_node_job"
|
|
|
|
|
|
def load_env(path: Path) -> None:
|
|
if not path.exists():
|
|
return
|
|
for raw in path.read_text(encoding="utf-8").splitlines():
|
|
line = raw.strip()
|
|
if not line or line.startswith("#") or "=" not in line:
|
|
continue
|
|
key, value = line.split("=", 1)
|
|
os.environ[key] = value
|
|
|
|
|
|
def log(message: str) -> None:
|
|
stamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
|
print(f"[{stamp}] {message}", flush=True)
|
|
|
|
|
|
def fetch_index_state(cur: psycopg2.extensions.cursor, index_name: str) -> tuple[bool, bool, bool] | None:
|
|
cur.execute(
|
|
"""
|
|
SELECT i.indisvalid, i.indisready, i.indislive
|
|
FROM pg_index i
|
|
JOIN pg_class c ON c.oid = i.indexrelid
|
|
WHERE c.relname = %s
|
|
""",
|
|
(index_name,),
|
|
)
|
|
row = cur.fetchone()
|
|
if not row:
|
|
return None
|
|
return bool(row[0]), bool(row[1]), bool(row[2])
|
|
|
|
|
|
def fetch_conflicting_backends(cur: psycopg2.extensions.cursor) -> list[tuple[int, str, str]]:
|
|
cur.execute(
|
|
"""
|
|
SELECT pid, query, coalesce(wait_event_type, '')
|
|
FROM pg_stat_activity
|
|
WHERE state <> 'idle'
|
|
AND (
|
|
query ILIKE %s OR
|
|
query ILIKE %s
|
|
)
|
|
ORDER BY backend_start
|
|
""",
|
|
(
|
|
"%idx_detect_job_items_release_node_job%",
|
|
"%idx_detect_job_items_stalled_job_activity%",
|
|
),
|
|
)
|
|
rows = []
|
|
for pid, query, wait_event_type in cur.fetchall():
|
|
snippet = " ".join((query or "").split())[:160]
|
|
rows.append((pid, wait_event_type, snippet))
|
|
return rows
|
|
|
|
|
|
def main() -> int:
|
|
load_env(ENV_FILE)
|
|
conn = psycopg2.connect(
|
|
host="127.0.0.1",
|
|
port=5432,
|
|
user=os.getenv("DB_USER", "postgres"),
|
|
password=os.getenv("DB_PASSWORD"),
|
|
dbname="domain",
|
|
)
|
|
conn.autocommit = True
|
|
cur = conn.cursor()
|
|
try:
|
|
conflicts = fetch_conflicting_backends(cur)
|
|
if conflicts:
|
|
log(f"preflight conflicting_backends={conflicts}")
|
|
|
|
log("drop start")
|
|
cur.execute(sql.SQL("DROP INDEX CONCURRENTLY IF EXISTS {}").format(sql.Identifier(INDEX_NAME)))
|
|
log("drop done")
|
|
|
|
residual_state = fetch_index_state(cur, INDEX_NAME)
|
|
if residual_state is not None:
|
|
raise RuntimeError(
|
|
f"index shell still present after drop: name={INDEX_NAME} "
|
|
f"state={residual_state}"
|
|
)
|
|
|
|
log("create start")
|
|
cur.execute(
|
|
sql.SQL(
|
|
"""
|
|
CREATE INDEX CONCURRENTLY {}
|
|
ON public.detect_job_items (claimed_by, job_id, status, id)
|
|
WHERE claimed_by <> ''
|
|
AND status IN ('claimed', 'running')
|
|
"""
|
|
).format(sql.Identifier(INDEX_NAME))
|
|
)
|
|
log("create done")
|
|
log(f"final state={fetch_index_state(cur, INDEX_NAME)}")
|
|
finally:
|
|
cur.close()
|
|
conn.close()
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|