Documentation
¶
Overview ¶
Per-operation attribute value generators for wide span emission Supports static, weighted, sequence, boolean, range, and normal distribution values
Structural analysis of topology graphs Computes worst-case depth, fan-out, and span count to catch surprising explosions
YAML DSL configuration types, loading, and validation for synthetic topology Parses service definitions, traffic patterns, and scenario overrides
Duration distribution parsing and sampling for synthetic telemetry Supports the "30ms +/- 10ms" DSL format with normal distribution sampling
Package synth generates synthetic OpenTelemetry signals from a topology graph. It provides a simulation engine, traffic patterns, attribute generators, and structural analysis tools for testing observability pipelines.
LogObserver derives log records from error and slow spans. Emits ERROR-severity logs for error spans and WARN-severity logs for slow spans.
MetricObserver derives request duration, count, and error metrics from spans. Uses the OTel Metrics API to record measurements with service and operation attributes.
SpanObserver interface for deriving signals (metrics, logs) from emitted spans. Observers receive span metadata after each span completes.
Rate parsing for traffic configuration Parses rate strings like "10/s", "5/m", "100/h" into a count and period
Scenario activation and override resolution for time-windowed behaviour changes Parses time offsets, determines active scenarios, and merges overlapping overrides
Per-operation runtime state for cross-trace simulation effects Tracks queue depth, circuit breaker status, and backpressure for each operation
Topology graph construction from parsed YAML configuration Resolves string references to pointers, detects root operations and cycles
Traffic pattern implementations for synthetic load generation Provides uniform, diurnal, bursty, and custom arrival rate models
Index ¶
- Constants
- func MaxDepth(topo *Topology) (int, []string)
- func MaxFanOut(topo *Topology) (int, string)
- func MaxSpans(topo *Topology) (int, string)
- func ParseOffset(s string) (time.Duration, error)
- func ResolveOverrides(active []Scenario) map[string]Override
- func ValidateConfig(cfg *Config) error
- type AttributeGenerator
- type AttributeValueConfig
- type BackpressureConfig
- type BoolValue
- type BurstyPattern
- type Call
- type CallConfig
- type CheckOptions
- type CheckResult
- type CircuitBreakerConfig
- type CircuitState
- type Config
- type Distribution
- type DistributionConfig
- type DistributionSummary
- type DiurnalPattern
- type DomainResolver
- type Engine
- type LogObserver
- type MetricObserver
- type NormalValue
- type Operation
- type OperationConfig
- type OperationState
- type Override
- type OverrideConfig
- type RangeValue
- type Rate
- type RemoveCallConfig
- type ResolvedBackpressure
- type ResolvedCircuitBreaker
- type SampleDistribution
- type SampleResults
- type Scenario
- type ScenarioConfig
- type SegmentConfig
- type SequenceValue
- type Service
- type ServiceConfig
- type SimulationState
- type SpanInfo
- type SpanObserver
- type StaticValue
- type Stats
- type Topology
- type TracerSource
- type TrafficConfig
- type TrafficPattern
- type UniformPattern
- type WeightedChoice
Constants ¶
const ( ReasonQueueFull = "queue_full" ReasonCircuitOpen = "circuit_open" )
Rejection reason constants for span attributes.
const CurrentVersion = 1
CurrentVersion is the supported schema version for synth topology configs.
const DefaultMaxSpansPerTrace = 10_000
DefaultMaxSpansPerTrace is the safety bound for span generation per trace.
const MaxRateCount = 10000
MaxRateCount is the maximum allowed count in a rate string (e.g. "10000/s").
Variables ¶
This section is empty.
Functions ¶
func MaxDepth ¶ added in v0.6.0
MaxDepth returns the longest path (edge count) from any root to any leaf and the operation refs along that path.
Memoisation is safe because BuildTopology guarantees the topology is acyclic. In a DAG, a node's subtree depth is the same regardless of which path reaches it, so the memo produces correct results under any visited set.
func MaxFanOut ¶ added in v0.6.0
MaxFanOut returns the worst-case direct children per span and which operation produces it. For each operation, it sums max(call.Count, 1) * (1 + call.Retries) across all calls.
func MaxSpans ¶ added in v0.6.0
MaxSpans returns the worst-case total spans per trace via DFS from each root. It multiplies fan-out at each level. Both on-error and on-success paths are included (conservative — real traces cannot take both). Returns the max and which root produces it.
Memoisation is safe because BuildTopology guarantees the topology is acyclic. In a DAG, a node's subtree span count is the same regardless of which path reaches it, so the memo produces correct results under any visited set.
func ParseOffset ¶
ParseOffset parses a time offset string like "+5m" or "30s" into a duration.
func ResolveOverrides ¶
ResolveOverrides merges overrides from multiple active scenarios. Later scenarios override earlier ones (last-defined-wins), but only for fields that are explicitly set. Attributes are merged per-key.
func ValidateConfig ¶
ValidateConfig checks a configuration for structural correctness.
Types ¶
type AttributeGenerator ¶
AttributeGenerator produces typed values for a span attribute.
func NewAttributeGenerator ¶
func NewAttributeGenerator(cfg AttributeValueConfig) (AttributeGenerator, error)
NewAttributeGenerator creates an AttributeGenerator from a config entry. Exactly one of the config fields must be set.
type AttributeValueConfig ¶
type AttributeValueConfig struct {
Value any `yaml:"value,omitempty"`
Values map[any]int `yaml:"values,omitempty"`
Sequence string `yaml:"sequence,omitempty"`
Probability *float64 `yaml:"probability,omitempty"`
Range []int64 `yaml:"range,omitempty"`
Distribution *DistributionConfig `yaml:"distribution,omitempty"`
}
AttributeValueConfig defines how an attribute value is generated from YAML.
type BackpressureConfig ¶
type BackpressureConfig struct {
LatencyThreshold string `yaml:"latency_threshold"`
DurationMultiplier float64 `yaml:"duration_multiplier,omitempty"`
ErrorRateAdd string `yaml:"error_rate_add,omitempty"`
}
BackpressureConfig describes backpressure behaviour for an operation.
type BoolValue ¶
type BoolValue struct {
Probability float64
}
BoolValue generates a boolean based on a probability threshold.
type BurstyPattern ¶
type BurstyPattern struct {
BaseRate float64
BurstMultiplier float64
BurstInterval time.Duration
BurstDuration time.Duration
}
BurstyPattern alternates between a base rate and periodic high-rate bursts.
type Call ¶
type Call struct {
Operation *Operation
Probability float64
Condition string
Count int
Timeout time.Duration
Retries int
RetryBackoff time.Duration
}
Call represents a resolved downstream call with optional modifiers.
type CallConfig ¶
type CallConfig struct {
Target string `yaml:"target"`
Probability float64 `yaml:"probability,omitempty"`
Condition string `yaml:"condition,omitempty"`
Count int `yaml:"count,omitempty"`
Timeout string `yaml:"timeout,omitempty"`
Retries int `yaml:"retries,omitempty"`
RetryBackoff string `yaml:"retry_backoff,omitempty"`
}
CallConfig describes a downstream call in the YAML DSL. Supports both simple string form ("service.op") and rich mapping form.
func (*CallConfig) UnmarshalYAML ¶
func (c *CallConfig) UnmarshalYAML(unmarshal func(any) error) error
UnmarshalYAML handles both scalar string and mapping forms for call config.
type CheckOptions ¶ added in v0.6.0
type CheckOptions struct {
MaxDepth int
MaxFanOut int
MaxSpans int
MaxSpansPerTrace int
Samples int
Seed uint64
}
CheckOptions configures the thresholds and sampling for Check.
type CheckResult ¶ added in v0.6.0
type CheckResult struct {
Name string
Pass bool
Limit int
Actual int
Sampled *int
SamplesRun int
Path []string
Ref string
Distribution *DistributionSummary
}
CheckResult holds the outcome of a single structural check.
func Check ¶ added in v0.6.0
func Check(topo *Topology, opts CheckOptions) []CheckResult
Check runs structural analysis and sampled exploration, returning one result per check.
type CircuitBreakerConfig ¶
type CircuitBreakerConfig struct {
FailureThreshold int `yaml:"failure_threshold"`
Window string `yaml:"window"`
Cooldown string `yaml:"cooldown"`
}
CircuitBreakerConfig describes circuit breaker behaviour for an operation.
type CircuitState ¶
type CircuitState int
CircuitState represents the state of a circuit breaker.
const ( CircuitClosed CircuitState = iota // Allows all requests through. CircuitOpen // Rejects all requests. CircuitHalfOpen // Allows a probe request to test recovery. )
Circuit breaker states.
type Config ¶
type Config struct {
Version int `yaml:"version"`
Services []ServiceConfig `yaml:"-"`
Traffic TrafficConfig `yaml:"traffic"`
Scenarios []ScenarioConfig `yaml:"scenarios,omitempty"`
}
Config is the top-level YAML configuration for a synthetic topology.
func LoadConfig ¶
LoadConfig reads and parses a YAML topology from a file path or URL.
type Distribution ¶
Distribution represents a duration with optional variance, sampled as a normal distribution.
func ParseDistribution ¶
func ParseDistribution(s string) (Distribution, error)
ParseDistribution parses a duration distribution string. Supported formats:
- "30ms +/- 10ms" (mean with standard deviation)
- "30ms ± 10ms" (unicode variant)
- "50ms" (fixed duration, zero variance)
func (Distribution) Sample ¶
func (d Distribution) Sample(rng *rand.Rand) time.Duration
Sample returns a duration drawn from a normal distribution, clamped to minimum zero.
func (Distribution) String ¶
func (d Distribution) String() string
String returns the distribution in DSL format.
type DistributionConfig ¶
DistributionConfig defines parameters for a normal distribution generator.
type DistributionSummary ¶ added in v0.6.1
DistributionSummary holds percentile statistics for a metric.
type DiurnalPattern ¶
type DiurnalPattern struct {
BaseRate float64
PeakMultiplier float64
TroughMultiplier float64
Period time.Duration
}
DiurnalPattern models a day/night cycle using a sine wave oscillating between trough and peak multipliers over a configurable period.
type DomainResolver ¶
type DomainResolver func(domain string) map[string]AttributeGenerator
DomainResolver maps a domain identifier to attribute generators. Returns nil if the domain is not recognised.
type Engine ¶
type Engine struct {
Topology *Topology
Traffic TrafficPattern
Scenarios []Scenario
Tracers TracerSource
Rng *rand.Rand
Duration time.Duration
Observers []SpanObserver
MaxSpansPerTrace int
State *SimulationState
LabelScenarios bool
TimeOffset time.Duration
}
Engine drives the trace generation simulation.
type LogObserver ¶
type LogObserver struct {
// contains filtered or unexported fields
}
LogObserver emits log records for notable span events.
func NewLogObserver ¶
NewLogObserver creates a LogObserver that emits logs via per-service loggers. Each logger should come from a LoggerProvider whose resource has the correct service.name. A slowThreshold of 0 disables slow span detection.
func (*LogObserver) Observe ¶
func (l *LogObserver) Observe(info SpanInfo)
Observe emits log records for error spans and spans exceeding the slow threshold.
type MetricObserver ¶
type MetricObserver struct {
// contains filtered or unexported fields
}
MetricObserver records derived metrics for each observed span.
func NewMetricObserver ¶
func NewMetricObserver(meters map[string]metric.Meter) (*MetricObserver, error)
NewMetricObserver creates a MetricObserver with per-service instruments. Each meter should carry a resource with the correct service.name for its service.
func (*MetricObserver) Observe ¶
func (m *MetricObserver) Observe(info SpanInfo)
Observe records metrics derived from the completed span. Note: metric data points are timestamped at collection time by the OTel SDK's PeriodicReader. The Metrics API does not support caller-supplied timestamps, so Engine.TimeOffset has no effect on metric timestamps. See issue 99.
type NormalValue ¶
NormalValue generates a normally distributed float64.
type Operation ¶
type Operation struct {
Service *Service
Name string
Ref string
Duration Distribution
ErrorRate float64
Calls []Call
CallStyle string
Attributes map[string]AttributeGenerator
QueueDepth int
Backpressure *ResolvedBackpressure
CircuitBreaker *ResolvedCircuitBreaker
}
Operation represents a resolved operation with pointers to downstream calls.
type OperationConfig ¶
type OperationConfig struct {
Name string
Domain string
Duration string
ErrorRate string
Calls []CallConfig
CallStyle string
Attributes map[string]AttributeValueConfig
QueueDepth int
Backpressure *BackpressureConfig
CircuitBreaker *CircuitBreakerConfig
}
OperationConfig describes an operation within a service.
type OperationState ¶
type OperationState struct {
ActiveRequests int
MaxQueueDepth int
BackpressureThreshold time.Duration
DurationMultiplier float64
ErrorRateAdd float64
RecentLatency time.Duration
BackpressureActive bool
FailureWindow []failureRecord
Circuit CircuitState
OpenedAt time.Duration
Cooldown time.Duration
FailureThreshold int
WindowDuration time.Duration
}
OperationState holds runtime state for a single operation across traces. Not safe for concurrent use. The engine calls all methods from a single goroutine.
func (*OperationState) Admit ¶
func (os *OperationState) Admit(elapsed time.Duration, rng *rand.Rand) (durationMult float64, errorRateAdd float64, rejected bool, reason string)
Admit checks operation state and returns adjustments for the current request. Mutates circuit breaker state (e.g. Open→HalfOpen transition on cooldown expiry). Returns the adjusted duration multiplier, additional error rate, and whether the request should be rejected outright.
func (*OperationState) Enter ¶
func (os *OperationState) Enter()
Enter increments the active request count.
type Override ¶
type Override struct {
Duration Distribution
ErrorRate float64
HasErrorRate bool
Attributes map[string]AttributeGenerator
AddCalls []Call
RemoveCalls map[string]bool
}
Override holds resolved per-operation overrides within a scenario.
func (Override) HasCallChanges ¶
HasCallChanges returns true if the override modifies the call graph.
type OverrideConfig ¶
type OverrideConfig struct {
Duration string `yaml:"duration,omitempty"`
ErrorRate string `yaml:"error_rate,omitempty"`
Attributes map[string]AttributeValueConfig `yaml:"attributes,omitempty"`
AddCalls []CallConfig `yaml:"add_calls,omitempty"`
RemoveCalls []RemoveCallConfig `yaml:"remove_calls,omitempty"`
}
OverrideConfig holds per-operation overrides within a scenario.
type RangeValue ¶
RangeValue generates a random int64 uniformly within [Min, Max].
type Rate ¶
type Rate struct {
// contains filtered or unexported fields
}
Rate represents requests per time period.
type RemoveCallConfig ¶
type RemoveCallConfig struct {
Target string `yaml:"target"`
}
RemoveCallConfig identifies a downstream call to remove by target reference.
func (*RemoveCallConfig) UnmarshalYAML ¶
func (r *RemoveCallConfig) UnmarshalYAML(unmarshal func(any) error) error
UnmarshalYAML handles both scalar string and mapping forms for remove call config.
type ResolvedBackpressure ¶
type ResolvedBackpressure struct {
LatencyThreshold time.Duration
DurationMultiplier float64
ErrorRateAdd float64
}
ResolvedBackpressure holds parsed backpressure settings for an operation.
type ResolvedCircuitBreaker ¶
type ResolvedCircuitBreaker struct {
FailureThreshold int
Window time.Duration
Cooldown time.Duration
}
ResolvedCircuitBreaker holds parsed circuit breaker settings for an operation.
type SampleDistribution ¶ added in v0.6.1
SampleDistribution collects per-trace metric values across sampled runs.
func (*SampleDistribution) Summary ¶ added in v0.6.1
func (d *SampleDistribution) Summary() (depth, spans, fanOut DistributionSummary)
Summary computes percentile summaries for each metric.
type SampleResults ¶ added in v0.6.0
type SampleResults struct {
MaxDepth int
MaxSpans int
MaxFanOut int
TracesRun int
Distribution SampleDistribution
}
SampleResults holds empirical measurements from sampled trace generation.
func SampleTraces ¶ added in v0.6.0
func SampleTraces(topo *Topology, n int, seed uint64, maxSpansPerTrace int) SampleResults
SampleTraces runs the engine n times with an in-memory exporter and measures empirical depth, fan-out, and span count. Observed span counts are bounded by maxSpansPerTrace (or DefaultMaxSpansPerTrace when 0), so for topologies whose static worst-case exceeds that limit, the observed value will plateau.
type Scenario ¶
type Scenario struct {
Name string
Start time.Duration
End time.Duration
Priority int
Overrides map[string]Override
Traffic TrafficPattern
}
Scenario is a resolved, time-windowed set of operation overrides.
func ActiveScenarios ¶
ActiveScenarios returns scenarios whose activation window contains the given elapsed time. Results are stable-sorted by priority (ascending) so higher-priority scenarios are processed last in ResolveOverrides and their values win.
func BuildScenarios ¶
func BuildScenarios(cfgs []ScenarioConfig, topo *Topology) ([]Scenario, error)
BuildScenarios converts scenario configs into resolved Scenarios. The topology is required to resolve add_calls targets to *Operation pointers.
type ScenarioConfig ¶
type ScenarioConfig struct {
Name string `yaml:"name"`
At string `yaml:"at"`
Duration string `yaml:"duration"`
Priority int `yaml:"priority,omitempty"`
Override map[string]OverrideConfig `yaml:"override,omitempty"`
Traffic *TrafficConfig `yaml:"traffic,omitempty"`
}
ScenarioConfig describes a time-windowed override to operation behaviour.
type SegmentConfig ¶
SegmentConfig describes a time-bounded rate segment in a custom traffic pattern.
type SequenceValue ¶
type SequenceValue struct {
Pattern string
// contains filtered or unexported fields
}
SequenceValue produces incrementing values by replacing {n} in a pattern.
type ServiceConfig ¶
type ServiceConfig struct {
Name string
Attributes map[string]string
Operations []OperationConfig
}
ServiceConfig describes a service in the topology.
type SimulationState ¶
type SimulationState struct {
// contains filtered or unexported fields
}
SimulationState tracks cross-trace state for operations during a run. Only operations with queue_depth, backpressure, or circuit_breaker config get an entry — unconfigured operations are unaffected.
State persists for the entire simulation, including across scenario boundaries. After a scenario ends, effects like open circuit breakers and backpressure remain until the system naturally recovers (e.g. cooldown expires, latency drops). This matches real-world behaviour where removing the cause of degradation does not instantly reset the symptoms.
func NewSimulationState ¶
func NewSimulationState(topo *Topology) *SimulationState
NewSimulationState builds state from topology operations that have queue depth, backpressure, or circuit breaker configuration.
func (*SimulationState) Get ¶
func (s *SimulationState) Get(ref string) *OperationState
Get returns the state for an operation, or nil if not tracked.
type SpanInfo ¶
type SpanInfo struct {
Service string
Operation string
Timestamp time.Time
Duration time.Duration
IsError bool
Kind trace.SpanKind
Attrs []attribute.KeyValue
Scenarios []string
}
SpanInfo holds span metadata for signal derivation.
type SpanObserver ¶
type SpanObserver interface {
Observe(info SpanInfo)
}
SpanObserver receives span metadata after each span is emitted.
type Stats ¶
type Stats struct {
Traces int64 `json:"traces"`
Spans int64 `json:"spans"`
Errors int64 `json:"errors"`
FailedTraces int64 `json:"failed_traces"`
Timeouts int64 `json:"timeouts"`
Retries int64 `json:"retries"`
SpansBounded int64 `json:"spans_bounded"`
QueueRejections int64 `json:"queue_rejections"`
CircuitBreakerTrips int64 `json:"circuit_breaker_trips"`
ElapsedMs int64 `json:"elapsed_ms"`
TracesPerSec float64 `json:"traces_per_second"`
SpansPerSec float64 `json:"spans_per_second"`
ErrorRate float64 `json:"error_rate"`
TraceErrorRate float64 `json:"trace_error_rate"`
}
Stats holds counters collected during a simulation run. Errors counts all spans in an error state, including those errored by cascading (a child failure marks its parent as errored too). ErrorRate is Errors/Spans. TraceErrorRate counts only traces where the root span errored.
type Topology ¶
Topology is the resolved service graph ready for simulation.
func BuildTopology ¶
func BuildTopology(cfg *Config, resolvers ...DomainResolver) (*Topology, error)
BuildTopology resolves a validated Config into a traversable Topology graph. cfg must have passed ValidateConfig first. Duration strings in backpressure and circuit breaker config are parsed without error checks here because validation has already rejected malformed values. An optional DomainResolver enables the domain field on operations.
type TracerSource ¶ added in v0.6.2
TracerSource returns a trace.Tracer for the named service. The engine calls this for every span, so implementations should be cheap (e.g. a map lookup or a method value on a single TracerProvider).
type TrafficConfig ¶
type TrafficConfig struct {
Rate string `yaml:"rate"`
Pattern string `yaml:"pattern,omitempty"`
BurstMultiplier float64 `yaml:"burst_multiplier,omitempty"`
BurstInterval string `yaml:"burst_interval,omitempty"`
BurstDuration string `yaml:"burst_duration,omitempty"`
PeakMultiplier float64 `yaml:"peak_multiplier,omitempty"`
TroughMultiplier float64 `yaml:"trough_multiplier,omitempty"`
Period string `yaml:"period,omitempty"`
Segments []SegmentConfig `yaml:"segments,omitempty"`
Overlay *TrafficConfig `yaml:"overlay,omitempty"`
}
TrafficConfig describes the traffic generation pattern.
type TrafficPattern ¶
TrafficPattern determines the trace generation rate at any given elapsed time.
func NewTrafficPattern ¶
func NewTrafficPattern(cfg TrafficConfig) (TrafficPattern, error)
NewTrafficPattern creates a TrafficPattern from configuration.
func ResolveTraffic ¶
func ResolveTraffic(active []Scenario) TrafficPattern
ResolveTraffic returns the traffic pattern from the highest-priority active scenario that has a traffic override, or nil if none do. Expects active to be sorted ascending by priority (as returned by ActiveScenarios).
type UniformPattern ¶
type UniformPattern struct {
BaseRate float64
}
UniformPattern generates a constant rate.
type WeightedChoice ¶
WeightedChoice picks from a set of values according to relative weights.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package traceimport infers a motel topology from recorded trace data.
|
Package traceimport infers a motel topology from recorded trace data. |