Skip to content

rw2: add stateless and stateless-typed modes (rc.6) - #3142

Open
bwplotka wants to merge 3 commits into
mainfrom
bwplotka/rw2-rc6-two-modes
Open

bwplotka wants to merge 3 commits into
mainfrom
bwplotka/rw2-rc6-two-modes

Conversation

@bwplotka

@bwplotka bwplotka commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Alternative to #3129.

Specify two progressive modes (stateless and stateless-typed) for io.prometheus.write.v2.Request in Remote-Write 2.0-rc.6, following the Prometheus Dev Summit consensus and RFC 9413.

By default, when no ;mode= parameter is set on Content-Type: application/x-protobuf;proto=io.prometheus.write.v2.Request, all field permutations permitted by the base specification may be sent. 2.0-rc.6 adds two optional, progressive modes where senders MUST guarantee:

  1. stateless mode (Content-Type: application/x-protobuf;proto=io.prometheus.write.v2.Request;mode=stateless):
    • Exemplars are attached to a TimeSeries that contains at least one sample or histogram, so no exemplar-only TimeSeries are sent.
    • Histograms use the Histogram message, so classic histograms are sent as NHCB instead of _bucket, _sum, and _count float series.
  2. stateless-typed mode (Content-Type: application/x-protobuf;proto=io.prometheus.write.v2.Request;mode=stateless-typed):
    • All requirements of stateless mode.
    • Metadata.type is set and not METRIC_TYPE_UNSPECIFIED.
    • start_timestamp is set (non-zero) on every sample and histogram for cumulative and delta metric types (METRIC_TYPE_COUNTER, METRIC_TYPE_HISTOGRAM, and METRIC_TYPE_SUMMARY _sum and _count).

Both modes are optional for senders and receivers and remain valid io.prometheus.write.v2.Request payloads on the wire.

Why two progressive modes instead of one mode or separate capabilities?

  • stateless rules, attaching exemplars to samples or histograms and converting classic histograms to NHCB, are sender-side formatting choices. Any sender such as Prometheus or Alloy can enforce them by configuration alone without dropping data, even when scraping legacy targets.
  • stateless-typed rules, Metadata.type and start_timestamp, require every series to have a known metric type and counter start time. When scraping targets that expose untyped metrics or omit _created timestamps, a sender can only satisfy stateless-typed by synthesizing state, inferring types, or dropping non-conforming series.
  • stateless-typed includes stateless because receivers that require explicit metric types and start timestamps also require structured histograms and attached exemplars, and validating start_timestamp already requires knowing Metadata.type.
  • Progressive modes (default < stateless < stateless-typed) give 3 well-defined protocol states rather than $2^N$ capability combinations (RFC 9413), keep Content-Type matching to a single parameter value, and keep prometheus/compliance testing linear.
[ENHANCEMENT] Remote-Write 2.0: Add optional `stateless` and `stateless-typed` modes (`rc.6`).

@bwplotka
bwplotka requested a review from krajorama October 16, 2026 14:14
@bwplotka
bwplotka force-pushed the bwplotka/rw2-rc6-two-modes branch from a080d34 to 1411e64 Compare October 16, 2026 15:49
@dashpole
dashpole self-requested a review October 16, 2026 16:02
@bwplotka
bwplotka force-pushed the bwplotka/rw2-rc6-two-modes branch 2 times, most recently from 8db2179 to 59397b4 Compare October 19, 2026 11:35
Define two progressive modes for io.prometheus.write.v2.Request: stateless (requiring Exemplars attached to samples/histograms and Histogram messages for histograms) and stateless-typed (extending stateless with mandatory Metadata.type and start_timestamp on cumulative/delta metric types).

Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
@bwplotka
bwplotka force-pushed the bwplotka/rw2-rc6-two-modes branch from 59397b4 to b865bfd Compare October 19, 2026 11:52
Comment thread docs/specs/prw/remote_write_spec_2_0.md Outdated
#### Invalid Samples

