///|
/// Summary metrics for a causal trace.
pub(all) struct TraceSummary {
event_count : Int
direct_edge_count : Int
concurrent_pair_count : Int
layer_count : Int
maximum_width : Int
critical_path_length : Int
} derive(Eq, Debug)
///|
/// A path through causally ordered events.
pub(all) struct CausalPath {
events : Array[TraceEvent]
} derive(Debug)
///|
/// Query facade for navigating a validated TraceAnalysis.
pub(all) struct TraceQuery {
analysis : TraceAnalysis
} derive(Debug)
///|
/// Create queries from an already validated analysis.
pub fn TraceQuery::new(analysis : TraceAnalysis) -> TraceQuery {
{ analysis, }
}
///|
/// All strict causal ancestors of one event.
pub fn TraceQuery::ancestors(
self : TraceQuery,
sequence : Int,
) -> Array[TraceEvent] {
let output : Array[TraceEvent] = []
for candidate in self.analysis.events() {
match self.analysis.relation(candidate.sequence, sequence) {
Some(Before) => output.push(candidate)
_ => ()
}
}
output
}
///|
/// All strict causal descendants of one event.
pub fn TraceQuery::descendants(
self : TraceQuery,
sequence : Int,
) -> Array[TraceEvent] {
let output : Array[TraceEvent] = []
for candidate in self.analysis.events() {
match self.analysis.relation(sequence, candidate.sequence) {
Some(Before) => output.push(candidate)
_ => ()
}
}
output
}
///|
/// Events concurrent with one selected event.
pub fn TraceQuery::concurrent_with(
self : TraceQuery,
sequence : Int,
) -> Array[TraceEvent] {
let output : Array[TraceEvent] = []
for candidate in self.analysis.events() {
match self.analysis.relation(sequence, candidate.sequence) {
Some(Concurrent) => output.push(candidate)
_ => ()
}
}
output
}
///|
/// Events produced by one replica, preserving trace order.
pub fn TraceQuery::by_replica(
self : TraceQuery,
replica : String,
) -> Array[TraceEvent] {
let output : Array[TraceEvent] = []
for event in self.analysis.events() {
if event.replica == replica {
output.push(event)
}
}
output
}
///|
/// Events whose HLC physical component lies in an inclusive interval.
pub fn TraceQuery::between_physical_times(
self : TraceQuery,
from_time : Int,
to_time : Int,
) -> Array[TraceEvent] {
let output : Array[TraceEvent] = []
if to_time < from_time {
return output
}
for event in self.analysis.events() {
if event.stamp.physical >= from_time && event.stamp.physical <= to_time {
output.push(event)
}
}
output
}
///| Smallest useful causal slice containing an event, its ancestors, and its
///|
/// descendants. Concurrent events outside that cone are omitted.
pub fn TraceQuery::causal_slice(
self : TraceQuery,
sequence : Int,
) -> Array[TraceEvent] {
let output : Array[TraceEvent] = []
for event in self.analysis.events() {
match self.analysis.relation(event.sequence, sequence) {
Some(Before) | Some(After) | Some(Equal) => output.push(event)
_ => ()
}
}
output
}
///|
/// Longest direct-edge path ending at a selected event.
pub fn TraceQuery::longest_path_to(
self : TraceQuery,
sequence : Int,
) -> CausalPath {
let mut best : Array[TraceEvent] = []
for edge in self.analysis.direct_predecessors(sequence) {
let candidate = self.longest_path_to(edge.earlier.sequence).events
if candidate.length() > best.length() {
best = candidate
}
}
for event in self.analysis.events() {
if event.sequence == sequence {
best.push(event)
break
}
}
{ events: best }
}
///|
/// Longest causal path anywhere in the trace.
pub fn TraceQuery::critical_path(self : TraceQuery) -> CausalPath {
let mut best : Array[TraceEvent] = []
for event in self.analysis.events() {
let candidate = self.longest_path_to(event.sequence).events
if candidate.length() > best.length() {
best = candidate
}
}
{ events: best }
}
///|
/// Events with no causal predecessors.
pub fn TraceQuery::roots(self : TraceQuery) -> Array[TraceEvent] {
let output : Array[TraceEvent] = []
for event in self.analysis.events() {
if self.analysis.direct_predecessors(event.sequence).length() == 0 {
output.push(event)
}
}
output
}
///|
/// Events with no causal successors.
pub fn TraceQuery::leaves(self : TraceQuery) -> Array[TraceEvent] {
let output : Array[TraceEvent] = []
for event in self.analysis.events() {
if self.descendants(event.sequence).length() == 0 {
output.push(event)
}
}
output
}
///|
/// Aggregate metrics used by diagnostics and visualizers.
pub fn TraceQuery::summary(self : TraceQuery) -> TraceSummary {
let layers = self.analysis.layers()
let mut maximum_width = 0
for layer in layers {
if layer.events.length() > maximum_width {
maximum_width = layer.events.length()
}
}
{
event_count: self.analysis.events().length(),
direct_edge_count: self.analysis.direct_edges().length(),
concurrent_pair_count: self.analysis.concurrent_pairs().length(),
layer_count: layers.length(),
maximum_width,
critical_path_length: self.critical_path().events.length(),
}
}