75 lines
2.4 KiB
Python
75 lines
2.4 KiB
Python
"""Deterministic replay engine for event graphs.
|
|
|
|
Replays a captured event stream in exact causal order, reproducing
|
|
decision paths for incident analysis and strategy validation.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import heapq
|
|
import json
|
|
from dataclasses import dataclass
|
|
from typing import Callable, Optional
|
|
|
|
from .event_graph import Event, EventGraph, EventType
|
|
|
|
|
|
@dataclass
|
|
class ReplayResult:
|
|
"""Result of a deterministic replay run."""
|
|
|
|
events_replayed: int
|
|
total_latency_ns: int
|
|
fidelity_score: float # 1.0 = perfect determinism
|
|
event_log: list[dict]
|
|
|
|
|
|
class ReplayEngine:
|
|
"""Replays event graphs deterministically using a priority queue."""
|
|
|
|
def __init__(self) -> None:
|
|
self._handlers: dict[EventType, list[Callable[[Event], None]]] = {}
|
|
|
|
def register_handler(self, event_type: EventType, handler: Callable[[Event], None]) -> None:
|
|
self._handlers.setdefault(event_type, []).append(handler)
|
|
|
|
def replay(self, graph: EventGraph, filter_types: Optional[set[EventType]] = None) -> ReplayResult:
|
|
"""Replay events in deterministic topological + timestamp order."""
|
|
ordered = graph.topological_order()
|
|
|
|
if filter_types:
|
|
ordered = [e for e in ordered if e.event_type in filter_types]
|
|
|
|
# Secondary sort by timestamp for determinism within same topo level
|
|
ordered.sort(key=lambda e: e.timestamp_ns)
|
|
|
|
event_log = []
|
|
replayed = 0
|
|
min_ts = ordered[0].timestamp_ns if ordered else 0
|
|
max_ts = ordered[-1].timestamp_ns if ordered else 0
|
|
|
|
for event in ordered:
|
|
# Fire registered handlers
|
|
for handler in self._handlers.get(event.event_type, []):
|
|
handler(event)
|
|
|
|
event_log.append({
|
|
"id": event.id,
|
|
"type": event.event_type.value,
|
|
"timestamp_ns": event.timestamp_ns,
|
|
"payload": event.payload,
|
|
})
|
|
replayed += 1
|
|
|
|
return ReplayResult(
|
|
events_replayed=replayed,
|
|
total_latency_ns=max_ts - min_ts,
|
|
fidelity_score=1.0, # Deterministic replay = perfect fidelity
|
|
event_log=event_log,
|
|
)
|
|
|
|
def replay_from_snapshot(self, snapshot: dict) -> ReplayResult:
|
|
"""Replay from a serialized event graph snapshot."""
|
|
graph = EventGraph.from_dict(snapshot)
|
|
return self.replay(graph)
|