diff --git a/src/service/alerts/incidents.rs b/src/service/alerts/incidents.rs index b0d3c18db6..4c5537a6d9 100644 --- a/src/service/alerts/incidents.rs +++ b/src/service/alerts/incidents.rs @@ -729,6 +729,7 @@ pub async fn enrich_with_topology( let current_node_first_fired = topology.nodes[current_node_index].first_fired_at; // Find the most recent previous node from same service + // Use node index as tiebreaker when timestamps are equal (handles simultaneous alerts) let most_recent_previous = topology .nodes .iter() @@ -736,9 +737,11 @@ pub async fn enrich_with_topology( .filter(|(idx, node)| { *idx != current_node_index && node.service_name == service_name - && node.first_fired_at < current_node_first_fired + && (node.first_fired_at < current_node_first_fired + || (node.first_fired_at == current_node_first_fired + && *idx < current_node_index)) }) - .max_by_key(|(_, node)| node.first_fired_at); + .max_by_key(|(idx, node)| (node.first_fired_at, *idx)); if let Some((prev_idx, _)) = most_recent_previous { // Check if edge already exists @@ -814,7 +817,11 @@ pub async fn enrich_with_topology( let to_node = &topology.nodes[to_idx]; // Only add if from happened before to (chronological) - if from_node.first_fired_at < to_node.first_fired_at { + // Use node index as tiebreaker when timestamps are equal (handles simultaneous + // alerts) + if from_node.first_fired_at < to_node.first_fired_at + || (from_node.first_fired_at == to_node.first_fired_at && from_idx < to_idx) + { // Check if edge already exists let edge_exists = topology.edges.iter().any(|e| { e.from_node_index == from_idx @@ -843,17 +850,22 @@ pub async fn enrich_with_topology( let from_node = &topology.nodes[from_idx]; // Find most recent node in to_service that fired after from_node + // Use node index as tiebreaker when timestamps are equal (handles simultaneous + // alerts) let most_recent_to = to_indices .iter() .filter_map(|&to_idx| { let to_node = &topology.nodes[to_idx]; - if to_node.first_fired_at > from_node.first_fired_at { + if to_node.first_fired_at > from_node.first_fired_at + || (to_node.first_fired_at == from_node.first_fired_at + && to_idx > from_idx) + { Some((to_idx, to_node.first_fired_at)) } else { None } }) - .min_by_key(|(_, fired_at)| *fired_at); // Closest in time (earliest after from_node) + .min_by_key(|(to_idx, fired_at)| (*fired_at, *to_idx)); // Closest in time, then by index if let Some((to_idx, _)) = most_recent_to { // Check if edge already exists diff --git a/web/src/plugins/traces/ServiceGraph.vue b/web/src/plugins/traces/ServiceGraph.vue index b3d7038f7e..3391947d4a 100644 --- a/web/src/plugins/traces/ServiceGraph.vue +++ b/web/src/plugins/traces/ServiceGraph.vue @@ -88,6 +88,7 @@ ]" dense no-caps + class="view-toggle-buttons" @update:model-value="setVisualizationType" /> @@ -144,58 +145,9 @@
No Service Graph Data
-
- Possible causes: +
+ Try querying a longer duration
-
-
-
- Queried time range has no traces, try selecting a longer duration. -
-
- -
-
- Maybe your traces have only internal spans (span_kind = 1)? -
-
- Make sure O2_SERVICE_GRAPH_EXCLUDE_INTERNAL_SPANS=true -
-
to process internal spans for service edges.
-
-
-
@@ -804,4 +756,24 @@ code { font-weight: 600; color: #333; } + +.view-toggle-buttons { + :deep(.q-btn) { + padding-left: 1rem; + padding-right: 1rem; + } + + :deep(.q-btn-item) { + padding-left: 1rem; + padding-right: 1rem; + } + + :deep(.q-btn__content) { + gap: 0.5rem; + } + + :deep(.q-separator) { + margin: 0.25rem 0; + } +}