|
1 | 1 | # A property asserting intake for Datadog Agent-like artifacts |
2 | 2 |
|
3 | | -This intake asserts structural and aggregation properties on payloads from |
4 | | -Datadog Agent-like programs and is intended to mimic the constraints set by |
5 | | -Datadog Intake API. This implementation is forked from the other intake in this |
6 | | -project and may be later merged back up, although the goals to make aggregation |
7 | | -assertions here is invasive. |
| 3 | +This intake asserts structural properties on payloads from Datadog Agent-like programs and is intended |
| 4 | +to mimic the constraints set by Datadog Intake API. This implementation is forked from the other intake |
| 5 | +in this project and may be later merged back up, although the goals to make aggregation assertions here |
| 6 | +are invasive. |
8 | 7 |
|
9 | | -### How This Project Works |
| 8 | +## How this project works |
10 | 9 |
|
11 | | -This document is the 'specification' for an abstract DogStatsD Agent. We assert |
12 | | -that for any given input stream ADP emits to the intake data that is correctly |
13 | | -shaped and, in a future update, that the aggregation model of ADP is accurate to |
14 | | -the reference implementation of Datadog Agent DogStatsD. |
| 10 | +This document is the specification for an abstract DogStatsD Agent. We assert that for any given input |
| 11 | +stream ADP emits to the intake data that is correctly shaped and, in a future update, that the |
| 12 | +aggregation model of ADP is accurate to the reference implementation of Datadog Agent DogStatsD. |
15 | 13 |
|
16 | | -# Properties |
| 14 | +The differential scenario adds one narrower oracle. For the same generated configuration and workload, |
| 15 | +ADP and the Datadog Agent must eventually report the same metric contexts. |
17 | 16 |
|
18 | | -## Payloads |
| 17 | +## Properties |
19 | 18 |
|
20 | | -The Agent emits outputs to Datadog intake endpoints as payloads. The current |
21 | | -specification covers `/api/v2/series` only. The Agent also emits to |
22 | | -`/api/v3/series`, which a future revision will add. |
| 19 | +### Payloads |
23 | 20 |
|
24 | | -In this section we define properties that hold for `/api/v2/series` payloads |
25 | | -irrespective of load generation profile. Precisely, a 'payload' is an HTTP |
26 | | -envelope wrap around the compressed bytes of a |
| 21 | +The Agent emits outputs to Datadog intake endpoints as payloads. The current specification covers |
| 22 | +`/api/v2/series` only. The Agent also emits to `/api/v3/series`, which a future revision will add. |
| 23 | + |
| 24 | +In this section we define properties that hold for `/api/v2/series` payloads irrespective of load |
| 25 | +generation profile. Precisely, a payload is an HTTP envelope around the compressed bytes of a |
27 | 26 | [`MetricPayload`](https://github.com/DataDog/agent-payload/blob/0a5f9ebbbe9c2a1f1e671467511f6189d3a3b443/proto/metrics/agent_payload.proto#L30-L72). |
28 | 27 |
|
29 | | -Some properties reference rig-controlled parameters. `MaxTags(orgID)` and |
30 | | -`MaxResources(orgID)` are per-org caps with defaults 100 and 500 respectively. |
31 | | - |
32 | | -| Number | Category | Name | Description | |
33 | | -|--------|---------------|------------------------|----------------------------------------------------------------| |
34 | | -| Pyld01 | Envelope | Content-Type | `Content-Type` in `{application/x-protobuf, application/json}` | |
35 | | -| Pyld02 | Envelope | Content-Encoding | `Content-Encoding` in `{deflate, gzip, zstd, identity}` | |
36 | | -| Pyld03 | Envelope | API Key | `DD-Api-Key` header present and non-empty | |
37 | | -| Pyld05 | Bytes | Compressed Size | body < 500 KiB compressed | |
38 | | -| Pyld06 | Bytes | Uncompressed Size | body <= 5 MiB uncompressed | |
39 | | -| Pyld07 | MetricPayload | Decode | body decodes as v2 `MetricPayload` via `rust-protobuf`. | |
40 | | -| Pyld08 | MetricPayload | Point Count | total points <= configured `serializer_max_series_points_per_payload` | |
41 | | -| Pyld09 | MetricSeries | Metric Non-Empty | `MetricSeries.metric` is non-empty | |
42 | | -| Pyld10 | MetricSeries | Metric Length | `len(metric) <= 350` bytes | |
43 | | -| Pyld11 | MetricSeries | Metric Alphabetic | `metric` contains at least one ASCII alphabetic char | |
44 | | -| Pyld12 | MetricSeries | Type Enum | `type` in `{COUNT, RATE, GAUGE}` | |
45 | | -| Pyld13 | MetricSeries | Tag Count | `len(tags) <= MaxTags(orgID)` | |
46 | | -| Pyld14 | MetricSeries | Tag Prefix Reserved | no tag starts with `device:` or `dd.internal.resource:` | |
47 | | -| Pyld15 | MetricSeries | Per-Series Point Count | `len(points) <=` configured `serializer_max_series_points_per_payload` | |
48 | | -| Pyld16 | MetricSeries | Origin Populated | `origin.{product, category, service}` enum-valid | |
49 | | -| Pyld17 | Resource | Host Resource Resolved | every series resolves a non-empty `(type="host")` resource and all series in a payload share one host | |
50 | | -| Pyld18 | Resource | Resource Count | `len(resources) <= MaxResources(orgID)` | |
51 | | -| Pyld19 | Resource | Host Name Length | host `name <= 255` bytes | |
52 | | -| Pyld20 | MetricPoint | Value Not-NaN | `value` is not NaN | |
53 | | -| Pyld21 | MetricPoint | Timestamp Future Bound | `timestamp <= intake_now + 600s` | |
54 | | -| Pyld22 | Bytes | Content-Length | `Content-Length` absent or value equals body byte count | |
| 28 | +Some properties reference rig-controlled parameters. `MaxTags(orgID)` and `MaxResources(orgID)` are |
| 29 | +per-org caps with defaults 100 and 500 respectively. |
| 30 | + |
| 31 | +| Number | Category | Name | Description | |
| 32 | +| --- | --- | --- | --- | |
| 33 | +| Pyld01 | Envelope | Content-Type | `Content-Type` in `{application/x-protobuf, application/json}` | |
| 34 | +| Pyld02 | Envelope | Content-Encoding | `Content-Encoding` in `{deflate, gzip, zstd, identity}` | |
| 35 | +| Pyld03 | Envelope | API Key | `DD-Api-Key` header present and non-empty | |
| 36 | +| Pyld05 | Bytes | Compressed Size | body < 500 KiB compressed | |
| 37 | +| Pyld06 | Bytes | Uncompressed Size | body <= 5 MiB uncompressed | |
| 38 | +| Pyld07 | MetricPayload | Decode | body decodes as v2 `MetricPayload` via `rust-protobuf` | |
| 39 | +| Pyld08 | MetricPayload | Point Count | total points <= configured `serializer_max_series_points_per_payload` | |
| 40 | +| Pyld09 | MetricSeries | Metric Non-Empty | `MetricSeries.metric` is non-empty | |
| 41 | +| Pyld10 | MetricSeries | Metric Length | `len(metric) <= 350` bytes | |
| 42 | +| Pyld11 | MetricSeries | Metric Alphabetic | `metric` contains at least one ASCII alphabetic char | |
| 43 | +| Pyld12 | MetricSeries | Type Enum | `type` in `{COUNT, RATE, GAUGE}` | |
| 44 | +| Pyld13 | MetricSeries | Tag Count | `len(tags) <= MaxTags(orgID)` | |
| 45 | +| Pyld14 | MetricSeries | Tag Prefix Reserved | no tag starts with `device:` or `dd.internal.resource:` | |
| 46 | +| Pyld15 | MetricSeries | Per-Series Point Count | `len(points) <=` configured `serializer_max_series_points_per_payload` | |
| 47 | +| Pyld16 | MetricSeries | Origin Populated | `origin.{product, category, service}` enum-valid | |
| 48 | +| Pyld17 | Resource | Host Resource Resolved | every series resolves a non-empty `(type="host")` resource and all series in a payload share one host | |
| 49 | +| Pyld18 | Resource | Resource Count | `len(resources) <= MaxResources(orgID)` | |
| 50 | +| Pyld19 | Resource | Host Name Length | host `name <= 255` bytes | |
| 51 | +| Pyld20 | MetricPoint | Value Not-NaN | `value` is not NaN | |
| 52 | +| Pyld21 | MetricPoint | Timestamp Future Bound | `timestamp <= intake_now + 600s` | |
| 53 | +| Pyld22 | Bytes | Content-Length | `Content-Length` absent or value equals body byte count | |
| 54 | + |
| 55 | +### Differential context capture |
| 56 | + |
| 57 | +The differential scenario uses the same intake binary for both lanes: |
| 58 | + |
| 59 | +- Datadog Agent lane: `POST /api/v2/series` |
| 60 | +- ADP lane: `POST /api/v2/series` |
| 61 | +- Private control API: `GET /antithesis/metrics/agent` |
| 62 | +- Private control API: `GET /antithesis/metrics/adp` |
| 63 | + |
| 64 | +For context equivalence, a metric context is a `stele::MetricContext`: |
| 65 | + |
| 66 | +- metric name |
| 67 | +- canonical tag list |
| 68 | + |
| 69 | +The intake folds each captured metric down to its canonical `stele::MetricContext` and stores the |
| 70 | +deduplicated set per lane, but it does not compare them. The control API returns those context |
| 71 | +sets. The differential workload command fetches both sets and owns the Antithesis assertion for |
| 72 | +eventual equivalence. |
| 73 | + |
| 74 | +The context oracle intentionally does not assert aggregate values, sketch values, event payloads, or |
| 75 | +service-check payloads. Those remain covered by the normal workload generation and payload structural |
| 76 | +assertions rather than by the context-equivalence check. |
0 commit comments