You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[FEATURE] Generate graphs in canvas from query data - #891
Add a data-driven mode to the canvas panel that derives nodes and edges live from query results instead of a hand-drawn graph, addressing the need to visualize dynamic topologies (e.g. frequently changing service or host relationships) where manual authoring is not feasible.
Infer graph structure from series labels: one node per distinct node-id label value, edges from configurable source/target labels (endpoints not among the generated nodes are dropped).
Lay out generated graphs with a deterministic, dependency-free force simulation so a given topology always settles to the same positions; nodes only move when nodes/edges are added or removed, not when metric values change.
Drive visuals from each series' last value: node color via panel thresholds, optional node-size scaling via per-threshold multipliers, edge color via dedicated edge thresholds (falling back to node thresholds), edge thickness via edge-thickness thresholds, and label interpolation for node/edge captions.
Add a Manual / From data mode toggle to the settings editor with the shared styling controls plus the new graph-generation and node-size panels.
Switch the read-only panel to view-mode zoom/pan (plain wheel zoom, left/middle drag pan) and clip edges to node perimeters so lines and arrowheads touch the shape boundary.
Keep every representation coherent: TypeScript runtime/editor, CUE schema, Go SDK builders, docs, and tests.
Screenshots
Checklist
Pull request has a descriptive title and context useful to a reviewer.
Pull request title follows the [<catalog_entry>] <commit message> naming convention using one of the
following catalog_entry values: FEATURE, ENHANCEMENT, BUGFIX, BREAKINGCHANGE, DOC,IGNORE.
Add a data-driven mode to the canvas panel that derives nodes and edges
live from query results instead of a hand-drawn graph, addressing the
need to visualize dynamic topologies (e.g. frequently changing service
or host relationships) where manual authoring is not feasible.
Closesperses/perses#4634
- Infer graph structure from series labels: one node per distinct node-id
label value, edges from configurable source/target labels (endpoints
not among the generated nodes are dropped).
- Lay out generated graphs with a deterministic, dependency-free force
simulation so a given topology always settles to the same positions;
nodes only move when nodes/edges are added or removed, not when metric
values change.
- Drive visuals from each series' last value: node color via panel
thresholds, optional node-size scaling via per-threshold multipliers,
edge color via dedicated edge thresholds (falling back to node
thresholds), edge thickness via edge-thickness thresholds, and label
interpolation for node/edge captions.
- Add a Manual / From data mode toggle to the settings editor with the
shared styling controls plus the new graph-generation and node-size
panels.
- Switch the read-only panel to view-mode zoom/pan (plain wheel zoom,
left/middle drag pan) and clip edges to node perimeters so lines and
arrowheads touch the shape boundary.
- Keep every representation coherent: TypeScript runtime/editor, CUE
schema, Go SDK builders, docs, and tests.
Signed-off-by: Philipp Matthes <p.matthes@sap.com>
To the reviewers: since this feature needs metrics in a specific format (source/target labels for edges), which the Perses dev stack for metrics doesn't provide, we added a small demo exporter for it. To test the plugin you can check out the README.
It's basically an nginx serving a static file of metrics, which can be added as a scrape target to the dev Prometheus. We couldn't figure out whether there's a "correct" way to provide demo data in a special format. If there is one, please let us know and we'll switch to it. :)
The demo dashboard is then available under http://localhost:8080/projects/perses/dashboards/canvasgraphdemo?var-job=tempo&start=5m&refresh=5s
- Bound the force simulation with a cooling schedule so high-degree hub
graphs stay viewable instead of diverging to extreme coordinates.
- Cache the layout by topology key so the simulation reruns only when the
node/edge set changes, not on every value refresh or viewport resize.
- Show the node-size controls only in From data mode, where generated nodes
actually opt into threshold-based sizing.
- Base edge-thickness breakpoints on the edge thresholds (falling back to
node thresholds), matching edge color resolution.
- Give per-threshold size multiplier inputs accessible names.
- Prune node-size multipliers whose threshold no longer exists so they
cannot linger as active-but-hidden scales.
- Collapse clipped edges to a point when nodes overlap instead of reversing
the line, and skip rendering the collapsed edge.
- Encode generated edge ids unambiguously so endpoint pairs sharing a
separator-joined form no longer collide.
- Apply the edge label template to both directions of a bidirectional edge.
Signed-off-by: Philipp Matthes <p.matthes@sap.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Topology-key collisions and stale cached node specs can reuse incorrect layouts and ignore appearance edits, while threshold changes leave hidden edge-width settings active.
- Cache only node positions by id (not whole node specs) so appearance
edits like default size and label template take effect on refresh without
rerunning the force simulation.
- Reconcile edgeThresholdWidths with the effective edge thresholds (edge
thresholds, or node thresholds as fallback) so a width cannot linger as an
active-but-hidden step after its breakpoint changes.
- Build the topology key from JSON-serialized sorted id arrays so it is
unambiguous (no separator collision) and independent of series order.
Signed-off-by: Philipp Matthes <p.matthes@sap.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Unresolved issues allow query ordering to shift node layouts, reciprocal edges and labels to overlap, and arrowheads on short segments to intrude into target nodes.
No open issues 🎉
3 previously missed issues
In code that hasn't changed since last review
Prevent arrowheads from exceeding clipped edge segments
Clipping can leave a segment shorter than its arrowhead: two 48px nodes 60px apart leave a 12px gap, but an 8px stroke produces a 20px arrowhead at 1× zoom. EdgeLines.shortenEnd leaves such short segments unchanged, so the marker extends into the target node. After clipping, shrink the marker or suppress edges whose directional segments cannot fit their arrowheads, accounting for zoom.
Prevent overlap for reciprocal edge paths
canvas/src/utils/graphGeneration.ts:114
If the edge query returns both A→B and B→A, the generated edges share the same path, so their colors and widths overpaint each other and their value labels overlap near the midpoint. This obscures directional metrics in reciprocal connections. Merge these rows into one bidirectional edge with separate series for each direction, or render them along separate paths.
Canonicalize simulation inputs before layout
canvas/src/utils/graphLayout.ts:58
The force simulation still depends on incoming node/edge order, so reopening the same topology with reordered query results can visibly move nodes; sorting only the final centroid does not prevent this. Canonicalize both inputs before the simulation while preserving the returned node order, and add a larger reordered-topology regression test.
- Canonicalize the force-simulation node and edge order (id-sorted) so a
topology laid out again with reordered query results settles to identical
positions; map back to input order on return. Add a larger shuffled-input
regression test.
- Merge reciprocal A->B / B->A edge rows into a single bidirectional edge
carrying a per-direction series, so the two directions no longer overpaint
one path or overlap their labels. Plumb a reverse-series map through to the
edge layer.
- Shrink an edge's arrowhead to fit when a node-clipped directional segment
is shorter than the marker, so arrowheads no longer intrude into the target
node; accounts for zoom via the already-scaled stroke width.
- Fix checkdocs: write the localhost dev-stack URLs in the example README as
inline code instead of autolinks so mdox does not try to reach them.
Signed-off-by: Philipp Matthes <p.matthes@sap.com>
The reason will be displayed to describe this comment to others. Learn more.
The example is inspired by a generic networking monitoring use case. Take a look at the Grafana docs, they have it similar, albeit with a more bogus example.
The scope of this feature is to allow users to generate the graph from metrics. However I don't see anything that would block us from supporting other datasources.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Reciprocal query-row reordering changes the edge ID and topologyKey despite unchanged topology, causing CanvasPanel to unnecessarily rerun all 300 force iterations.
Derive a generated edge's id from the canonical unordered pair key rather
than the first-seen orientation. Reordering reciprocal A->B / B->A query
rows no longer flips the id or the topology key, so CanvasPanel does not
rerun the force simulation on an order-only refresh. Orientation is still
carried by the entry's source/target for the directional series.
Signed-off-by: Philipp Matthes <p.matthes@sap.com>
Hi @PhilippMatthes, thanks for the PR! I've started reviewing it, and I have a few thoughts on its scope:
Zoom control changes: I noticed the PR also changes the zoom control. I'm not sure that fits the scope of this PR. Could we discuss whether we need this change in a separate issue?
New edge settings: The new edge settings might be easier to add in their own PR. Right now the legend doesn't show these settings yet. Moving them to a follow-up PR would keep this one smaller and let us handle the legend there.
Splitting things up this way should make the review faster and keep this PR focused. What do you think?
@adrianSepiol you are right, it'll make it much easier to review when we split this into multiple parts and keep the scope of this PR minimal. @SoWieMarkus will work on this and get back to you.
Keep this branch focused on automatic graph generation. Remove the edge
color scheme (edgeThresholds), node-size scaling (nodeThresholdSizes,
sizeMode scaling), and the view-mode zoom interaction change, which move
to follow-up PRs stacked on top of this one.
Generation plumbing is retained, including the no-op sizeMode:'threshold'
on generated nodes and nodeDefaultSize.
Signed-off-by: Markus Wieland <markus.wieland@sap.com>
The reason will be displayed to describe this comment to others. Learn more.
Not sure if it is just me but "From data" is not intuitive name for what it does
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Add a data-driven mode to the canvas panel that derives nodes and edges live from query results instead of a hand-drawn graph, addressing the need to visualize dynamic topologies (e.g. frequently changing service or host relationships) where manual authoring is not feasible.
Closes perses/perses#4634
Screenshots
Checklist
[<catalog_entry>] <commit message>naming convention using one of thefollowing
catalog_entryvalues:FEATURE,ENHANCEMENT,BUGFIX,BREAKINGCHANGE,DOC,IGNORE.UI Changes