fix: alert graph edges not rendering due to timestamp equality (#10347)

### **User description**
## Problem

Fixes openobserve/o2-enterprise#1229

Alert graphs were not rendering edges (arrows) when nodes had identical
timestamps, causing the topology graph to show nodes without
connections.

## Root Cause

The edge creation logic used strict timestamp inequality (`<`, `>`),
which failed when alerts fired at the exact same microsecond. This
commonly occurs with:
- High-frequency alerting
- Bulk alert ingestion
- Simulated/test data with fixed timestamps
- Clock resolution limitations

## Solution

Added timestamp tiebreaker logic using node index (insertion order) for
all three edge creation scenarios:

1. **Same-service temporal edges**: Connect to most recent previous
node, use index as tiebreaker
2. **Cross-service temporal edges**: Connect to chronologically closest
node, use index as tiebreaker
3. **Service dependency edges**: Connect related services, use index as
tiebreaker

## Changes

- `src/service/alerts/incidents.rs`: Updated 3 edge creation locations
with timestamp + index comparisons

## Impact

- ✅ Edges now created even when alerts have identical timestamps
- ✅ Deterministic behavior based on insertion order
- ✅ Graph visualization shows proper alert flow with arrows
- ✅ Root cause analysis now possible

## Testing

- Verified code compiles successfully
- Manual testing recommended with alerts at same timestamp


___

### **PR Type**
Bug fix


___

### **Description**
- Add tiebreaker for equal timestamps

- Ensure deterministic topology edge creation

- Fix missing incident graph arrows


___

### Diagram Walkthrough


```mermaid
flowchart LR
  A["New alert node added"] --> B["Find candidate prior/next nodes"]
  B --> C["Compare (first_fired_at, node_index)"]
  C --> D["Create temporal/dependency edges deterministically"]
```



<details> <summary><h3> File Walkthrough</h3></summary>

<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Bug
fix</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>incidents.rs</strong><dd><code>Deterministic edge
creation for equal timestamps</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

src/service/alerts/incidents.rs

<ul><li>Add index-based tiebreaker for same-service edges<br> <li> Allow
dependency edges when timestamps equal<br> <li> Refine cross-service
temporal matching with index<br> <li> Update ordering keys to include
node index</ul>


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10347/files#diff-2411d921fd8b8783c049deea11da06726ed986550d405477d486234892353ca9">+17/-5</a>&nbsp;
&nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

</details>

___
This commit is contained in:
ByteBaker
2026-02-03 20:39:52 +05:30
committed by GitHub
parent d7c0874084
commit 4c080dfd5f
2 changed files with 40 additions and 56 deletions
+23 -51
View File
@@ -88,6 +88,7 @@
]"
dense
no-caps
class="view-toggle-buttons"
@update:model-value="setVisualizationType"
/>
@@ -144,58 +145,9 @@
<div class="text-h6 q-mt-md text-grey-7">
No Service Graph Data
</div>
<div
class="text-h6 text-grey-7 q-mt-lg q-mb-md"
style="font-size: 1.1rem"
>
Possible causes:
<div class="text-body2 text-grey-6 q-mt-sm">
Try querying a longer duration
</div>
<div
class="text-body1 text-grey-6"
style="max-width: 800px; font-size: 0.95rem"
>
<div
class="q-pa-md q-mb-md"
style="
background: rgba(var(--q-primary-rgb), 0.05);
border-radius: 6px;
"
>
<div
class="text-weight-medium q-mb-sm"
style="font-size: 1rem"
>
Queried time range has no traces, try selecting a longer duration.
</div>
</div>
<div
class="q-pa-md q-mb-md"
style="
background: rgba(var(--q-primary-rgb), 0.05);
border-radius: 6px;
"
>
<div
class="text-weight-medium q-mb-sm"
style="font-size: 1rem"
>
Maybe your traces have only internal spans (<code>span_kind = 1</code>)?
</div>
<div class="q-mt-sm">
Make sure <code>O2_SERVICE_GRAPH_EXCLUDE_INTERNAL_SPANS=true</code>
</div>
<div>to process internal spans for service edges.</div>
</div>
</div>
<q-btn
outline
color="primary"
label="Refresh"
icon="refresh"
@click="loadServiceGraph"
class="q-mt-lg"
/>
</div>
</div>
<div v-else class="tw:h-full">
@@ -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;
}
}
</style>