Dataway Tail Sampling¶
Introduction¶
Dataway provides tail-sampling APIs:
/v1/tail_sampling(raw payload; also accepts headerless zstd payloads from Datakit 2.10)/v1/tail_sampling_v2(legacy raw compatibility path)/v2/tail_sampling(zstd payload)/v1/tail_sampling_config
Tail sampling receives grouped data on Dataway first, applies sampling rules, and sends the kept data upstream.
Three data types are currently supported:
tracingloggingrum
The basic flow is:
sequenceDiagram
autonumber
participant dk as Datakit/Client
participant dw as Dataway
participant ts as TailSamplingProcessor
participant kodo as Kodo
dk ->> dw: POST /v2/tail_sampling (zstd) or /v1/tail_sampling (raw)
alt config ready
dw ->> ts: ingest packet
ts ->> dw: kept packets
dw ->> kodo: write tracing/logging/rum
else config not ready
dw ->> dw: pending cache
dw -->> dk: 412 Precondition Failed
dk ->> dw: POST /v1/tail_sampling_config
dw ->> ts: update config and drain pending
end
Working Modes¶
Tail sampling uses the same mode settings as aggregate:
standaloneproxy
standalone¶
In standalone mode, the current Dataway handles tail-sampling data directly:
- receive protobuf-encoded
aggregate.DataPacket - look up sampling config by
token + data_type - ingest the packet into
TailSamplingProcessorwhen config is ready - periodically flush expired groups to the target write API
In the current implementation:
- the sampling loop advances every 1 second
- derived metrics are flushed every 1 minute
- a worker pool is used for async delivery
proxy¶
In proxy mode, the current Dataway does not keep local tail-sampling state:
/v1/tail_sampling,/v1/tail_sampling_v2, and/v2/tail_samplingare forwarded to backend nodes/v1/tail_sampling_configis broadcast to all backend nodes
That means in proxy mode:
aggregator_endpointis required- the client must send a valid
Guance-Pick-Key - backend nodes keep the actual sampling state
Warning
In Kubernetes, if the front Dataway needs to forward tail-sampling requests to fixed backend nodes, aggregator_endpoint must contain stable backend addresses. Backend Dataway nodes should be deployed with StatefulSet so each Pod keeps a stable address and DNS name for deterministic forwarding.
Local Configuration¶
There is no separate local YAML section for tail sampling. Dataway uses the same mode settings as aggregate:
Environment variables:
Notes:
standalone: the current node keeps tail-sampling state locallyproxy: the current node only forwards or broadcasts
In Kubernetes, when a front Dataway acts as the ingress layer and backend Dataway nodes perform the actual tail sampling, StatefulSet is the better backend deployment model and its stable Pod addresses should be used in aggregator_endpoint.
Sampling Config Delivery¶
Sampling rules are delivered by API instead of being written in dataway.yaml:
The request body is JSON with this top-level structure:
Where:
traceis the tracing tail-sampling configloggingis the logging tail-sampling configrumis the RUM tail-sampling config
Tracing Example¶
{
"version": 1,
"trace": {
"version": 1,
"data_ttl": "5m",
"group_key": "trace_id",
"pipelines": [
{
"name": "keep-all",
"type": "probabilistic",
"rate": 1
}
],
"builtin_metrics": [
{
"name": "trace_total_count",
"enabled": true
}
]
}
}
Notes:
trace.group_keycurrently only supportstrace_idtrace.data_ttldefaults to5mwhen emptypipelinessupportconditionandprobabilisticconditionusesaction=keep/dropprobabilisticusesrate=0~1
Logging Example¶
{
"version": 1,
"logging": {
"version": 1,
"data_ttl": "1m",
"group_dimensions": [
{
"group_key": "service",
"pipelines": [
{
"name": "keep-all",
"type": "probabilistic",
"rate": 1
}
]
}
]
}
}
RUM Example¶
{
"version": 1,
"rum": {
"version": 1,
"data_ttl": "1m",
"group_dimensions": [
{
"group_key": "session_id",
"pipelines": [
{
"name": "keep-all",
"type": "probabilistic",
"rate": 1
}
]
}
]
}
}
Info
logging and rum use group_dimensions for grouping. When data_ttl is empty, both default to 1m.
Warning
The current implementation validates the config. trace only allows group_key=trace_id, and derived_metrics is not supported yet.
Data APIs¶
Tail-sampling data APIs:
Notes:
/v1/tail_samplingaccepts an uncompressed PBPoints payload; as a narrow compatibility exception, it also accepts a zstd packet from Datakit 2.10 when the negotiation header is absent andPayloadCompression=1/v1/tail_sampling_v2is a legacy raw compatibility path and uses the same handler as/v1/tail_sampling; it is not the zstd protocol path/v2/tail_samplingaccepts only zstd payloads; each request must include both:- header
Guance-Tail-Sampling-Payload-Compression: zstd aggregate.DataPacket.PayloadCompression=1- in
standalonemode, the request body must be protobuf-encodedaggregate.DataPacket - in
proxymode, the request is forwarded to a backend node
Clients should use one of the following protocol combinations:
| Client scenario | Request path | Compression negotiation header | PayloadCompression |
Payload |
|---|---|---|---|---|
| Datakit versions earlier than 2.10 | /v1/tail_sampling |
none | 0 |
raw PBPoints |
| Datakit 2.10 compatibility path | /v1/tail_sampling |
none | 1 |
zstd PBPoints |
| Default path for Datakit 2.11 and later | /v2/tail_sampling |
zstd |
1 |
zstd PBPoints |
| Fallback path for Datakit 2.11 and later | /v1/tail_sampling |
none | 0 |
raw PBPoints |
Common response status codes:
| Status | Meaning |
|---|---|
200 |
the packet was accepted |
400 |
the protobuf, PBPoints payload, or packet fields are invalid |
412 |
the sampling configuration is not ready, but the packet was stored in the pending cache |
413 |
the request body exceeds the Dataway size limit |
415 |
the compression method is unsupported, or the path, header, and packet compression field do not match |
503 |
the pending cache is full and the packet was not accepted |
Compression Protocol And Rolling Upgrades¶
Datakit 2.11 and later send zstd payloads to /v2/tail_sampling by default. Dataway validates the path, header, and packet compression field explicitly:
- an old Dataway does not recognize
/v2/tail_samplingand returns404 - a new Dataway returns
415 Unsupported Media Typefor an unsupported compression method, a missing header, a raw v2 packet, or a v1 zstd packet that does not match the Datakit 2.10 compatibility exception - after receiving
404or415, Datakit converts the current packet back to raw, retries/v1/tail_sampling, and caches the endpoint's legacy capability for 10 minutes - before fallback, Datakit splits the packet by its decoded protobuf body size so every splittable raw packet stays within Dataway's
MaxRawBodySize; this prevents a highly compressible packet from being rejected by an old node with413 - Datakit 2.10 sends zstd packets directly to
/v1/tail_samplingwithout a negotiation header; a new Dataway accepts this exact combination for compatibility with that released version - Datakit versions earlier than 2.10 continue sending raw v1 data; a new Dataway computes missing span predicates and compresses the payload before ingestion when compression is beneficial
With both compatibility paths available, Datakit 2.11+ and the new Dataway can be rolled out in either order. Datakit 2.10 remains incompatible with an old Dataway that lacks this exception, so one side must first be upgraded to a version that provides the compatibility bridge.
Kept-packet Delivery And Shutdown Recovery¶
Dataway sends packets that have been kept by the sampling decision through a bounded worker pool and a disk overflow queue:
- a failed send is retried with backoff up to 3 times; only exhausted retries count as
failure/drop - when the memory queue is full, packets enter an asynchronous overflow channel and are then persisted to disk; Dataway opens an existing queue at startup and resumes delivery
- during shutdown, packets in overflow, the memory queue, and retry backoff are persisted to a healthy disk first
- when disk persistence is unavailable, shutdown network fallback retries share a 5-second budget; packets left after the budget are counted explicitly as failures and summarized in the log
412 And Pending Cache¶
In standalone mode, if Dataway has just started and the sampling config for the current token + data_type has not arrived yet:
- Dataway stores the packet in the local pending cache first
- then returns
412 Precondition Failed
Current behavior:
- the pending cache is in memory
- packets are grouped by
token + data_type - after config delivery succeeds, matching packets are drained into
TailSamplingProcessor - the current default limit is
100000packets
Expected client behavior:
- when the client receives
412, it should treat the packet as already accepted by Dataway - the client should continue sending
/v1/tail_sampling_config - the client should not resend the same data packet
Failure case:
- if the pending cache is full, Dataway returns
503 - in that case the request must not be treated as accepted
Tail-sampling Metric Set (tail_sampling)¶
Tail-sampling config supports builtin_metrics. These metrics are generated by the tail-sampling processor itself and flushed upstream periodically. Once flushed, the measurement (metric set) name is tail_sampling — for example, query field trace_dropped_count with tags stage/decision/data_type in Guance Cloud.
The current built-in metrics are:
tracing¶
trace_total_counttrace_kept_counttrace_dropped_counttrace_error_countspan_total_counttrace_duration
Where:
trace_durationis a duration-distribution metric- the others are count metrics
logging¶
logging_total_countlogging_error_countlogging_kept_countlogging_dropped_count
rum¶
rum_total_countrum_kept_countrum_dropped_count
Notes:
- when
builtin_metricsis empty, all supported built-in metrics for that data type are enabled by default - these metrics come from the sampling process itself, not from Dataway runtime observation
Automatic Dataway Metrics (metric set dataway_aggregate)¶
Besides sampling builtin_metrics (written to the tail_sampling metric set), apis/metrics_special.go also tracks Dataway runtime metrics for the tail-sampling APIs. These metrics are aggregated into the dataway_aggregate metric set with field prefix dataway_http_tail_sampling_*.
The current tail-sampling-related metrics are:
| Metric | Type | Labels | Description |
|---|---|---|---|
dataway_http_api_body_size_bytes_total |
Counter | api, token |
accumulated request body bytes for tail-sampling APIs |
dataway_http_tail_sampling_trace_total |
Counter | token |
received tracing group count |
dataway_http_tail_sampling_span_total |
Counter | token |
received tracing span count |
dataway_http_tail_sampling_packet_stage_total |
Counter | token, data_type, stage, result |
packet count by stage (receive/ingest/decision/submit/kodo) and result |
dataway_http_tail_sampling_point_stage_total |
Counter | token, data_type, stage, result |
point count by stage and result |
dataway_http_tail_sampling_rule_packet_total |
Counter | token, data_type, rule_name, rule_index, rule_type, action, result |
packet count by matched sampling rule and decision |
dataway_http_tail_sampling_rule_point_total |
Counter | same as above | point count by matched rule and decision |
dataway_http_tail_sampling_packet_send_total |
Counter | token, data_type, result |
send result count, where result includes success, failure, and drop |
dataway_http_tail_sampling_submit_queue_event_total |
Counter | token, data_type, result |
submit queue enqueue result (memory/wait/overflow/disk/drop/closed) |
dataway_http_tail_sampling_submit_queue_depth |
Gauge | - | current submit queue depth (backlog) |
dataway_http_tail_sampling_submit_queue_capacity |
Gauge | - | submit queue capacity |
dataway_http_tail_sampling_submit_worker_total |
Gauge | - | current worker count |
dataway_http_tail_sampling_submit_worker_busy |
Gauge | - | current busy worker count |
dataway_http_tail_sampling_submit_disk_depth |
Gauge | - | disk overflow queue depth |
dataway_http_tail_sampling_submit_queue_wait_seconds |
Summary | source |
queue wait duration before subsequent handling (source: memory/wait/overflow/disk) |
These metrics are:
- gathered every 1 minute
- converted into
dataway_aggregatepoints - sent to
/v1/write/metricwith the default Dataway token - reset after a reporting round
- the
tokenlabel is always set toredacted; no part of the original token is retained
These metrics describe Dataway's own tail-sampling runtime behavior rather than the business-level sampling outcome.
Division of labor between the two central metric sets:
tail_sampling: business-level sampling statistics (one copy per token), such astrace_kept_count/trace_dropped_countdataway_aggregate: Dataway processing runtime status (aggregated under the default Dataway token), such as per-stage counters, send/backlog, and worker state