Skip to content
Topology YAML Reference

Topology YAML Reference

A topology file declares the synthetic microservice topology that xk6-otel-gen uses to generate OpenTelemetry signals. This document lists every field you can set in YAML.

Top level

KeyTypeRequiredDefaultDescription
namespacestringnoxk6-otel-genDefault service.namespace for all services; overridable per service.
servicesmapyesMap of service identifier → service declaration. At least one required.
journeysmapyesMap of journey name → user-action sequence. At least one required.
faultslistno[]Ordered array of fault injection specs.
namespace: shop            # optional; defaults to xk6-otel-gen
services: { ... }          # required
journeys: { ... }          # required
faults: [ ... ]            # optional

Selected validation rules:

  • services and journeys must each contain at least one entry.
  • The operation call graph must be a DAG (acyclic); cycles are a validation error.
  • Every call target, journey step, and fault target must reference a service / operation / edge that actually exists in the schema.

Export the JSON Schema for editor integration:

go run ./cmd/xk6-otel-gen-schema > topology.schema.json
go run ./cmd/xk6-otel-gen-schema -output topology.schema.json

services

services maps a service identifier (the map key) to a service declaration. Each service owns one or more operations, and each operation may make outgoing calls (edges) to other services.

Configurable fields (overview)

Service (services.<id>)

FieldTypeRequiredDefaultDescription
kindenumyesService category. application / database / external_api / cache / queue
operationslistyesOperations owned by the service (at least one)
namespacestringnotop-level namespaceservice.namespace override for this service
replicasintno1Number of instances to synthesize (>= 1)
languagestringnoImplementation language metadata
frameworkstringnoFramework metadata
versionstringnoVersion metadata
metricslistno[]Service-scoped observable custom metrics (ObservableMetric)

Operation (operations[])

FieldTypeRequiredDefaultDescription
namestringyesName unique within the service (1–120 bytes)
callslistno[]Ordered outgoing calls made by this operation (CallNode)
log_eventslistno[]Structured log events emitted when this operation completes (LogEvent)
metricslistno[]Custom metric data points recorded when this operation completes (Metric)
state_updateslistno[]Accumulator updates for service-scoped observable metrics
profileobjectnoSynthetic flamegraph pushed to Pyroscope (Profile)

Call (CallNode — items of calls[] / parallel[])

Each item is either a single edge or a parallel group (mutually exclusive).

FieldTypeRequiredDefaultDescription
toobjectyes for an edgeCall target { service, operation }
protocolenumyesTransport protocol. http / grpc / messaging
latencyobjectnosee LatencyDist belowLatency distribution
error_ratenumberno0.0Failure probability [0,1]
timeoutdurationno0 (unlimited)Per-attempt timeout
retriesintno0Retry count (>= 0)
retry_backoffenumnoexponentialRetry delay strategy. exponential / linear / constant
retry_base_delaydurationno100msBase retry delay
on_failureobjectnoFallback policy on failure (RecoveryPolicy)
parallellistyes for a groupChild CallNodes that run concurrently (at least one)

LatencyDist (latency)

FieldTypeRequiredDefaultDescription
distributionenumnoconstantconstant / lognormal / normal / exponential
p50durationno0Median (50th percentile)
p95durationnosame as p5095th percentile (must be >= p50)

RecoveryPolicy (on_failure)

FieldTypeRequiredDefaultDescription
fallbacklistno[]Ordered fallback calls to try (CallNode)
on_exhaustedenumnopropagateAction after all fallbacks fail. propagate / return_default / succeed_silently
default_responseobjectnoSynthetic response returned with return_default (arbitrary keys)

Field details

kind (required)

The semantic service category. Allowed values: application, database, external_api, cache, queue. Reflected in generated span kinds and resource attributes.

operations (required)

The array of callable units the service exposes (endpoints, RPC methods, message handlers). At least one is required.

namespace

Overrides this service’s service.namespace, taking precedence over the top-level default.

replicas

Number of service instances to synthesize. Must be >= 1; defaults to 1.

language / framework / version

Metadata (implementation language, framework, version) attached as resource attributes and used to classify the generated telemetry.

operations[].name (required)

An operation name unique within the service. Must be a non-empty string of 1–120 bytes.

