Skip to content

Automatically ingest trace topology (e.g., from OpenTelemetry) to build and update these DAGs dynamically #2

Description

@pristley

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

  1. Trace Reception: OpenTelemetry traces arrive via OTLP (gRPC/HTTP)
  2. Normalization: Extract key attributes (service.name, span.kind, attributes)
  3. Relationship Detection: Identify parent-child relationships and RPC calls
  4. Service Mapping: Group spans by service and build adjacency matrix
  5. DAG Construction: Create graph nodes (services) and edges (calls)
  6. Change Detection: Compare against previous topology state
  7. Storage & Versioning: Persist changes with timestamps and metadata
  8. 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

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

  1. Alpha: Internal testing with one production service
  2. Beta: Gradual rollout to 10% of users, with manual DAG override option
  3. GA: Full rollout, auto-discovery as default
  4. 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

  1. Trace Reception: OpenTelemetry traces arrive via OTLP (gRPC/HTTP)
  2. Normalization: Extract key attributes (service.name, span.kind, attributes)
  3. Relationship Detection: Identify parent-child relationships and RPC calls
  4. Service Mapping: Group spans by service and build adjacency matrix
  5. DAG Construction: Create graph nodes (services) and edges (calls)
  6. Change Detection: Compare against previous topology state
  7. Storage & Versioning: Persist changes with timestamps and metadata
  8. 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

  1. Alpha: Internal testing with one production service
  2. Beta: Gradual rollout to 10% of users, with manual DAG override option
  3. GA: Full rollout, auto-discovery as default
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions