NAME

Punk::Observe::Map - laying out the service graph

SYNOPSIS

use Punk::Observe::Map;

my $m = Punk::Observe::Map::layout([
    { caller => '*', callee => 1, count => 600, errors => 0 },
    { caller => 1,   callee => 2, count => 600, errors => 4 },
]);

for my $n (@{ $m->{nodes} }) {
    printf "service %s at layer %d slot %d\n",
        $n->{service}, $n->{layer}, $n->{slot};
}

DESCRIPTION

Turns the accumulated service graph into positions: a layer per node and a slot within it. Callers sit left of callees, so traffic reads in one direction.

The graph itself is built when a segment is sealed rather than per query. See "The service graph is accumulated at seal" in Punk::Observe::Trace.

Cycles are drawn, not refused

Real service graphs have cycles - two services that call each other, or a retry path that loops back. A layering algorithm needs an acyclic graph, so the edges that would close a cycle are identified and reported as back edges rather than being dropped.

They are still edges and still drawn. Removing them would hide exactly the relationship somebody is looking at the map to find.

FUNCTIONS

layout

my $m = Punk::Observe::Map::layout(\@edges);

Lays out a graph. Each edge is a hashref taking caller, callee, count and errors. Services are symbol numbers; the string * as a caller means the synthetic root, which is where traffic from something uninstrumented arrives.

Edges repeating the same pair are summed rather than duplicated.

{
  nodes  => [ { service, layer, slot, in, out, errors }, ... ],
  layers => 3,
  back_edges => 1,
  back   => [ 2 ],
}

service is the symbol number, or * for the synthetic root. layer is the horizontal position and slot the vertical one within it. in and out are call counts through the node, and errors the errors attributed to it.

back holds indexes into the edge list as the layout saw it, which is the deduplicated graph rather than the arrayref that was passed in.

SEE ALSO

Punk::Observe, Punk::Observe::Trace, Punk::Observe::SVG