### **User description**
## Problem
Fixesopenobserve/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>
</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>
</td>
</tr>
</table></td></tr></tr></tbody></table>
</details>
___
### **User description**
## Summary
Adds DateTime picker to service graph allowing users to select custom
time ranges for visualization. Also rename the tab from `Service Maps`
to `Service Graphs`.
## Changes
- Add DateTime component to ServiceGraph.vue
- Update service graph service to pass start_time/end_time parameters
- Backend API already supports custom time range queries
- Rename tab name and address to service graphs (from service maps)
Backend changes:
- Remove server_address from peer attribute candidates to reduce cardinality
- Use only peer_service, db_name, and db_system for peer identification
- Skip streams without peer attributes instead of using fallback
- Filter out empty client/server names in SQL queries
- Remove 'internal' fallback for INTERNAL spans
Frontend changes:
- Add P50/P95/P99 latency metrics to edge tooltips
- Add latency-based color coding (errors priority, then latency thresholds)
- Display formatted latency values (auto-converts ns to ms/s)
- Preserve stream dropdown options when switching between streams
## Summary
- Removes `http_url` from peer attribute candidates to fix massive node
cardinality
- Fixes API to respect configured time range instead of hardcoded 60
minutes
- Fixes tree view to display all nodes including disconnected components
- Changes default visualization to graph view
## Problems Fixed
### 1. Node Explosion from URL Variations
Service graph was creating separate nodes for every URL variation with
different query parameters or path parameters:
- `/api/products/1YMWWN1N4O`, `/api/products/9SIQT8TOJO`, etc.
- `?contextKeys=books`, `?contextKeys=travel`, etc.
This caused hundreds of nodes instead of a clean service topology.
### 2. API Ignoring Configuration
API hardcoded 60-minute query window, ignoring the configured
`service_graph.query_time_range_minutes` setting.
### 3. Tree View Missing Nodes
Tree view only showed nodes reachable from root nodes (no incoming
edges), hiding services in cycles or disconnected components.
## Solutions
### 1. Use server_address Instead of http_url
Now uses `server_address` attribute (hostname only) following
OpenTelemetry HTTP semantic conventions for service identification.
### 2. Respect Configured Time Range
API now uses same configured time window as processor for consistency.
### 3. Virtual Root for Tree View
Creates invisible virtual root node to display all disconnected service
trees in ECharts.
### 4. Better Default UX
Graph view is now the default with reordered buttons (Graph View first).
- Check schema for peer attributes before querying
- COALESCE priority: peer_service > server_address > db_name > db_system > http_url
- Fallback to 'internal' if no peer attributes
- Support INTERNAL spans with exclude config
- Improved UI empty state with troubleshooting
Backend:
- Add `stream_name` parameter to service graph query API for multi-stream filtering
- Pass `stream_name` through `handle_otlp_request` to service graph tracking
- Filter Prometheus metrics by both `org_id` and optional `stream_name`
- Update API documentation with stream filtering examples
Frontend:
- Add tree view and graph view visualization modes with toggle
- Implement tree layouts: horizontal, vertical, and radial
- Implement graph layouts: force-directed and circular
- Add stream selector dropdown to filter metrics by trace stream
- Add auto-refresh toggle with configurable interval (5-300 seconds)
- Refactor graph rendering to use `ChartRenderer` component with ECharts
- Implement D3-force physics simulation for force-directed layout
- Add `convertServiceGraphToTree` and `convertServiceGraphToNetwork` utilities
- Preserve node positions during graph updates to prevent re-layout jitter
- Use incremental updates (`notMerge: false`) for stable graph view
- Store user preferences (visualization type, layout, stream filter) in localStorage
- Update `ChartRenderer` to respect `notMerge` and `lazyUpdate` flags from props
Dependencies:
- Add `d3-force` for force-directed graph layout calculations
- Add `@types/d3-force` for TypeScript support
- Add `d3-quadtree` for spatial indexing in force simulation
Service graph automatically discovers service topology from distributed traces.
OSS includes:
- API routing endpoints (gated with enterprise feature flag)
- UI components (functional only with enterprise backend)
- Configuration options
Enterprise includes:
- 256-shard concurrent edge store with LRU eviction
- Span matching and processing logic
- Prometheus metrics and background workers