Skip to content

rw2: allow optional gzip and zstd compression (rc.7) - #3173

Open
kgeckhart wants to merge 2 commits into
prometheus:mainfrom
kgeckhart:kgeckhart/rw2-compression
Open

kgeckhart wants to merge 2 commits into
prometheus:mainfrom
kgeckhart:kgeckhart/rw2-compression

Conversation

@kgeckhart

@kgeckhart kgeckhart commented Oct 30, 2026 •

Copy link
Copy Markdown

Optionally allow gzip and zstd compression while Senders and Receivers still MUST support snappy.

RW2 with snappy sends roughly 1.7x the bytes of OTLP with gzip. gzip or zstd closes that gap. On the Receiver side, zstd decodes about 9% slower than snappy, and still well under OTLP with gzip.

All numbers are for 10000 samples.

Size

bytes/message vs snappy
RW2 snappy 346.1 KB
RW2 zstd 160.3 KB -54%
RW2 gzip 190.4 KB -45%
OTLP gzip 200.7 KB

Encode

ns/op vs snappy
RW2 snappy 5.4 ms
RW2 zstd 8.9 ms +65%
RW2 gzip 16.0 ms +196%
OTLP gzip 23.7 ms

Decode

ns/op vs snappy
RW2 snappy 11.0 ms
RW2 zstd 12.0 ms +9%
RW2 gzip 15.1 ms +37%
OTLP gzip 36.1 ms

Medians of 6 runs from @bwplotka's October 2026 metrics-streaming benchmarks (encode, decode), vtprotobuf, zstd at SpeedFastest, gzip at DefaultCompression.

The 2.0 proposal held off on new compressions over receiver-side CPU cost. Keeping gzip and zstd a SHOULD for Receivers leaves that choice with them.

Related to: prometheus/prometheus#13366

Signed-off-by: Kyle Eckhart <kgeckhart@users.noreply.github.com>

@bwplotka bwplotka left a comment

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.

Nice! Some suggestions, otherwise looks great.

Thanks!

Comment thread docs/specs/prw/remote_write_spec_2_0.md Outdated
* `gzip`: MUST follow [RFC 1952](https://www.rfc-editor.org/rfc/rfc1952.html).
* `zstd`: MUST follow [RFC 8878](https://www.rfc-editor.org/rfc/rfc8878.html) and [RFC 9659](https://www.rfc-editor.org/rfc/rfc9659.html).

Senders and Receivers MUST support `snappy`. Receivers SHOULD support `gzip` and `zstd`. `gzip` and `zstd` MUST only be used with `io.prometheus.write.v2.Request`.

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.

Since gzip and zstd are SHOULD for Receivers and 415 retry is MAY for Senders, Senders SHOULD still default to snappy unless configured otherwise or advertised via Accept-Encoding so 2.x Senders and snappy-only Receivers interoperate out of the box. Also, MUST only be used leaves it ambiguous whether HTTP decompression middlewares running before Content-Type parsing must reject prometheus.WriteRequest compressed with gzip or zstd:

Suggested change
Senders and Receivers MUST support `snappy`. Receivers SHOULD support `gzip` and `zstd`. `gzip` and `zstd` MUST only be used with `io.prometheus.write.v2.Request`.
Senders and Receivers MUST support `snappy`. Receivers SHOULD support `gzip` and `zstd`. Senders MAY support `gzip` and `zstd`, and SHOULD use `snappy` by default unless configured otherwise or advertised by the Receiver via `Accept-Encoding`. Senders MUST NOT use `gzip` or `zstd` with `prometheus.WriteRequest`, and Receivers MAY reject such requests with `415 HTTP Unsupported Media Type`.

@kgeckhart kgeckhart Nov 10, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I took most of this. I made it “MAY use” instead of “MAY support”, and turned the prometheus.WriteRequest MUST NOT into a scope sentence (gzip and zstd MUST only be used with io.prometheus.write.v2.Request). I left out “Receivers MAY reject with 415” since the 415 section already covers it.

Comment thread docs/specs/prw/remote_write_spec_2_0.md Outdated

Senders and Receivers MUST support `snappy`. Receivers SHOULD support `gzip` and `zstd`. `gzip` and `zstd` MUST only be used with `io.prometheus.write.v2.Request`.

Receivers MAY include the `Accept-Encoding` response header on 2xx responses to advertise supported `Content-Encoding` values, as defined in [RFC 7694](https://www.rfc-editor.org/rfc/rfc7694.html). Senders MAY use it to choose a supported encoding for subsequent requests.

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.

If a Receiver pool sits behind an L7 load balancer during a rolling update or rollback, upgrading from a 2xx Accept-Encoding header without 415 retry support will drop batches when subsequent requests hit snappy-only replicas, or flip-flop across replicas. Also, RFC 9110 obsoleted RFC 7694 and folded response Accept-Encoding directly into Section 12.5.3:

Suggested change
Receivers MAY include the `Accept-Encoding` response header on 2xx responses to advertise supported `Content-Encoding` values, as defined in [RFC 7694](https://www.rfc-editor.org/rfc/rfc7694.html). Senders MAY use it to choose a supported encoding for subsequent requests.
Receivers MAY include the `Accept-Encoding` response header on 2xx responses to advertise supported `Content-Encoding` values for that request, as defined in [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding). Senders using `io.prometheus.write.v2.Request` MAY use it to choose an allowed encoding for subsequent requests, in which case they MUST fall back to `snappy` and retry on `415 HTTP Unsupported Media Type` and SHOULD avoid immediately re-upgrading across load-balanced Receiver replicas.

@kgeckhart kgeckhart Nov 10, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I took this and moved it into the Content-Encoding section so the Accept-Encoding rules live in one place. I made the fallback “retry once with snappy“, and skipped it when the 415 has Accept but no Accept-Encoding. I dropped the re-upgrade SHOULD because I couldn’t figure out how to test “immediately”. Happy to add it back if you have wording in mind.

Comment thread docs/specs/prw/remote_write_spec_2_0.md Outdated
#### Unsupported Request Content

Receivers MUST return [415 HTTP Unsupported Media Type](https://www.rfc-editor.org/rfc/rfc9110.html#name-415-unsupported-media-type) status code if they don't support a given content type or encoding provided by Senders. When returning `415 HTTP Unsupported Media Type`, Receivers SHOULD include the `Accept` (listing supported `Content-Type` values, including supported `;proto=` and `;mode=` parameters) and `Accept-Encoding` (listing supported `Content-Encoding` values) response headers as defined in [RFC 7694](https://www.rfc-editor.org/rfc/rfc7694.html) so Senders can determine what the Receiver supports without guessing.
Receivers MUST return [415 HTTP Unsupported Media Type](https://www.rfc-editor.org/rfc/rfc9110.html#name-415-unsupported-media-type) status code if they don't support a given content type or encoding provided by Senders. When returning `415 HTTP Unsupported Media Type`, Receivers SHOULD include the `Accept` response header (listing supported `Content-Type` values, including supported `;proto=` and `;mode=` parameters) so Senders can determine what the Receiver supports without guessing. If the 415 is due to an unsupported `Content-Encoding`, Receivers SHOULD also include the `Accept-Encoding` response header listing supported `Content-Encoding` values. Otherwise, in a 415 response, Receivers MUST NOT include `Accept-Encoding`, as defined in [RFC 7694](https://www.rfc-editor.org/rfc/rfc7694.html).

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.

Keeping Accept as an unconditional SHOULD on all 415 responses means decompression middlewares like SnappyDecodeMiddleware or reverse proxies violate SHOULD when rejecting Content-Encoding before the inner handler knows accepted ;proto= and ;mode= values. Maybe scope Accept to Content-Type rejections per RFC 9110 Section 15.5.16 and link RFC 9110 Section 12.5.3 instead of obsoleted RFC 7694:

Suggested change
Receivers MUST return [415 HTTP Unsupported Media Type](https://www.rfc-editor.org/rfc/rfc9110.html#name-415-unsupported-media-type) status code if they don't support a given content type or encoding provided by Senders. When returning `415 HTTP Unsupported Media Type`, Receivers SHOULD include the `Accept` response header (listing supported `Content-Type` values, including supported `;proto=` and `;mode=` parameters) so Senders can determine what the Receiver supports without guessing. If the 415 is due to an unsupported `Content-Encoding`, Receivers SHOULD also include the `Accept-Encoding` response header listing supported `Content-Encoding` values. Otherwise, in a 415 response, Receivers MUST NOT include `Accept-Encoding`, as defined in [RFC 7694](https://www.rfc-editor.org/rfc/rfc7694.html).
Receivers MUST return [415 HTTP Unsupported Media Type](https://www.rfc-editor.org/rfc/rfc9110.html#name-415-unsupported-media-type) status code if they don't support a given content type or encoding provided by Senders. If the 415 is due to an unsupported `Content-Type`, Receivers SHOULD include the [`Accept`](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept) response header listing supported `Content-Type` values, including supported `;proto=` and `;mode=` parameters, so Senders can determine what the Receiver supports without guessing. If the 415 is due to an unsupported `Content-Encoding`, Receivers SHOULD include the [`Accept-Encoding`](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding) response header listing supported `Content-Encoding` values. Receivers that return 415 for reasons unrelated to `Content-Encoding` MUST NOT include `Accept-Encoding`, as defined in [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Took the Content-Type half as is. I moved the Accept-Encoding half up next to the 2xx rule and linked to it from here.

Comment thread docs/specs/prw/remote_write_spec_2_0.md Resolved
@bwplotka

bwplotka commented Nov 2, 2026

Copy link
Copy Markdown
Member

The 2.0 proposal held off on new compressions over receiver-side CPU cost. Keeping gzip and zstd a SHOULD for Receivers leaves that choice with them. I'm not sure if we should update the proposal if we merge this?

Proposal is completely out of sync at this point. However it tried to explain history of certain changes and its motivation. Perhaps we close the proposal and write a biggy blog post for this. Not sure yet. Fine to skip for now.

Signed-off-by: Kyle Eckhart <kgeckhart@users.noreply.github.com>

@bwplotka bwplotka left a comment

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.

A few nits, thanks!


* `snappy`: [Google’s Snappy](https://github.com/google/snappy). The [block format](https://github.com/google/snappy/blob/2c94e11145f0b7b184b831577c93e5a41c4c0346/format_description.txt) MUST be used -- [the framed format](https://github.com/google/snappy/blob/2c94e11145f0b7b184b831577c93e5a41c4c0346/framing_format.txt) MUST NOT be used.
* `gzip`: MUST follow [RFC 1952](https://www.rfc-editor.org/rfc/rfc1952.html).
* `zstd`: MUST follow [RFC 8878](https://www.rfc-editor.org/rfc/rfc8878.html) and [RFC 9659](https://www.rfc-editor.org/rfc/rfc9659.html).

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.

Neither RFC 8878 nor RFC 9659 forbids Zstandard dictionaries on zstd content coding, and RW 2.0 has no dictionary negotiation yet. Worth ruling them out explicitly like Snappy's framed format above:

Suggested change
* `zstd`: MUST follow [RFC 8878](https://www.rfc-editor.org/rfc/rfc8878.html) and [RFC 9659](https://www.rfc-editor.org/rfc/rfc9659.html).
* `zstd`: MUST follow [RFC 8878](https://www.rfc-editor.org/rfc/rfc8878.html) and [RFC 9659](https://www.rfc-editor.org/rfc/rfc9659.html). Dictionaries MUST NOT be used.

* `gzip`: MUST follow [RFC 1952](https://www.rfc-editor.org/rfc/rfc1952.html).
* `zstd`: MUST follow [RFC 8878](https://www.rfc-editor.org/rfc/rfc8878.html) and [RFC 9659](https://www.rfc-editor.org/rfc/rfc9659.html).

`gzip` and `zstd` MUST only be used with `io.prometheus.write.v2.Request`. `prometheus.WriteRequest` is unchanged from [1.0](./remote_write_spec.md), `snappy` only.

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.

Since #unsupported-request-content is a MUST, passive MUST only be used here implies dual-version Receivers have to reject prometheus.WriteRequest compressed with gzip or zstd, which breaks HTTP decompression middlewares in client_golang or reverse proxies that decode Content-Encoding before checking Content-Type. Keeping MUST NOT on Senders and MAY on Receivers lets both middleware-first and Content-Type-first Receivers pass compliance:

Suggested change
`gzip` and `zstd` MUST only be used with `io.prometheus.write.v2.Request`. `prometheus.WriteRequest` is unchanged from [1.0](./remote_write_spec.md), `snappy` only.
Senders MUST NOT use `gzip` or `zstd` with `prometheus.WriteRequest`, which remains `snappy` only as in [1.0](./remote_write_spec.md). Receivers MAY reject `prometheus.WriteRequest` compressed with `gzip` or `zstd` with [415 HTTP Unsupported Media Type](https://www.rfc-editor.org/rfc/rfc9110.html#name-415-unsupported-media-type).


Senders and Receivers MUST support `snappy`. Receivers SHOULD support `gzip` and `zstd`. Senders MAY use `gzip` or `zstd`, and SHOULD use `snappy` unless configured otherwise or the Receiver advertised the encoding with `Accept-Encoding`.

Receivers advertise supported `Content-Encoding` values with the [`Accept-Encoding`](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding) response header, which applies to the request it answers. Receivers MAY send it on 2xx responses. On a [415 HTTP Unsupported Media Type](https://www.rfc-editor.org/rfc/rfc9110.html#name-415-unsupported-media-type) they SHOULD send it when an unsupported `Content-Encoding` is the cause, and MUST NOT send it otherwise. A Sender that chose its encoding from a 2xx `Accept-Encoding` MUST retry once with `snappy` when it gets a 415, unless the 415 carries `Accept` and no `Accept-Encoding`.

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.

MUST retry once with snappy only retries that single batch and leaves gzip or zstd active for subsequent requests after a Receiver rollback. Also, phrasing the re-upgrade guard as a cooldown makes it easy to test in prometheus/compliance with back-to-back scrapes right after a 415 fallback:

Suggested change
Receivers advertise supported `Content-Encoding` values with the [`Accept-Encoding`](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding) response header, which applies to the request it answers. Receivers MAY send it on 2xx responses. On a [415 HTTP Unsupported Media Type](https://www.rfc-editor.org/rfc/rfc9110.html#name-415-unsupported-media-type) they SHOULD send it when an unsupported `Content-Encoding` is the cause, and MUST NOT send it otherwise. A Sender that chose its encoding from a 2xx `Accept-Encoding` MUST retry once with `snappy` when it gets a 415, unless the 415 carries `Accept` and no `Accept-Encoding`.
Receivers advertise supported `Content-Encoding` values with the [`Accept-Encoding`](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding) response header, which applies to the request it answers. Receivers MAY send it on 2xx responses. On a [415 HTTP Unsupported Media Type](https://www.rfc-editor.org/rfc/rfc9110.html#name-415-unsupported-media-type) they SHOULD send it when an unsupported `Content-Encoding` is the cause, and MUST NOT send it otherwise. A Sender that switched to `gzip` or `zstd` from a 2xx `Accept-Encoding` MUST revert to `snappy` for subsequent requests and retry the failed request once with `snappy` when it gets a 415, unless the 415 carries `Accept` and no `Accept-Encoding`. After reverting on a 415, the Sender SHOULD wait for a cooldown period before switching away from `snappy` via `Accept-Encoding` again.

* `gzip`: MUST follow [RFC 1952](https://www.rfc-editor.org/rfc/rfc1952.html).
* `zstd`: MUST follow [RFC 8878](https://www.rfc-editor.org/rfc/rfc8878.html) and [RFC 9659](https://www.rfc-editor.org/rfc/rfc9659.html).

`gzip` and `zstd` MUST only be used with `io.prometheus.write.v2.Request`. `prometheus.WriteRequest` is unchanged from [1.0](./remote_write_spec.md), `snappy` only.

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.

" snappy only." is weird. Did you forget to finish some sentence?

* `gzip`: MUST follow [RFC 1952](https://www.rfc-editor.org/rfc/rfc1952.html).
* `zstd`: MUST follow [RFC 8878](https://www.rfc-editor.org/rfc/rfc8878.html) and [RFC 9659](https://www.rfc-editor.org/rfc/rfc9659.html).

`gzip` and `zstd` MUST only be used with `io.prometheus.write.v2.Request`. `prometheus.WriteRequest` is unchanged from [1.0](./remote_write_spec.md), `snappy` only.

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.

Let's also be specific (sender/receiver). Mention e.g. "Sender MUST NOT".

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.

2 participants