Feature Specification: Automatic Trace Topology Ingestion & Dynamic DAG Building
1. Overview
This feature enables NeuralBudget to automatically ingest distributed trace data from OpenTelemetry (and compatible observability systems) to dynamically construct and maintain Directed Acyclic Graphs (DAGs) representing service topologies and call flows. This eliminates manual service dependency definition and creates living, self-updating visualizations of system architecture.
2. Problem Statement
Current workflow limitations:
- DAG definitions are static and require manual updates when service architecture changes
- Teams must manually maintain service dependency mappings
- New services or changed communication patterns require code updates
- No automatic detection of service relationships in distributed systems
- Difficult to keep documentation in sync with actual runtime topology
3. Goals
- Automatic Discovery: Extract service topology directly from distributed trace telemetry
- Real-time Updates: Reflect architectural changes without manual intervention
- OpenTelemetry Native: Leverage OpenTelemetry standards for maximum compatibility
- Low Overhead: Minimal performance impact on trace processing
- Visualization: Auto-generate and update service dependency graphs
- Historical Tracking: Maintain topology versioning and change history
4. Scope
In Scope
- OpenTelemetry trace ingestion (OTLP protocol)
- Service and operation discovery from spans
- Call relationship mapping (parent-child span relationships)
- Dynamic DAG construction and updates
- Topology versioning and change detection
- REST APIs for topology queries
- Basic visualization export (JSON, GraphML)
Out of Scope
- Metrics/logs ingestion (trace-only in Phase 1)
- Real-time visualization UI (API-first, UI integration separate)
- Custom trace filtering rules (standard OpenTelemetry semantics only)
- Cost optimization for trace sampling
5. Technical Architecture
5.1 Core Components
┌─────────────────────────────────────────────────────────┐
│ OpenTelemetry Collector │
│ (OTLP Receiver) │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Trace Ingestion Service │
│ - Validate & normalize traces │
│ - Extract span metadata │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Topology Discovery Engine │
│ - Build service graph from spans │
│ - Detect operation flows │
│ - Identify communication patterns │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ DAG Builder & Manager │
│ - Construct/update DAGs │
│ - Detect topology changes │
│ - Version management │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Topology Store (Database) │
│ - Current topology state │
│ - Historical versions │
│ - Change audit log │
└─────────────────────────────────────────────────────────┘
5.2 Data Flow
- Trace Reception: OpenTelemetry traces arrive via OTLP (gRPC/HTTP)
- Normalization: Extract key attributes (service.name, span.kind, attributes)
- Relationship Detection: Identify parent-child relationships and RPC calls
- Service Mapping: Group spans by service and build adjacency matrix
- DAG Construction: Create graph nodes (services) and edges (calls)
- Change Detection: Compare against previous topology state
- Storage & Versioning: Persist changes with timestamps and metadata
- Notification: Emit events for topology changes
6. Data Model
6.1 Key Span Attributes (OpenTelemetry Semantic Conventions)
{
"service.name": "payment-service",
"service.version": "1.2.3",
"span.kind": "CLIENT|SERVER|PRODUCER|CONSUMER|INTERNAL",
"span.name": "PaymentService.processPayment",
"rpc.service": "payment.v1.PaymentService",
"rpc.method": "ProcessPayment",
"http.method": "POST",
"http.target": "/api/v1/payments",
"peer.service": "database-service",
"messaging.system": "kafka",
"messaging.destination": "payment-events"
}
6.2 Service Node
interface ServiceNode {
id: string; // Unique service identifier
name: string; // service.name
version: string; // service.version (nullable)
kind: 'SERVICE' | 'DATABASE' | 'QUEUE' | 'EXTERNAL';
firstSeen: ISO8601DateTime;
lastSeen: ISO8601DateTime;
metadata: {
environment?: string;
region?: string;
language?: string;
[key: string]: any;
};
operations: string[]; // List of span.name values
}
6.3 Edge (Call Relationship)
interface ServiceEdge {
id: string; // Unique edge identifier
sourceId: string; // caller service
targetId: string; // callee service
protocol: 'HTTP' | 'GRPC' | 'AMQP' | 'KAFKA' | 'DATABASE' | 'UNKNOWN';
callCount: number; // Cumulative call count
errorCount: number; // Failed calls in window
latencyP50: number; // ms
latencyP99: number; // ms
firstSeen: ISO8601DateTime;
lastSeen: ISO8601DateTime;
operations: string[]; // RPC/HTTP operations
attributes: {
[key: string]: any;
};
}
6.4 DAG Version
interface DAGVersion {
id: string; // UUID
version: number; // Incremental counter
timestamp: ISO8601DateTime;
nodes: ServiceNode[];
edges: ServiceEdge[];
changeMetadata: {
nodesAdded: string[]; // Node IDs
nodesRemoved: string[];
edgesAdded: string[];
edgesRemoved: string[];
nodesModified: string[]; // version/metadata changes
reason: string; // "topology_discovery" | "trace_analysis"
};
hash: string; // SHA256 of topology
}
7. Functional Requirements
FR1: Trace Ingestion
- Accept OpenTelemetry traces via OTLP (gRPC + HTTP)
- Validate trace format and schema
- Handle batch and streaming ingestion
- Support configurable retention window (default: 7 days)
FR2: Service Discovery
- Extract service.name from trace attributes
- Identify service kind (service vs external dependency)
- Track service versions when available
- Detect first/last seen timestamps
FR3: Call Relationship Detection
- Identify parent-child span relationships
- Classify communication protocol (HTTP, gRPC, DB, Messaging)
- Extract operation names and endpoints
- Calculate call frequency and error rates
- Measure latency percentiles (p50, p99)
FR4: DAG Construction
- Build service graph from discovered relationships
- Ensure DAG remains acyclic (cycle detection)
- Merge redundant relationships
- Support multiple versions of DAG (historical)
FR5: Change Detection & Notification
- Compare current topology against previous state
- Classify changes (additions, removals, modifications)
- Generate audit trail with reasons
- Emit events for topology changes (webhook, queue)
FR6: Query & Export APIs
- List all services in current topology
- Get service details with operations
- Query edges between services
- Export DAG in multiple formats (JSON, GraphML, DOT)
- Historical topology queries
- Change log queries
FR7: Aggregation Window
- Configure time window for topology analysis (default: 1 hour)
- Aggregate metrics within window (call counts, latencies)
- Generate new DAG version periodically or on-demand
8. Non-Functional Requirements
NFR1: Performance
- Ingest 100k+ spans/second with <100ms latency
- Topology update <5 seconds for most changes
- Query API response time <200ms (p99)
NFR2: Reliability
- 99.9% availability for ingestion pipeline
- Graceful degradation (buffer overflow handling)
- Automatic retry for failed trace processing
NFR3: Scalability
- Horizontal scaling of trace processors
- Database sharding support for large deployments
- Efficient memory usage for large topologies (1000+ services)
NFR4: Data Quality
- Deduplicate spans with matching IDs
- Validate semantic convention compliance
- Configurable leniency for malformed traces
NFR5: Storage
- Compress historical DAG versions
- Configurable TTL for version history (default: 30 days)
- Efficient storage <10MB per DAG snapshot
9. API Specification
9.1 Trace Ingestion Endpoint
POST /v1/traces
Content-Type: application/protobuf
Request Body: ExportTraceServiceRequest (OTLP format)
Response: ExportTraceServiceResponse (202 Accepted)
9.2 Topology Query APIs
# Get current service topology
GET /v1/topology/services
Response:
{
"services": [ServiceNode],
"timestamp": "2024-01-15T10:30:00Z",
"version": 42
}
Get service details
GET /v1/topology/services/{serviceId}
Response: ServiceNode + operations array + edge statistics
Get call relationships
GET /v1/topology/edges?source={serviceId}&target={targetId}
Response: {
"edges": [ServiceEdge],
"aggregationWindow": "1h"
}
Get DAG for visualization
GET /v1/topology/dag?format={json|graphml|dot}
Response:
- JSON: { "nodes": [...], "edges": [...] }
- GraphML/DOT: Graph format
List DAG versions
GET /v1/topology/versions?limit=10&offset=0
Response: {
"versions": [DAGVersion],
"total": 150
}
Get specific DAG version
GET /v1/topology/versions/{versionId}
Response: DAGVersion
Get topology changes
GET /v1/topology/changes?since={timestamp}&limit=50
Response: {
"changes": [ChangeMetadata],
"hasMore": boolean
}
9.3 Configuration API
# Update topology discovery settings
POST /v1/config/topology
{
"aggregationWindow": "1h",
"retentionDays": 30,
"cyclicityCheckEnabled": true,
"minCallThreshold": 1,
"externalServicePatterns": ["*.external.com"]
}
Define service kind overrides
POST /v1/config/services/{serviceName}
{
"kind": "DATABASE",
"metadata": {
"environment": "production",
"team": "platform"
}
}
10. Implementation Details
10.1 Technology Stack
- Language: (Match NeuralBudget's existing stack)
- Trace Processing: OpenTelemetry SDK / Collector
- Graph Library: networkx (Python) or igraph
- Storage: PostgreSQL with JSONB (topologies) + TimescaleDB (metrics)
- Streaming: Optional: Kafka for high-volume scenarios
- Change Events: Pub/Sub or Webhooks
10.2 Topology Discovery Algorithm
Algorithm: DiscoverTopology(traces, aggregationWindow)
Input: List of traces from past aggregationWindow
Output: DAG with services (nodes) and calls (edges)
-
groupByService(traces) → traces_by_service
-
for each service_trace in traces_by_service:
- extract service_name, version, metadata
- create ServiceNode if not exists
- update lastSeen, version, metadata
-
for each trace in traces:
for each span in trace:
- if span.kind == SERVER or CONSUMER:
- extract peer.service or rpc.service
- if parentSpan exists and parentSpan.service != current.service:
- create Edge(parentSpan.service → span.service)
- update protocol, operation, latency metrics
-
deduplicateEdges() → merge parallel edges
-
validateDAG() → check acyclicity
-
compareWithPreviousVersion() → detect changes
-
persistDAG(version)
-
emitChangeEvents()
Time Complexity: O(n) where n = number of spans
Space Complexity: O(s + e) where s = services, e = edges
10.3 Cycle Detection & Resolution
Algorithm: DetectAndResolveCycles()
- Use DFS to identify cycles in current DAG
- If cycles detected:
- Log warning with cycle details
- Option A: Remove lowest-confidence edges (weak signals)
- Option B: Mark services as "potential cycle" with metadata
- Option C: Flag for manual review
Preserve topology for operational continuity
10.4 Change Detection Algorithm
Algorithm: DetectChanges(currentDAG, previousDAG)
Input: Two DAG versions
Output: Change metadata
-
nodeChanges = diff(currentDAG.nodes, previousDAG.nodes)
- nodesAdded: nodes in current but not previous
- nodesRemoved: nodes in previous but not current
- nodesModified: version or metadata changes
-
edgeChanges = diff(currentDAG.edges, previousDAG.edges)
- edgesAdded: new communication patterns
- edgesRemoved: deprecated communication paths
- edgesModified: protocol/latency threshold changes
generateChangeNotification()
11. Configuration
11.1 Environment Variables
# Trace ingestion
TRACE_OTLP_PORT=4317 # gRPC endpoint
TRACE_HTTP_PORT=4318 # HTTP endpoint
TRACE_BATCH_SIZE=1000
TRACE_BATCH_TIMEOUT_MS=5000
Topology discovery
TOPOLOGY_AGGREGATION_WINDOW=3600 # seconds (1 hour)
TOPOLOGY_DISCOVERY_INTERVAL=600 # seconds (10 minutes)
TOPOLOGY_RETENTION_DAYS=30
TOPOLOGY_VERSION_LIMIT=500
Change detection
TOPOLOGY_CHANGE_THRESHOLD=0.05 # 5% change triggers new version
TOPOLOGY_CHANGE_WEBHOOK_URL= # optional webhook
TOPOLOGY_CHANGE_EVENTS_ENABLED=true
Storage
POSTGRES_CONNECTION_STRING=
TIMESCALEDB_ENABLED=true
METRICS_WINDOW_SIZE=3600 # seconds
Performance
TRACE_PROCESSOR_WORKERS=4
DAG_BUILDER_TIMEOUT_MS=5000
CYCLE_CHECK_ENABLED=true
11.2 Configuration File Example
# config.yaml
tracing:
otlp:
enabled: true
port: 4317
batch:
size: 1000
timeout_ms: 5000
topology:
discovery:
enabled: true
aggregation_window: 1h
interval: 10m
min_call_threshold: 1
storage:
retention: 30d
max_versions: 500
compression: gzip
validation:
enable_cycle_check: true
external_service_patterns:
- ".amazonaws.com"
- ".stripe.com"
- "*.external.io"
change_detection:
enabled: true
threshold: 0.05 # 5%
notifications:
webhook_url: "https://events.company.com/topology-changes"
queue_topic: "topology.changes"
12. Example Usage
12.1 Sending Traces
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
Configure OTLP exporter pointing to NeuralBudget
otlp_exporter = OTLPSpanExporter(
endpoint="localhost:4317"
)
trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(otlp_exporter)
)
tracer = trace.get_tracer(name)
Spans automatically contribute to topology discovery
with tracer.start_as_current_span("PaymentService.processPayment") as span:
span.set_attribute("service.name", "payment-service")
# ... business logic
12.2 Querying Topology
# Get current service topology
curl http://localhost:8080/v1/topology/services | jq
Export as GraphML for visualization
curl http://localhost:8080/v1/topology/dag?format=graphml > topology.graphml
Check what changed in last hour
curl "http://localhost:8080/v1/topology/changes?since=2024-01-15T09:00:00Z" | jq
Get service dependencies
curl http://localhost:8080/v1/topology/edges?source=payment-service | jq
13. Error Handling
13.1 Trace Processing Errors
| Error |
Handling |
| Malformed trace |
Log warning, skip span, increment error counter |
| Missing service.name |
Use span.peer_service or rpc.service, fallback to "unknown-service" |
| Inconsistent versions |
Use most recent version seen |
| Dropped due to backpressure |
Emit metric, return HTTP 429 |
14. Testing Strategy
14.1 Unit Tests
- Span attribute extraction and validation
- Service node creation and deduplication
- Edge relationship building
- Cycle detection algorithm
- Change detection logic
14.2 Integration Tests
- Full trace → topology pipeline
- Multiple concurrent services
- Protocol detection (HTTP, gRPC, DB, Messaging)
- Version management and history
14.3 Load Testing
- 100k spans/second ingestion
- 10,000 service DAG construction
- Query response times under load
- Memory/storage efficiency
14.4 Test Data
# Generate synthetic traces
from opentelemetry.proto.trace.v1 import traces_pb2
def generate_synthetic_trace(
num_spans=100,
num_services=10,
error_rate=0.05
):
# Creates realistic trace with multiple services
# for testing topology discovery
pass
15. Monitoring & Observability
15.1 Key Metrics
# Trace processing
trace_ingestion_rate (spans/sec)
trace_processing_latency (ms, p50/p99)
trace_processing_errors (count)
trace_buffer_capacity (%)
Topology discovery
services_discovered (count)
service_edges_detected (count)
topology_update_latency (ms)
topology_version_changes (count)
cycles_detected (count)
Storage
topology_storage_size (bytes)
version_history_count (count)
database_query_latency (ms)
15.2 Alerting Rules
alerts:
- name: HighTraceProcessingError
condition: trace_processing_errors > 100/min
severity: warning
16. Future Enhancements
- Phase 2: Metrics & logs correlation with traces
- Phase 3: ML-based anomaly detection in topology changes
- Phase 4: Service mesh integration (Istio, Linkerd)
- Phase 5: Real-time Web UI with live topology updates
- Phase 6: Trace-based SLA tracking and compliance
- Phase 7: Cost attribution via topology paths
17. Migration & Rollout
17.1 Backward Compatibility
- DAG APIs versioned (
/v1/, /v2/)
- Manual DAG definition still supported
- Gradual migration of users to auto-discovery
17.2 Rollout Plan
- Alpha: Internal testing with one production service
- Beta: Gradual rollout to 10% of users, with manual DAG override option
- GA: Full rollout, auto-discovery as default
- Deprecation: Manual DAG definition deprecated in 12 months
18. Success Metrics
- Topology accuracy >99% vs. manual definitions
- 95% reduction in manual DAG updates
- <5 second detection latency for new services
- <2% false positive change detection rate
- 99.9% availability of topology APIs
- Ingestion throughput >100k spans/sec
19. References
# Feature Specification: Automatic Trace Topology Ingestion & Dynamic DAG Building
1. Overview
This feature enables NeuralBudget to automatically ingest distributed trace data from OpenTelemetry (and compatible observability systems) to dynamically construct and maintain Directed Acyclic Graphs (DAGs) representing service topologies and call flows. This eliminates manual service dependency definition and creates living, self-updating visualizations of system architecture.
2. Problem Statement
Current workflow limitations:
- DAG definitions are static and require manual updates when service architecture changes
- Teams must manually maintain service dependency mappings
- New services or changed communication patterns require code updates
- No automatic detection of service relationships in distributed systems
- Difficult to keep documentation in sync with actual runtime topology
3. Goals
- Automatic Discovery: Extract service topology directly from distributed trace telemetry
- Real-time Updates: Reflect architectural changes without manual intervention
- OpenTelemetry Native: Leverage OpenTelemetry standards for maximum compatibility
- Low Overhead: Minimal performance impact on trace processing
- Visualization: Auto-generate and update service dependency graphs
- Historical Tracking: Maintain topology versioning and change history
4. Scope
In Scope
- OpenTelemetry trace ingestion (OTLP protocol)
- Service and operation discovery from spans
- Call relationship mapping (parent-child span relationships)
- Dynamic DAG construction and updates
- Topology versioning and change detection
- REST APIs for topology queries
- Basic visualization export (JSON, GraphML)
Out of Scope
- Metrics/logs ingestion (trace-only in Phase 1)
- Real-time visualization UI (API-first, UI integration separate)
- Custom trace filtering rules (standard OpenTelemetry semantics only)
- Cost optimization for trace sampling
5. Technical Architecture
5.1 Core Components
┌─────────────────────────────────────────────────────────┐
│ OpenTelemetry Collector │
│ (OTLP Receiver) │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Trace Ingestion Service │
│ - Validate & normalize traces │
│ - Extract span metadata │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Topology Discovery Engine │
│ - Build service graph from spans │
│ - Detect operation flows │
│ - Identify communication patterns │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ DAG Builder & Manager │
│ - Construct/update DAGs │
│ - Detect topology changes │
│ - Version management │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Topology Store (Database) │
│ - Current topology state │
│ - Historical versions │
│ - Change audit log │
└─────────────────────────────────────────────────────────┘
5.2 Data Flow
- Trace Reception: OpenTelemetry traces arrive via OTLP (gRPC/HTTP)
- Normalization: Extract key attributes (service.name, span.kind, attributes)
- Relationship Detection: Identify parent-child relationships and RPC calls
- Service Mapping: Group spans by service and build adjacency matrix
- DAG Construction: Create graph nodes (services) and edges (calls)
- Change Detection: Compare against previous topology state
- Storage & Versioning: Persist changes with timestamps and metadata
- Notification: Emit events for topology changes
6. Data Model
6.1 Key Span Attributes (OpenTelemetry Semantic Conventions)
{
"service.name": "payment-service",
"service.version": "1.2.3",
"span.kind": "CLIENT|SERVER|PRODUCER|CONSUMER|INTERNAL",
"span.name": "PaymentService.processPayment",
"rpc.service": "payment.v1.PaymentService",
"rpc.method": "ProcessPayment",
"http.method": "POST",
"http.target": "/api/v1/payments",
"peer.service": "database-service",
"messaging.system": "kafka",
"messaging.destination": "payment-events"
}
6.2 Service Node
interface ServiceNode {
id: string; // Unique service identifier
name: string; // service.name
version: string; // service.version (nullable)
kind: 'SERVICE' | 'DATABASE' | 'QUEUE' | 'EXTERNAL';
firstSeen: ISO8601DateTime;
lastSeen: ISO8601DateTime;
metadata: {
environment?: string;
region?: string;
language?: string;
[key: string]: any;
};
operations: string[]; // List of span.name values
}
6.3 Edge (Call Relationship)
interface ServiceEdge {
id: string; // Unique edge identifier
sourceId: string; // caller service
targetId: string; // callee service
protocol: 'HTTP' | 'GRPC' | 'AMQP' | 'KAFKA' | 'DATABASE' | 'UNKNOWN';
callCount: number; // Cumulative call count
errorCount: number; // Failed calls in window
latencyP50: number; // ms
latencyP99: number; // ms
firstSeen: ISO8601DateTime;
lastSeen: ISO8601DateTime;
operations: string[]; // RPC/HTTP operations
attributes: {
[key: string]: any;
};
}
6.4 DAG Version
interface DAGVersion {
id: string; // UUID
version: number; // Incremental counter
timestamp: ISO8601DateTime;
nodes: ServiceNode[];
edges: ServiceEdge[];
changeMetadata: {
nodesAdded: string[]; // Node IDs
nodesRemoved: string[];
edgesAdded: string[];
edgesRemoved: string[];
nodesModified: string[]; // version/metadata changes
reason: string; // "topology_discovery" | "trace_analysis"
};
hash: string; // SHA256 of topology
}
7. Functional Requirements
FR1: Trace Ingestion
- Accept OpenTelemetry traces via OTLP (gRPC + HTTP)
- Validate trace format and schema
- Handle batch and streaming ingestion
- Support configurable retention window (default: 7 days)
FR2: Service Discovery
- Extract service.name from trace attributes
- Identify service kind (service vs external dependency)
- Track service versions when available
- Detect first/last seen timestamps
FR3: Call Relationship Detection
- Identify parent-child span relationships
- Classify communication protocol (HTTP, gRPC, DB, Messaging)
- Extract operation names and endpoints
- Calculate call frequency and error rates
- Measure latency percentiles (p50, p99)
FR4: DAG Construction
- Build service graph from discovered relationships
- Ensure DAG remains acyclic (cycle detection)
- Merge redundant relationships
- Support multiple versions of DAG (historical)
FR5: Change Detection & Notification
- Compare current topology against previous state
- Classify changes (additions, removals, modifications)
- Generate audit trail with reasons
- Emit events for topology changes (webhook, queue)
FR6: Query & Export APIs
- List all services in current topology
- Get service details with operations
- Query edges between services
- Export DAG in multiple formats (JSON, GraphML, DOT)
- Historical topology queries
- Change log queries
FR7: Aggregation Window
- Configure time window for topology analysis (default: 1 hour)
- Aggregate metrics within window (call counts, latencies)
- Generate new DAG version periodically or on-demand
8. Non-Functional Requirements
NFR1: Performance
- Ingest 100k+ spans/second with <100ms latency
- Topology update <5 seconds for most changes
- Query API response time <200ms (p99)
NFR2: Reliability
- 99.9% availability for ingestion pipeline
- Graceful degradation (buffer overflow handling)
- Automatic retry for failed trace processing
NFR3: Scalability
- Horizontal scaling of trace processors
- Database sharding support for large deployments
- Efficient memory usage for large topologies (1000+ services)
NFR4: Data Quality
- Deduplicate spans with matching IDs
- Validate semantic convention compliance
- Configurable leniency for malformed traces
NFR5: Storage
- Compress historical DAG versions
- Configurable TTL for version history (default: 30 days)
- Efficient storage <10MB per DAG snapshot
9. API Specification
9.1 Trace Ingestion Endpoint
POST /v1/traces
Content-Type: application/protobuf
Request Body: ExportTraceServiceRequest (OTLP format)
Response: ExportTraceServiceResponse (202 Accepted)
9.2 Topology Query APIs
# Get current service topology
GET /v1/topology/services
Response:
{
"services": [ServiceNode],
"timestamp": "2024-01-15T10:30:00Z",
"version": 42
}
# Get service details
GET /v1/topology/services/{serviceId}
Response: ServiceNode + operations array + edge statistics
# Get call relationships
GET /v1/topology/edges?source={serviceId}&target={targetId}
Response: {
"edges": [ServiceEdge],
"aggregationWindow": "1h"
}
# Get DAG for visualization
GET /v1/topology/dag?format={json|graphml|dot}
Response:
- JSON: { "nodes": [...], "edges": [...] }
- GraphML/DOT: Graph format
# List DAG versions
GET /v1/topology/versions?limit=10&offset=0
Response: {
"versions": [DAGVersion],
"total": 150
}
# Get specific DAG version
GET /v1/topology/versions/{versionId}
Response: DAGVersion
# Get topology changes
GET /v1/topology/changes?since={timestamp}&limit=50
Response: {
"changes": [ChangeMetadata],
"hasMore": boolean
}
9.3 Configuration API
# Update topology discovery settings
POST /v1/config/topology
{
"aggregationWindow": "1h",
"retentionDays": 30,
"cyclicityCheckEnabled": true,
"minCallThreshold": 1,
"externalServicePatterns": ["*.external.com"]
}
# Define service kind overrides
POST /v1/config/services/{serviceName}
{
"kind": "DATABASE",
"metadata": {
"environment": "production",
"team": "platform"
}
}
10. Implementation Details
10.1 Technology Stack
- Language: (Match NeuralBudget's existing stack)
- Trace Processing: OpenTelemetry SDK / Collector
- Graph Library: networkx (Python) or igraph
- Storage: PostgreSQL with JSONB (topologies) + TimescaleDB (metrics)
- Streaming: Optional: Kafka for high-volume scenarios
- Change Events: Pub/Sub or Webhooks
10.2 Topology Discovery Algorithm
Algorithm: DiscoverTopology(traces, aggregationWindow)
Input: List of traces from past aggregationWindow
Output: DAG with services (nodes) and calls (edges)
1. groupByService(traces) → traces_by_service
2. for each service_trace in traces_by_service:
- extract service_name, version, metadata
- create ServiceNode if not exists
- update lastSeen, version, metadata
3. for each trace in traces:
for each span in trace:
- if span.kind == SERVER or CONSUMER:
- extract peer.service or rpc.service
- if parentSpan exists and parentSpan.service != current.service:
- create Edge(parentSpan.service → span.service)
- update protocol, operation, latency metrics
4. deduplicateEdges() → merge parallel edges
5. validateDAG() → check acyclicity
6. compareWithPreviousVersion() → detect changes
7. persistDAG(version)
8. emitChangeEvents()
Time Complexity: O(n) where n = number of spans
Space Complexity: O(s + e) where s = services, e = edges
10.3 Cycle Detection & Resolution
Algorithm: DetectAndResolveCycles()
1. Use DFS to identify cycles in current DAG
2. If cycles detected:
- Log warning with cycle details
- Option A: Remove lowest-confidence edges (weak signals)
- Option B: Mark services as "potential cycle" with metadata
- Option C: Flag for manual review
3. Preserve topology for operational continuity
10.4 Change Detection Algorithm
Algorithm: DetectChanges(currentDAG, previousDAG)
Input: Two DAG versions
Output: Change metadata
1. nodeChanges = diff(currentDAG.nodes, previousDAG.nodes)
- nodesAdded: nodes in current but not previous
- nodesRemoved: nodes in previous but not current
- nodesModified: version or metadata changes
2. edgeChanges = diff(currentDAG.edges, previousDAG.edges)
- edgesAdded: new communication patterns
- edgesRemoved: deprecated communication paths
- edgesModified: protocol/latency threshold changes
3. generateChangeNotification()
11. Configuration
11.1 Environment Variables
# Trace ingestion
TRACE_OTLP_PORT=4317 # gRPC endpoint
TRACE_HTTP_PORT=4318 # HTTP endpoint
TRACE_BATCH_SIZE=1000
TRACE_BATCH_TIMEOUT_MS=5000
# Topology discovery
TOPOLOGY_AGGREGATION_WINDOW=3600 # seconds (1 hour)
TOPOLOGY_DISCOVERY_INTERVAL=600 # seconds (10 minutes)
TOPOLOGY_RETENTION_DAYS=30
TOPOLOGY_VERSION_LIMIT=500
# Change detection
TOPOLOGY_CHANGE_THRESHOLD=0.05 # 5% change triggers new version
TOPOLOGY_CHANGE_WEBHOOK_URL= # optional webhook
TOPOLOGY_CHANGE_EVENTS_ENABLED=true
# Storage
POSTGRES_CONNECTION_STRING=
TIMESCALEDB_ENABLED=true
METRICS_WINDOW_SIZE=3600 # seconds
# Performance
TRACE_PROCESSOR_WORKERS=4
DAG_BUILDER_TIMEOUT_MS=5000
CYCLE_CHECK_ENABLED=true
11.2 Configuration File Example
# config.yaml
tracing:
otlp:
enabled: true
port: 4317
batch:
size: 1000
timeout_ms: 5000
topology:
discovery:
enabled: true
aggregation_window: 1h
interval: 10m
min_call_threshold: 1
storage:
retention: 30d
max_versions: 500
compression: gzip
validation:
enable_cycle_check: true
external_service_patterns:
- "*.amazonaws.com"
- "*.stripe.com"
- "*.external.io"
change_detection:
enabled: true
threshold: 0.05 # 5%
notifications:
webhook_url: "https://events.company.com/topology-changes"
queue_topic: "topology.changes"
12. Example Usage
12.1 Sending Traces
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# Configure OTLP exporter pointing to NeuralBudget
otlp_exporter = OTLPSpanExporter(
endpoint="localhost:4317"
)
trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(otlp_exporter)
)
tracer = trace.get_tracer(__name__)
# Spans automatically contribute to topology discovery
with tracer.start_as_current_span("PaymentService.processPayment") as span:
span.set_attribute("service.name", "payment-service")
# ... business logic
12.2 Querying Topology
# Get current service topology
curl http://localhost:8080/v1/topology/services | jq
# Export as GraphML for visualization
curl http://localhost:8080/v1/topology/dag?format=graphml > topology.graphml
# Check what changed in last hour
curl "http://localhost:8080/v1/topology/changes?since=2024-01-15T09:00:00Z" | jq
# Get service dependencies
curl http://localhost:8080/v1/topology/edges?source=payment-service | jq
13. Error Handling
13.1 Trace Processing Errors
| Error |
Handling |
| Malformed trace |
Log warning, skip span, increment error counter |
| Missing service.name |
Use span.peer_service or rpc.service, fallback to "unknown-service" |
| Inconsistent versions |
Use most recent version seen |
| Dropped due to backpressure |
Emit metric, return HTTP 429 |
13.2 DAG Construction Errors
| Error |
Handling |
| Detected cycle |
Log warning, remove weakest edge, flag for review |
| Topology explosion (>5000 nodes) |
Aggregate by domain, emit alert |
| Failed change detection |
Skip version, retry on next interval |
14. Testing Strategy
14.1 Unit Tests
- Span attribute extraction and validation
- Service node creation and deduplication
- Edge relationship building
- Cycle detection algorithm
- Change detection logic
14.2 Integration Tests
- Full trace → topology pipeline
- Multiple concurrent services
- Protocol detection (HTTP, gRPC, DB, Messaging)
- Version management and history
14.3 Load Testing
- 100k spans/second ingestion
- 10,000 service DAG construction
- Query response times under load
- Memory/storage efficiency
14.4 Test Data
# Generate synthetic traces
from opentelemetry.proto.trace.v1 import traces_pb2
def generate_synthetic_trace(
num_spans=100,
num_services=10,
error_rate=0.05
):
# Creates realistic trace with multiple services
# for testing topology discovery
pass
15. Monitoring & Observability
15.1 Key Metrics
# Trace processing
trace_ingestion_rate (spans/sec)
trace_processing_latency (ms, p50/p99)
trace_processing_errors (count)
trace_buffer_capacity (%)
# Topology discovery
services_discovered (count)
service_edges_detected (count)
topology_update_latency (ms)
topology_version_changes (count)
cycles_detected (count)
# Storage
topology_storage_size (bytes)
version_history_count (count)
database_query_latency (ms)
15.2 Alerting Rules
alerts:
- name: HighTraceProcessingError
condition: trace_processing_errors > 100/min
severity: warning
- name: TopologyDiscoveryFailure
condition: topology_update_latency > 30s
severity: critical
- name: CycleDetected
condition: cycles_detected > 0
severity: warning
16. Future Enhancements
- Phase 2: Metrics & logs correlation with traces
- Phase 3: ML-based anomaly detection in topology changes
- Phase 4: Service mesh integration (Istio, Linkerd)
- Phase 5: Real-time Web UI with live topology updates
- Phase 6: Trace-based SLA tracking and compliance
- Phase 7: Cost attribution via topology paths
17. Migration & Rollout
17.1 Backward Compatibility
- DAG APIs versioned (
/v1/, /v2/)
- Manual DAG definition still supported
- Gradual migration of users to auto-discovery
17.2 Rollout Plan
- Alpha: Internal testing with one production service
- Beta: Gradual rollout to 10% of users, with manual DAG override option
- GA: Full rollout, auto-discovery as default
- Deprecation: Manual DAG definition deprecated in 12 months
18. Success Metrics
- Topology accuracy >99% vs. manual definitions
- 95% reduction in manual DAG updates
- <5 second detection latency for new services
- <2% false positive change detection rate
- 99.9% availability of topology APIs
- Ingestion throughput >100k spans/sec
19. References
Feature Specification: Automatic Trace Topology Ingestion & Dynamic DAG Building
1. Overview
This feature enables NeuralBudget to automatically ingest distributed trace data from OpenTelemetry (and compatible observability systems) to dynamically construct and maintain Directed Acyclic Graphs (DAGs) representing service topologies and call flows. This eliminates manual service dependency definition and creates living, self-updating visualizations of system architecture.
2. Problem Statement
Current workflow limitations:
3. Goals
4. Scope
In Scope
Out of Scope
5. Technical Architecture
5.1 Core Components
5.2 Data Flow
6. Data Model
6.1 Key Span Attributes (OpenTelemetry Semantic Conventions)
6.2 Service Node
6.3 Edge (Call Relationship)
6.4 DAG Version
7. Functional Requirements
FR1: Trace Ingestion
FR2: Service Discovery
FR3: Call Relationship Detection
FR4: DAG Construction
FR5: Change Detection & Notification
FR6: Query & Export APIs
FR7: Aggregation Window
8. Non-Functional Requirements
NFR1: Performance
NFR2: Reliability
NFR3: Scalability
NFR4: Data Quality
NFR5: Storage
9. API Specification
9.1 Trace Ingestion Endpoint
9.2 Topology Query APIs
9.3 Configuration API
10. Implementation Details
10.1 Technology Stack
10.2 Topology Discovery Algorithm
10.3 Cycle Detection & Resolution
10.4 Change Detection Algorithm
11. Configuration
11.1 Environment Variables
11.2 Configuration File Example
12. Example Usage
12.1 Sending Traces
12.2 Querying Topology
13. Error Handling
13.1 Trace Processing Errors
14. Testing Strategy
14.1 Unit Tests
14.2 Integration Tests
14.3 Load Testing
14.4 Test Data
15. Monitoring & Observability
15.1 Key Metrics
15.2 Alerting Rules
name: TopologyDiscoveryFailure
condition: topology_update_latency > 30s
severity: critical
name: CycleDetected
condition: cycles_detected > 0
severity: warning
16. Future Enhancements
17. Migration & Rollout
17.1 Backward Compatibility
/v1/,/v2/)17.2 Rollout Plan
18. Success Metrics
19. References
- OpenTelemetry Specification
- OpenTelemetry Semantic Conventions
- OTLP Protocol
- Graph Theory - Cycle Detection
# Feature Specification: Automatic Trace Topology Ingestion & Dynamic DAG Building1. Overview
This feature enables NeuralBudget to automatically ingest distributed trace data from OpenTelemetry (and compatible observability systems) to dynamically construct and maintain Directed Acyclic Graphs (DAGs) representing service topologies and call flows. This eliminates manual service dependency definition and creates living, self-updating visualizations of system architecture.
2. Problem Statement
Current workflow limitations:
3. Goals
4. Scope
In Scope
Out of Scope
5. Technical Architecture
5.1 Core Components
5.2 Data Flow
6. Data Model
6.1 Key Span Attributes (OpenTelemetry Semantic Conventions)
{ "service.name": "payment-service", "service.version": "1.2.3", "span.kind": "CLIENT|SERVER|PRODUCER|CONSUMER|INTERNAL", "span.name": "PaymentService.processPayment", "rpc.service": "payment.v1.PaymentService", "rpc.method": "ProcessPayment", "http.method": "POST", "http.target": "/api/v1/payments", "peer.service": "database-service", "messaging.system": "kafka", "messaging.destination": "payment-events" }6.2 Service Node
6.3 Edge (Call Relationship)
6.4 DAG Version
7. Functional Requirements
FR1: Trace Ingestion
FR2: Service Discovery
FR3: Call Relationship Detection
FR4: DAG Construction
FR5: Change Detection & Notification
FR6: Query & Export APIs
FR7: Aggregation Window
8. Non-Functional Requirements
NFR1: Performance
NFR2: Reliability
NFR3: Scalability
NFR4: Data Quality
NFR5: Storage
9. API Specification
9.1 Trace Ingestion Endpoint
9.2 Topology Query APIs
9.3 Configuration API
10. Implementation Details
10.1 Technology Stack
10.2 Topology Discovery Algorithm
10.3 Cycle Detection & Resolution
10.4 Change Detection Algorithm
11. Configuration
11.1 Environment Variables
11.2 Configuration File Example
12. Example Usage
12.1 Sending Traces
12.2 Querying Topology
13. Error Handling
13.1 Trace Processing Errors
13.2 DAG Construction Errors
14. Testing Strategy
14.1 Unit Tests
14.2 Integration Tests
14.3 Load Testing
14.4 Test Data
15. Monitoring & Observability
15.1 Key Metrics
15.2 Alerting Rules
16. Future Enhancements
17. Migration & Rollout
17.1 Backward Compatibility
/v1/,/v2/)17.2 Rollout Plan
18. Success Metrics
19. References