Skip to content

Analysis and Querying

Once a computation has been executed (or events have been streamed), PyRapide provides a rich set of query functions that operate on the causal graph. These functions answer questions that timestamp-ordered logs fundamentally cannot.

💡 Tip
All query functions accept a Computation object. You can obtain one from Engine.run(), StreamProcessor.computation(), or deserialize_computation().

critical_path

Returns the longest causal chain in the computation. This is the sequence of events that determines the minimum end-to-end latency.

critical_path.py python
from pyrapide import critical_path

path = critical_path(computation)
for event in path:
    print(f"{event.name} at {event.timestamp}")

root_causes

Given a target event, walks backward through the causal graph to find the originating events that have no causal predecessors.

root_causes.py python
from pyrapide import root_causes

causes = root_causes(computation, target_event)
# Returns all events with no causal predecessors
# that are ancestors of target_event

impact_set

Given a source event, returns every event that is causally downstream, revealing the full blast radius.

impact_set.py python
from pyrapide import impact_set

affected = impact_set(computation, source_event)
print(f"{len(affected)} events affected by {source_event.name}")

causal_distance

Returns the length of the shortest causal path between two events. If the events are not causally related, returns None.

causal_distance.py python
from pyrapide import causal_distance

dist = causal_distance(computation, event_a, event_b)
if dist is not None:
    print(f"Events are {dist} causal steps apart")
else:
    print("Events are causally independent")

common_ancestors

Given two events, finds all events that are causal ancestors of both. Useful for identifying shared root causes of seemingly independent failures.

common_ancestors.py python
from pyrapide import common_ancestors

shared = common_ancestors(computation, event_a, event_b)
for ancestor in shared:
    print(f"Shared ancestor: {ancestor.name}")

backward_slice

Returns the transitive closure of all causal predecessors of a given event: everything that contributed to it.

backward_slice.py python
from pyrapide import backward_slice

# Everything that led to this failure
predecessors = backward_slice(computation, failure_event)
print(f"{len(predecessors)} events in the backward slice")

forward_slice

Returns the transitive closure of all causal successors of a given event: everything that it affected.

forward_slice.py python
from pyrapide import forward_slice

# Everything affected by this configuration change
successors = forward_slice(computation, config_change_event)
print(f"{len(successors)} events in the forward slice")
i Note
backward_slice and forward_slice are the causal equivalents of program slicing in software analysis. They answer "what contributed to X?" and "what did X affect?" respectively.

parallel_events

Returns all events that are causally independent of a given event. These are events that happened concurrently with no causal relationship.

parallel_events.py python
from pyrapide import parallel_events

concurrent = parallel_events(computation, event)
print(f"{len(concurrent)} events concurrent with {event.name}")

bottleneck_events

Identifies events that appear on a disproportionate number of causal paths. These are the points where many independent chains converge, and where failures have the largest blast radius.

bottleneck_events.py python
from pyrapide import bottleneck_events

bottlenecks = bottleneck_events(computation, threshold=0.5)
for event, score in bottlenecks:
    print(f"{event.name}: appears on {score:.0%} of causal paths")

event_frequency

Counts the number of times each event type appears in the computation. Useful for profiling and identifying hot paths.

event_frequency.py python
from pyrapide import event_frequency

freq = event_frequency(computation)
for event_type, count in freq.most_common(10):
    print(f"{event_type}: {count} occurrences")

causal_density

Returns a ratio of causal edges to total possible edges. A high density means events are tightly coupled; a low density means the system has high concurrency and loose coupling.

causal_density.py python
from pyrapide import causal_density

density = causal_density(computation)
print(f"Causal density: {density:.3f}")
# 0.0 = fully concurrent (no causal links)
# 1.0 = fully sequential (total order)
âš  Warning
A causal density approaching 1.0 in a system that should be concurrent may indicate an architectural bottleneck. Use bottleneck_events to identify the specific chokepoints.

Next Steps