operations[].calls

The ordered list of outgoing calls this operation makes. Each item is a CallNode: either an edge or a parallel group.

CallNode: edge

A directed call to another operation. to is required and is mutually exclusive with parallel.

calls:
  - to: { service: payment, operation: authorize_card }
    protocol: grpc
    latency: { distribution: lognormal, p50: 20ms, p95: 200ms }
    error_rate: 0.02
    timeout: 750ms
    retries: 2
    retry_backoff: exponential
    retry_base_delay: 100ms
  • to — the target { service, operation }. Both required; must point to an existing operation.
  • protocol — one of http / grpc / messaging. Must be specified. For messaging, the sender emits a PRODUCER (publish) span and the receiver a CONSUMER (receive) span that is linked back to it with a span link, within the same journey trace.
  • latency — latency distribution (see below).
  • error_rate — failure probability of this call. [0,1]. Default 0.0.
  • timeout — upper bound for one attempt; if simulated latency exceeds it, the attempt is treated as a timeout failure. 0 (default) means unlimited.
  • retries — retry count on failure. >= 0. Default 0.
  • retry_backoff — how the retry interval grows. exponential (default) / linear / constant.
  • retry_base_delay — base retry delay. Default 100ms.
  • on_failure — fallback policy (RecoveryPolicy, below).

CallNode: parallel group

Runs child CallNodes concurrently. parallel is required and mutually exclusive with to. Nestable.

calls:
  - parallel:
      - to: { service: inventory, operation: check_stock }
        protocol: grpc
      - to: { service: pricing, operation: get_price }
        protocol: grpc

LatencyDist (latency)

Describes a call’s latency distribution.

  • distributionconstant (default) / lognormal / normal / exponential.
  • p50 — median. Default 0.
  • p95 — 95th percentile. Defaults to p50. Must be >= p50.

A duration may be a Go-style string (e.g. 10ms, 1s) or a nanosecond integer.

RecoveryPolicy (on_failure)

Defines fallback behavior when an edge fails.

  • fallback — ordered alternative calls to try (a list of CallNodes). Each fallback must belong to the same caller (from) as the original edge.
  • on_exhausted — action after all fallbacks fail.
    • propagate (default) — propagate the error to the caller.
    • return_default — return default_response.
    • succeed_silently — suppress the error and treat as success.
  • default_response — synthetic response returned with return_default (an object with arbitrary keys).
calls:
  - to: { service: payment, operation: authorize_card }
    protocol: grpc
    on_failure:
      fallback:
        - to: { service: payment-backup, operation: authorize_card }
          protocol: grpc
      on_exhausted: return_default
      default_response: { status: "queued" }

operations[].log_events

Structured log events emitted when the operation completes, in addition to the generic per-operation log. Each item becomes one OTLP log record whose event.name is set (and also attached as an event.name attribute).

FieldTypeRequiredDefaultDescription
namestringyesEvent name; emitted as event.name
severityenumnoinfotrace / debug / info / warn / error / fatal
conditionenumnoalwaysWhen to emit. always / on_success / on_error
bodystringnoLog record body
attributesmapnoExtra structured attributes (arbitrary keys)
operations:
  - name: authorize_card
    log_events:
      - name: provider_call.timeout
        severity: error
        condition: on_error
        body: "payment provider call timed out"
        attributes: { provider: stripe, retryable: true }

This powers LogQL such as {service_name="payment"} | event_name="provider_call.timeout".

services.<id>.metrics

Service-scoped observable metrics are collected by the OTel SDK callback path, not when an operation completes. They are useful for values owned by a service or dependency node that is not naturally an operation, such as queue consumer lag.

FieldTypeRequiredDefaultDescription
namestringyesInstrument name
typeenumyesobservable_gauge / observable_counter
unitstringnoUCUM-style unit
baselinenumberno0Base value before source/fault adjustment
attributesmapnoExtra data-point attributes
sourceobjectnoAccumulator source { accumulator, minus? }
when_faultobjectnoDeterministic service fault adjustment

source.minus is valid only for observable_gauge. observable_counter sources are cumulative and use a single accumulator key.

