89 lines
2.4 KiB
Python
89 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Repeatedly kill blockers holding DROP INDEX CONCURRENTLY for stalled index."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import time
|
|
from pathlib import Path
|
|
|
|
import psycopg2
|
|
|
|
|
|
ENV_FILE = Path("/etc/default/domaincheck-worker")
|
|
INDEX_NAME = "idx_detect_job_items_stalled_job_activity"
|
|
POLL_SECONDS = 3
|
|
MAX_LOOPS = 1200
|
|
|
|
|
|
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 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:
|
|
for loop in range(MAX_LOOPS):
|
|
cur.execute(
|
|
"""
|
|
SELECT pid
|
|
FROM pg_stat_activity
|
|
WHERE pid <> pg_backend_pid()
|
|
AND state <> 'idle'
|
|
AND query ILIKE %s
|
|
ORDER BY pid
|
|
""",
|
|
(f"%DROP INDEX CONCURRENTLY IF EXISTS {INDEX_NAME}%",),
|
|
)
|
|
drop_pids = [r[0] for r in cur.fetchall()]
|
|
if not drop_pids:
|
|
log("no_drop_pid")
|
|
break
|
|
|
|
blockers: list[int] = []
|
|
for pid in drop_pids:
|
|
cur.execute("SELECT pg_blocking_pids(%s)", (pid,))
|
|
row = cur.fetchone()
|
|
if row and row[0]:
|
|
blockers.extend(int(v) for v in row[0])
|
|
|
|
blockers = sorted(set(blockers))
|
|
log(f"loop={loop} drop_pids={drop_pids} blockers={blockers}")
|
|
if not blockers:
|
|
time.sleep(POLL_SECONDS)
|
|
continue
|
|
|
|
for pid in blockers:
|
|
cur.execute("SELECT pg_cancel_backend(%s), pg_terminate_backend(%s)", (pid, pid))
|
|
log(f"kill pid={pid} result={cur.fetchone()}")
|
|
time.sleep(POLL_SECONDS)
|
|
finally:
|
|
cur.close()
|
|
conn.close()
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|