Add Snappy support to remote write endpoint#143994
Merged
felixbarny merged 15 commits intoelastic:mainfrom Mar 13, 2026
Merged
Conversation
Collaborator
|
Pinging @elastic/es-storage-engine (Team:StorageEngine) |
DaveCTurner
reviewed
Mar 12, 2026
Contributor
DaveCTurner
left a comment
There was a problem hiding this comment.
Looks good - some small changes requested inline but nothing substantial.
server/src/main/java/org/elasticsearch/rest/IndexingPressureAwareContentAggregator.java
Outdated
Show resolved
Hide resolved
...us/src/javaRestTest/java/org/elasticsearch/xpack/prometheus/PrometheusRemoteWriteRestIT.java
Show resolved
Hide resolved
...s/src/main/java/org/elasticsearch/xpack/prometheus/rest/PrometheusRemoteWriteRestAction.java
Show resolved
Hide resolved
...gin/prometheus/src/main/java/org/elasticsearch/xpack/prometheus/rest/SnappyBlockDecoder.java
Outdated
Show resolved
Hide resolved
...rometheus/src/test/java/org/elasticsearch/xpack/prometheus/rest/SnappyBlockDecoderTests.java
Show resolved
Hide resolved
...rometheus/src/test/java/org/elasticsearch/xpack/prometheus/rest/SnappyBlockDecoderTests.java
Outdated
Show resolved
Hide resolved
...rometheus/src/test/java/org/elasticsearch/xpack/prometheus/rest/SnappyBlockDecoderTests.java
Outdated
Show resolved
Hide resolved
...rometheus/src/test/java/org/elasticsearch/xpack/prometheus/rest/SnappyBlockDecoderTests.java
Outdated
Show resolved
Hide resolved
...rometheus/src/test/java/org/elasticsearch/xpack/prometheus/rest/SnappyBlockDecoderTests.java
Outdated
Show resolved
Hide resolved
...gin/prometheus/src/main/java/org/elasticsearch/xpack/prometheus/rest/SnappyBlockDecoder.java
Outdated
Show resolved
Hide resolved
DaveCTurner
reviewed
Mar 12, 2026
...gin/prometheus/src/main/java/org/elasticsearch/xpack/prometheus/rest/SnappyBlockDecoder.java
Outdated
Show resolved
Hide resolved
...gin/prometheus/src/main/java/org/elasticsearch/xpack/prometheus/rest/SnappyBlockDecoder.java
Show resolved
Hide resolved
...gin/prometheus/src/main/java/org/elasticsearch/xpack/prometheus/rest/SnappyBlockDecoder.java
Outdated
Show resolved
Hide resolved
Member
Author
|
Thanks a lot for the thorough review ❤️ |
DaveCTurner
approved these changes
Mar 12, 2026
Contributor
DaveCTurner
left a comment
There was a problem hiding this comment.
One nit, otherwise LGTM
...rometheus/src/test/java/org/elasticsearch/xpack/prometheus/rest/SnappyBlockDecoderTests.java
Show resolved
Hide resolved
...rometheus/src/test/java/org/elasticsearch/xpack/prometheus/rest/SnappyBlockDecoderTests.java
Show resolved
Hide resolved
Contributor
|
WDYT about a test like this? Can be added in a follow-up if you'd prefer /**
* Randomly synthesize a valid Snappy block as a sequence of arbitrary tagged elements and assert it decodes correctly.
*/
public void testSyntheticCompressedStream() throws IOException {
final var uncompressed = new byte[scaledRandomIntBetween(0, ByteSizeUnit.MB.toIntBytes(32))];
final var compressed = new RecyclerBytesStreamOutput(recycler);
writeVarint(compressed, uncompressed.length);
int uncompressedPosition = 0;
while (true) {
int remaining = uncompressed.length - uncompressedPosition;
if (remaining == 0) {
break;
}
if (uncompressedPosition == 0 || randomBoolean()) {
final var literal = randomByteArrayOfLength(scaledRandomIntBetween(1, remaining));
System.arraycopy(literal, 0, uncompressed, uncompressedPosition, literal.length);
writeLiteralLength(compressed, literal.length);
compressed.write(literal);
uncompressedPosition += literal.length;
} else {
final int copyLength = between(1, Math.min(remaining, 64));
int copyPosition = between(0, uncompressedPosition - 1);
writeCopy(compressed, uncompressedPosition - copyPosition, copyLength);
for (int i = 0; i < copyLength; i++) {
uncompressed[uncompressedPosition++] = uncompressed[copyPosition++];
}
}
}
try (var decoded = decoder.process(compressed.moveToBytesReference(), uncompressed.length + between(0, 1024))) {
assertThat(decoded, equalBytes(new BytesArray(uncompressed)));
}
}
private void writeLiteralLength(OutputStream out, int length) throws IOException {
int offsetLength = length - 1;
if (offsetLength > 0xFFFFFF || randomBoolean()) {
out.write(63 << 2);
out.write(offsetLength & 0xFF);
out.write((offsetLength >> 8) & 0xFF);
out.write((offsetLength >> 16) & 0xFF);
out.write((offsetLength >> 24) & 0xFF);
} else if (offsetLength > 0xFFFF || randomBoolean()) {
out.write(62 << 2);
out.write(offsetLength & 0xFF);
out.write((offsetLength >> 8) & 0xFF);
out.write((offsetLength >> 16) & 0xFF);
} else if (offsetLength > 0xFF || randomBoolean()) {
out.write(61 << 2);
out.write(offsetLength & 0xFF);
out.write((offsetLength >> 8) & 0xFF);
} else if (offsetLength > 59 || randomBoolean()) {
out.write(60 << 2);
out.write(offsetLength & 0xFF);
} else {
out.write(offsetLength << 2);
}
}
private void writeCopy(OutputStream out, int offset, int length) throws IOException {
if (offset > 0xFFFF || randomBoolean()) {
out.write(0x03 | ((length - 1) << 2));
out.write(offset & 0xFF);
out.write((offset >> 8) & 0xFF);
out.write((offset >> 16) & 0xFF);
out.write((offset >> 24) & 0xFF);
} else if (offset > 0x7FF || ((length - 4) | 7) != 7 || randomBoolean()) {
out.write(0x02 | ((length - 1) << 2));
out.write(offset & 0xFF);
out.write((offset >> 8) & 0xFF);
} else {
out.write(0x01 | (length - 4 << 2) | (((offset >> 8) & 0x07) << 5));
out.write(offset & 0xFF);
}
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Prometheus remote write 1.0 spec mandates Snappy block format compression (Content-Encoding: snappy). Netty's built-in HttpContentDecompressor intercepts this header but uses SnappyFrameDecoder (the framed format), which is incompatible. Valid Prometheus remote write requests are rejected before reaching the REST handler.
This PR exempts the
_prometheuspath from Netty's built-in Snappy framed decoding and adds aBodyPostProcessorhook toIndexingPressureAwareContentAggregator. It adds aSnappyBlockDecoderthat contains a fork of Netty's Snappy decoding logic which has the following improvements:out.ensureWritable(uncompressedLength)pre-allocation — prevents a malicious preamble from causing a huge allocation before any data is decoded and avoids humongous object allocations.maxSizecheck — defends against decompression bombs.out.written != uncompressedLength) — Netty doesn't check this.Alternative to #142102
To only review the introduction of
BodyPostProcessor, have a look at #144035.