services:
  kafka:
    kind: queue
    metrics:
      - name: kafka.consumer.lag
        type: observable_gauge
        unit: "{message}"
        source:
          accumulator: kafka.orders.produced
          minus: kafka.orders.consumed
    operations:
      - name: publish_order
        state_updates:
          - key: kafka.orders.produced
            delta: 1
            condition: on_success
          - key: kafka.orders.consumed
            delta: 0.8
            condition: on_success

when_fault on service metrics is evaluated only for deterministic service faults (target: node:<service> with probability >= 1). Per-iteration random fault state is intentionally not read from OTel callbacks.

operations[].metrics

Custom metric data points recorded when the operation completes. Optionally fault-linked: while the referenced fault kind is active on this operation, the recorded value becomes baseline + delta (or is overridden by value).

FieldTypeRequiredDefaultDescription
namestringyesInstrument name
typeenumyescounter / gauge / histogram
unitstringnoUCUM-style unit (e.g. {request})
baselinenumberno0Value recorded (counter: amount added; gauge/histogram: value)
conditionenumnoalwaysWhen to record. always / on_success / on_error
attributesmapnoExtra data-point attributes
when_faultobjectnoFault linkage (below)

when_fault

FieldTypeRequiredDefaultDescription
kindenumyesFault kind to react to. latency_inflation / error_rate_override / disconnect / crash
deltanumberno0Added to baseline while the fault is active on this operation
valuenumbernoOverrides the value (instead of delta) while the fault is active
operations:
  - name: quote_shipping
    metrics:
      - name: shipping.quote.backlog
        type: gauge
        unit: "{request}"
        baseline: 5
        when_fault:
          kind: latency_inflation
          delta: 40

The fault linkage is evaluated against the same fault state applied to the operation, so the value moves deterministically when the fault fires. A counter fed on condition: on_success plus OTLP cumulative temporality yields a continuously growing total (e.g. a settlement amount).

operations[].state_updates

State updates increment process-wide accumulators after an operation completes. Observable service metrics can read these values on each collection interval.

FieldTypeRequiredDefaultDescription
keystringyesAccumulator key
deltanumberno0Amount added to the accumulator
conditionenumnoalwaysalways / on_success / on_error
when_faultobjectnoAdjust the delta while the operation fault is active

For when_fault, delta is added to the normal update amount; value overrides the update amount for that operation completion.

operations[].profile

A synthetic flamegraph for the operation, pushed to Pyroscope as pprof when profilesEndpoint is set (otherwise it is a no-op). Two stack-set variants enable diff flamegraphs: while the linked fault kind is active, the incident stacks are emitted instead of baseline. The operation span carries a pyroscope.profile.id attribute (equal to its span id) for Span→Profiles correlation.

FieldTypeRequiredDefaultDescription
enabledboolnofalseWhether to emit a profile for this operation
sample_rateintno100Sample rate in Hz
baselinelistyes when enabledStack samples for normal runs (StackSample)
incidentlistrequired with when_faultStack samples emitted while the linked fault is active
when_faultobjectno{ kind } — fault kind that selects the incident variant

StackSample (baseline[] / incident[] items)

FieldTypeRequiredDefaultDescription
frameslist of stringyesCall stack frames, ordered root → leaf
weightnumberno0Self weight of the stack (e.g. sample count)
operations:
  - name: quote_shipping
    profile:
      enabled: true
      sample_rate: 100
      baseline:
        - frames: ["handleQuoteShipping", "calcBaseRate"]
          weight: 120
      incident:
        - frames: ["handleQuoteShipping", "calcBaseRate", "geoLookup", "matrixSolve"]
          weight: 900
      when_fault:
        kind: latency_inflation

journeys

journeys maps a journey name to a sequence of user actions. Each journey execution produces one synthetic trace.

Configurable fields (overview)

Journey (journeys.<name>)

FieldTypeRequiredDefaultDescription
stepslistyesOrdered steps (at least one)
weightnumberno1Relative selection weight for runRandomJourney() (> 0)

Step (items of steps[] / parallel[])

Each item is either a single operation or a parallel group (mutually exclusive).

FieldTypeRequiredDefaultDescription
servicestringyes for a single stepEntry service
operationstringyes for a single stepEntry operation
parallellistyes for a groupChild steps that run concurrently (at least one)

Field details

steps (required)

