#!/usr/bin/env python3 """Refresh the part_vehicle_preview materialized view. Uses REFRESH MATERIALIZED VIEW CONCURRENTLY so reads are not blocked. Requires the unique index idx_pvp_part to exist. Usage: python3 refresh_part_vehicle_preview.py python3 refresh_part_vehicle_preview.py --dsn "postgresql://..." Recommended cron (as postgres user or via systemd timer): 0 3 * * * /usr/bin/python3 /home/Autopartes/scripts/refresh_part_vehicle_preview.py >> /var/log/nexus-pos/mv_refresh.log 2>&1 """ import argparse import os import subprocess import sys import time from datetime import datetime import psycopg2 DEFAULT_DSN = os.environ.get('MASTER_DB_URL', 'postgresql://postgres@/nexus_autoparts') def log(msg): print(f"[{datetime.now().isoformat(timespec='seconds')}] {msg}", flush=True) def _connect(dsn): return psycopg2.connect(dsn) def _ensure_connection(dsn): try: return _connect(dsn) except psycopg2.OperationalError as exc: err = str(exc).lower() if 'peer' in err or 'authentication' in err: if os.geteuid() == 0: log("ERROR: PostgreSQL peer authentication failed.") log(" Run as postgres OS user: sudo -u postgres python3 " + __file__) sys.exit(1) log("Peer auth failed. Re-running with sudo -u postgres ...") cmd = ['sudo', '-u', 'postgres', sys.executable, __file__] env = os.environ.copy() env['MASTER_DB_URL'] = dsn for i, arg in enumerate(sys.argv[1:], start=1): if arg in ('--dsn', '-d') and i < len(sys.argv) - 1: env['MASTER_DB_URL'] = sys.argv[i + 1] ret = subprocess.call(cmd, env=env) sys.exit(ret) raise def main(): parser = argparse.ArgumentParser(description='Refresh part_vehicle_preview MV') parser.add_argument('--dsn', '-d', default=DEFAULT_DSN, help='PostgreSQL DSN') args = parser.parse_args() log("Starting REFRESH MATERIALIZED VIEW CONCURRENTLY part_vehicle_preview ...") conn = _ensure_connection(args.dsn) conn.autocommit = True cur = conn.cursor() start = time.time() try: cur.execute("SET statement_timeout = 0;") cur.execute("REFRESH MATERIALIZED VIEW CONCURRENTLY part_vehicle_preview;") elapsed = time.time() - start log(f"Refresh completed in {elapsed:.1f}s") except psycopg2.Error as exc: log(f"ERROR: {exc}") sys.exit(1) finally: cur.close() conn.close() if __name__ == '__main__': main()