#!/usr/bin/env python3 """Validate the Go shadow ingest (dereth_go) against production (dereth). Run on the server. The shadow tracker replays Python's /ws/live firehose into its own dereth_go DB. Absolute counts differ (shadow started fresh; char_stats / rare_stats accumulate deltas from connect time), so we validate the paths whose writes are FULL upserts/inserts and therefore exactly comparable: * character_stats: a full-payload upsert. For a character whose row has the SAME timestamp in both DBs, stats_data must be byte-identical. * /live online set: telemetry end-to-end (compared separately by the caller). """ import json import subprocess SEP = "\x1f" def q(container, db, sql): out = subprocess.check_output( ["docker", "exec", container, "psql", "-U", "postgres", "-d", db, "-tA", "-F", SEP, "-c", sql], text=True) return [line.split(SEP) for line in out.splitlines() if line.strip()] def main(): print("=== dereth_go ingested row counts ===") counts = q("dereth-go-db", "dereth_go", """ SELECT 'telemetry_events', count(*)::text FROM telemetry_events UNION ALL SELECT 'telemetry_distinct_chars', count(distinct character_name)::text FROM telemetry_events UNION ALL SELECT 'character_stats', count(*)::text FROM character_stats UNION ALL SELECT 'char_stats', count(*)::text FROM char_stats UNION ALL SELECT 'rare_events', count(*)::text FROM rare_events UNION ALL SELECT 'rare_stats', count(*)::text FROM rare_stats UNION ALL SELECT 'portals', count(*)::text FROM portals """) for k, v in counts: print(f" {k:26} {v}") print("\n=== character_stats exact match (same-timestamp rows) ===") prod = {r[0]: (r[1], r[2]) for r in q("dereth-db", "dereth", "SELECT character_name, timestamp::text, stats_data::text FROM character_stats")} shadow = q("dereth-go-db", "dereth_go", "SELECT character_name, timestamp::text, stats_data::text FROM character_stats") match = mismatch = newer = 0 for name, ts, sd in shadow: if name not in prod: continue pts, psd = prod[name] if ts != pts: newer += 1 # one side got a newer character_stats message; not comparable continue if json.loads(sd) == json.loads(psd): match += 1 else: mismatch += 1 print(f" MISMATCH {name}") print(f" exact match={match} mismatch={mismatch} skipped(diff timestamp)={newer}") print("\nRESULT:", "ingest OK" if mismatch == 0 else f"{mismatch} character_stats mismatch(es)") return 1 if mismatch else 0 if __name__ == "__main__": raise SystemExit(main())