The ordered list of steps that make up the journey. At least one is required. Each step is either a single operation invocation or a parallel group.

weight

The relative weight used when runRandomJourney() selects a journey. Must be

0; defaults to 1.0.

journeys:
  browse:
    weight: 4.0
    steps:
      - service: frontend
        operation: browse_home
  checkout:
    weight: 1.0
    steps:
      - service: frontend
        operation: view_cart
      - service: frontend
        operation: checkout

Step: single operation

service and operation specify the entry point. Both required; mutually exclusive with parallel. Must point to an existing operation.

Step: parallel group

parallel runs multiple child steps concurrently. Mutually exclusive with service / operation; nestable.

steps:
  - parallel:
      - service: frontend
        operation: load_recommendations
      - service: frontend
        operation: load_banner

faults

faults declares faults to inject during synthesis as an ordered array. Each fault has a target, a kind, and severity parameters.

Configurable fields (overview)

Fault (faults[])

FieldTypeRequiredDefaultDescription
targetstringyesTarget in node: / operation: / edge: form
kindenumyesFault kind. latency_inflation / error_rate_override / disconnect / crash
severityobjectnoSeverity parameters (below)
schedulelistno[]Elapsed-time intensity schedule for this fault

SeverityParams (severity)

FieldTypeRequiredDefaultDescription
probabilitynumberno0Probability the fault fires [0,1]
multipliernumberrequired for latency_inflation0Latency multiplier (> 0)
adddurationno0Fixed delay to add (latency_inflation)
valuenumberused by error_rate_override0Overriding error rate [0,1]

FaultSchedulePoint (schedule[])

FieldTypeRequiredDefaultDescription
atdurationno0sElapsed time from engine start
intensitynumberno1Non-negative intensity active at and after at

Field details

target (required)

The fault target, as a string in one of three forms.

Target syntaxScope
node:<svc>all operations on one service
operation:<svc>.<op>one service operation
edge:<from_svc>.<from_op>-><to_svc>.<to_op>one call edge

The referenced service / operation / edge must exist in the schema.

kind (required)

The type of fault to inject.

  • latency_inflation — increases latency. With add (fixed) and multiplier, it adds add + (multiplier - 1) × base latency. multiplier must be > 0.
  • error_rate_override — overrides the target’s error rate with value (clamped to [0,1]).
  • disconnect — injects a connection error (disconnect).
  • crash — injects a crash.

severity

Severity parameters. Which fields apply depends on kind.

kindseverity fields used
latency_inflationprobability, multiplier (required, > 0), add (optional)
error_rate_overrideprobability, value
disconnectprobability
crashprobability
  • probability — probability the fault fires per call. [0,1].
  • multiplier — latency multiplier (latency_inflation). > 0.
  • add — fixed delay to add (latency_inflation).
  • value — overriding error rate (error_rate_override); clamped to [0,1].

The effective runtime intensity multiplies probability and value. For latency_inflation, it also scales the configured add and (multiplier - 1) amplitude after the fault activates. For example, intensity 0.5 halves both the activation probability and the added latency magnitude.

schedule

schedule declares a per-fault intensity timeline. Points must be in strictly increasing at order, and intensity must be finite and >= 0. The engine evaluates the schedule as a step function: before the first point intensity is 0, then the most recent point at or before the elapsed engine time applies. The schedule does not consume random values; it only changes the effective probability, error-rate override, and latency amplitude applied to the seeded fault decisions.

If JavaScript calls handle.setFaultIntensity(target, x), that target-specific override takes precedence over the YAML schedule. The global handle.setFaultIntensity(x) is used only for faults without a schedule or target override.

faults:
  - target: node:payment
    kind: latency_inflation
    severity: { probability: 0.20, multiplier: 3.0, add: 50ms }
  - target: operation:checkout.place_order
    kind: error_rate_override
    severity: { probability: 1.0, value: 0.05 }
    schedule:
      - at: 0s
        intensity: 0
      - at: 1m
        intensity: 1
      - at: 3m
        intensity: 0
  - target: edge:frontend.checkout->payment.authorize_card
    kind: disconnect
    severity: { probability: 0.01 }
  - target: operation:cart.get_cart
    kind: crash
    severity: { probability: 0.005 }