Receivers MAY NOT support certain metric types or samples (e.g. a Receiver might reject sample without metadata type specified or without start timestamp, while another Receiver might accept such sample.). It’s up to the Receiver what sample is invalid. Receivers MUST return a [400 HTTP Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) status code for write requests that contain any invalid samples unless the [partial retriable write](#retries-on-partial-writes) occurs.
Receivers SHOULD reject samples that violate this specification or the negotiated [mode](#modes). Receivers MAY reject samples that violate receiver-specific limits and gaps (such as not supporting native histograms, or label count or length limits). Receivers MUST return a [400 HTTP Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) status code for write requests that contain any invalid samples unless the [partial retriable write](#retries-on-partial-writes) occurs.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to address here some discussions points of @krajorama

1. Receiver strict validation of spec and mode (IMO "SHOULD" not "MUST")

The existing spec was relaxed on this. Receiver are not responsible for validating the spec (e.g. ST on gauges), although they MAY. Somewhat exception are core elements like Sample, Histogram and Example (response headers are MUST).

Rationales for keeping MAY for both spec and mode validation:

a. Forward compatibility:

  • Proto allows us to add new fields etc. Expecting receiver to validate would mean new fields would be a breaking change
  • Similarly we wouldn't be able to add new modes as technically old receivers would need to add special validation per mode. Our goal is to ensure generic receiver implementation is possible - one that can support all modes similar to HTTP Media Type Profiles (RFC 6906):

A profile parameter on Content-Type constrains what the sender puts on the wire so profile-aware receivers can rely on it, while explicitly guaranteeing that any generic receiver of the base media type can accept the payload even if it ignores the profile's extra semantics.

b. Makes RW2 adoption on receive harder (e.g. my RW2 sink that only checks certain things is not "compliant").
c. Efficiency - burning CPU on things you don't care about?
d. Adoption: If client ever supports stateless - receiver might want to accept it and partially reject untyped metrics.

As a result, it's all SHOULD here.

2. Receiver accepting a mode means it will use all it's data (IMO that's explicitly NOT an intention)

We mentioned this concept that sender might want to know that e.g. if ST is sent and stateless-typed is used and receiver returns 200 code it means ST is actually used for querying (!).

This has some uses, but I think it's hard to rely on, plus all the problems above.

So we don't expect that - modes are to:

  • Simplify communication between sender and receiver (e.g. simplify sender configuration, reject undesired modes early on receive, clearly document which mode is how expensive)
  • Help with adoption of more advanced modes (e.g. letting sender know that some receivers will only support certain mode, monitoring mode adoption etc)

@bwplotka

Copy link
Copy Markdown
Member Author

@bwplotka
bwplotka requested a review from kgeckhart October 19, 2026 14:53
Comment thread docs/specs/prw/remote_write_spec_2_0.md Outdated
Comment thread docs/specs/prw/remote_write_spec_2_0.md Outdated
dashpole
dashpole previously approved these changes Oct 19, 2026

@dashpole dashpole left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This split makes sense to me

…tart_timestamp

Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
Comment thread docs/specs/prw/remote_write_spec_2_0.md Outdated
Comment thread docs/specs/prw/remote_write_spec_2_0.md Outdated
Comment thread docs/specs/prw/remote_write_spec_2_0.md Outdated
* Use content type of `Content-Type: application/x-protobuf;proto=io.prometheus.write.v2.Request;mode=stateless`.
* Ensure all of the following for every `TimeSeries` in a `Request`:
* `Exemplar`s MUST only be sent on a `TimeSeries` that also contains at least one element in `samples` or in `histograms`. Exemplar-only `TimeSeries` messages MUST NOT be sent.
* Histograms MUST use the `Histogram` message (e.g. using Native Histograms with Custom Buckets instead of classic histogram `_bucket`, `_sum`, and `_count` float `Sample` series).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if the type is not set, it's impossible to validate this, which questions the stateless name a little bit and the usefulness of this mode :(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, not if we specify a clear heuristic e.g. _bucket, _sum, _count UNTYPED can be rejected. But yes, this is probably the biggest downside of this solution.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_sum and _count can be summary as well, so nope. This is what we ran into when migrating to native histograms internally, we couldn't tell from PromQL queries alone what the type was, so we had to cross reference with the source code all the time.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit naive on this front, are summaries something we still want to support to the point that this would be a blocker beyond updating the spec to say they get dropped too if they are untyped?

Comment thread docs/specs/prw/remote_write_spec_2_0.md Outdated
Comment thread docs/specs/prw/remote_write_spec_2_0.md Outdated
* Satisfy all `TimeSeries` requirements of [`stateless` mode](#stateless-mode).
* Ensure all of the following for every `TimeSeries` in a `Request`:
* `Metadata.type` MUST be specified and MUST NOT be `METRIC_TYPE_UNSPECIFIED` (`0`).
* `start_timestamp` MUST be set (non-zero) on every `Sample` and `Histogram` (except stale markers) for cumulative or delta metric types (`METRIC_TYPE_COUNTER`, `METRIC_TYPE_HISTOGRAM`, as well as `METRIC_TYPE_SUMMARY` `_sum` and `_count` `Samples`).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: I'm unsure about this combined with metadata. As we said, maybe sending ST with every sample isn't a good idea for bandwidth when it comes to cumulative metrics. But I'm unsure about the actual overhead.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it's what some backends needs - true ST on cumulatives.

In Prometheus ecosystem I can imagine stateless + metric types (no ST) is the future (aka 3.0)

@krajorama

Copy link
Copy Markdown
Member

Practically, when a client sends the new mode there's no feedback if it was actually verified. Unless of course it's flat out refused with HTTP status 415.

Which means that the user needs to ask the admin of the receiver whether the check was actually performed.

Consider returning X-Prometheus-Remote-Write-Mode-Checked: <stateless|stateless-typed> if a check was actually performed.

…tion, and metric types

Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
@bwplotka

bwplotka commented Oct 20, 2026 •

Copy link
Copy Markdown
Member Author

Practically, when a client sends the new mode there's no feedback if it was actually verified. Unless of course it's flat out refused with HTTP status 415.
Which means that the user needs to ask the admin of the receiver whether the check was actually performed.
Consider returning X-Prometheus-Remote-Write-Mode-Checked: <stateless|stateless-typed> if a check was actually performed.

I don't think we should add a response header for this:

  1. ;mode= is a sender-side guarantee (similar to RFC 6906 profiles), not a request for the receiver to validate the sender. Checking that a sender implementation actually satisfies stateless or stateless-typed is the job of prometheus/compliance tests, not production receivers on every request.
  2. In practice, receivers validate what their ingestion pipeline needs rather than linting every rule in the mode (for example, a receiver might require Metadata.type and start_timestamp and return 400 when missing, but not burn CPU checking if a float series name ends in _bucket). That makes a single Mode-Checked header hard to define accurately.
  3. A sender has nothing actionable to do with that header at runtime on a 2xx response. If a receiver requires a mode, the user already gets immediate feedback via 415 when the mode is not sent and 400 when data cannot be ingested. If a generic receiver (like Prometheus TSDB) accepts all modes without needing the extra guarantees, paying per-response header overhead just to report that is unnecessary.

<!---
Rationales: https://github.com/prometheus/proposals/blob/alexg/remote-write-20-proposal/proposals/2024-04-09_remote-write-20.md#no-new-compression-added--yet-
-->
Content encoding request header MUST follow [the RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-content-encoding). Senders MUST use the `snappy` value. Receivers MUST support `snappy` compression. New, optional compression algorithms might come in 2.x or beyond.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to do anything about opening up compression options for rc6 or leave that for a different RC?


#### Modes

By default (when no `;mode=` parameter is set), `io.prometheus.write.v2.Request` keeps several structural and metadata rules optional (`SHOULD` or `MAY`) so Senders can forward any scraped metric without data loss or conversion overhead. However, some common stateless or strongly typed Receivers cannot ingest all the data e.g. detached exemplars, classic histogram series or untyped metrics.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT about naming the default mode stateful? This way every mode has a named option and if we move the default at a later time there's still a named option for people to fall back on.

I think being able to explicitly configure the mode could also be helpful vs needing to know implicitly what no mode means.


Senders SHOULD support `stateless` and `stateless-typed` modes. Senders MAY operate in either mode behind configuration or by default.

Receivers MAY accept any valid `io.prometheus.write.v2.Request` regardless of mode, require at least `stateless` mode, or require `stateless-typed` mode only. If rejected, [415 HTTP Unsupported Media Type](https://www.rfc-editor.org/rfc/rfc9110.html#name-415-unsupported-media-type) MUST be used.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/prometheus/docs/pull/3142/changes#diff-fb08fe6850c73166c5ad591d90f78aee4c90986316885ec04d0942ee871640b9R199 mentions a sender can retry when they get a 415 to try to find something that works. We could return the supported content types in the response so senders aren't having to iterate and guess. Doing this is probably enough to avoid needing an ahead of time content negotiation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants