644f564c30
Unreplicated, the site-x survivor came up empty (by design: no LocalDb peer); replicated, kill and freeze each lost only the 26-30 s takeover window, every buffered message was delivered once, and the 4 duplicate ticks came from the frozen victim's 6 s zombie window. Central kill: 0 gap. Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
31 lines
1.7 KiB
Python
31 lines
1.7 KiB
Python
"""analyze.py <received.jsonl> <from_epoch> <to_epoch> [ipA ipB]
|
|
Tick accounting for the env2 S&F soak: one SoakTick per 2 s tick (tick = unix/2).
|
|
Reports rows, unique ticks, duplicates, missing tick runs (with UTC times) and, per
|
|
sender node, how many rows arrived late (= were buffered: at - tick*2 > 4 s)."""
|
|
import json, sys, datetime, collections
|
|
path, t0, t1 = sys.argv[1], float(sys.argv[2]), float(sys.argv[3])
|
|
ips = {}
|
|
if len(sys.argv) > 5: ips = {sys.argv[4]: "site-x-a", sys.argv[5]: "site-x-b"}
|
|
rows = [json.loads(l) for l in open(path) if l.strip()]
|
|
rows = [r for r in rows if isinstance(r.get("body"), dict) and t0 <= r["body"]["tick"] * 2 <= t1]
|
|
c = collections.Counter(r["body"]["tick"] for r in rows)
|
|
ts = lambda t: datetime.datetime.utcfromtimestamp(t * 2).strftime("%H:%M:%S")
|
|
print(f"window {ts(t0/2)}..{ts(t1/2)} UTC: rows={len(rows)} unique_ticks={len(c)} duplicates={sum(v-1 for v in c.values())}")
|
|
dups = sorted(t for t, v in c.items() if v > 1)
|
|
if dups: print(" duplicate ticks:", ", ".join(f"{ts(t)}x{c[t]}" for t in dups[:20]))
|
|
lo, hi = int(t0 // 2) + 1, int(t1 // 2)
|
|
missing = [t for t in range(lo, hi) if t not in c]
|
|
runs, cur = [], []
|
|
for t in missing:
|
|
if cur and t != cur[-1] + 1: runs.append(cur); cur = []
|
|
cur.append(t)
|
|
if cur: runs.append(cur)
|
|
for r in runs: print(f" gap: {ts(r[0])}..{ts(r[-1])} ({len(r)} ticks = {len(r)*2} s)")
|
|
by = collections.defaultdict(lambda: [0, 0, 0.0])
|
|
for r in rows:
|
|
n = ips.get(r["client"], r["client"]); late = r["at"] - r["body"]["tick"] * 2
|
|
by[n][0] += 1
|
|
if late > 4: by[n][1] += 1; by[n][2] = max(by[n][2], late)
|
|
for n, (k, l, mx) in sorted(by.items()):
|
|
print(f" sender {n}: rows={k} delivered_late(>4s, i.e. buffered)={l} max_delay={mx:.0f}s")
|