Skip to content

Commit df76e30

Browse files
author
Matt
authored
Enable ZSTD compression for profiling (#8862)
1 parent c7b5ca4 commit df76e30

File tree

8 files changed

+85
-14
lines changed

8 files changed

+85
-14
lines changed

dd-java-agent/agent-profiling/profiling-controller/src/main/java/com/datadog/profiling/controller/ProfilerSettingsSupport.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,12 @@ protected ProfilerSettingsSupport(
123123
configProvider.getInteger(
124124
ProfilingConfig.PROFILING_UPLOAD_TIMEOUT,
125125
ProfilingConfig.PROFILING_UPLOAD_TIMEOUT_DEFAULT);
126+
// First try the new debug upload compression property, and fall back to the deprecated one
126127
uploadCompression =
127128
configProvider.getString(
128-
ProfilingConfig.PROFILING_UPLOAD_COMPRESSION,
129-
ProfilingConfig.PROFILING_UPLOAD_COMPRESSION_DEFAULT);
129+
ProfilingConfig.PROFILING_DEBUG_UPLOAD_COMPRESSION,
130+
ProfilingConfig.PROFILING_DEBUG_UPLOAD_COMPRESSION_DEFAULT,
131+
ProfilingConfig.PROFILING_UPLOAD_COMPRESSION);
130132
allocationProfilingEnabled =
131133
configProvider.getBoolean(
132134
ProfilingConfig.PROFILING_ALLOCATION_ENABLED,

dd-java-agent/agent-profiling/profiling-uploader/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ dependencies {
3131

3232
implementation libs.okhttp
3333
implementation libs.lz4
34+
implementation group: 'io.airlift', name: 'aircompressor', version: '2.0.2'
3435

3536
testImplementation libs.bundles.junit5
3637
testImplementation project(':dd-java-agent:agent-profiling:profiling-testing')

dd-java-agent/agent-profiling/profiling-uploader/src/main/java/com/datadog/profiling/uploader/CompressingRequestBody.java

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import datadog.trace.api.Platform;
44
import datadog.trace.api.profiling.RecordingInputStream;
5+
import io.airlift.compress.zstd.ZstdOutputStream;
56
import java.io.BufferedOutputStream;
67
import java.io.IOException;
78
import java.io.InputStream;
@@ -86,6 +87,7 @@ interface RetryBackoff {
8687
private static final int[] LZ4_MAGIC = new int[] {0x04, 0x22, 0x4D, 0x18};
8788
private static final int ZIP_MAGIC[] = new int[] {80, 75, 3, 4};
8889
private static final int GZ_MAGIC[] = new int[] {31, 139};
90+
private static final int ZSTD_MAGIC[] = new int[] {0x28, 0xB5, 0x2F, 0xFD};
8991

9092
private final InputStreamSupplier inputStreamSupplier;
9193
private final OutputStreamMappingFunction outputStreamMapper;
@@ -269,7 +271,24 @@ public void close() throws IOException {
269271
*/
270272
static boolean isCompressed(@Nonnull final InputStream is) throws IOException {
271273
checkMarkSupported(is);
272-
return isGzip(is) || isLz4(is) || isZip(is);
274+
return isGzip(is) || isLz4(is) || isZip(is) || isZstd(is);
275+
}
276+
277+
/**
278+
* Check whether the stream represents ZSTD data
279+
*
280+
* @param is input stream; must support {@linkplain InputStream#mark(int)}
281+
* @return {@literal true} if the stream represents ZSTD data
282+
* @throws IOException
283+
*/
284+
static boolean isZstd(@Nonnull final InputStream is) throws IOException {
285+
checkMarkSupported(is);
286+
is.mark(ZSTD_MAGIC.length);
287+
try {
288+
return hasMagic(is, ZSTD_MAGIC);
289+
} finally {
290+
is.reset();
291+
}
273292
}
274293

275294
/**
@@ -331,12 +350,11 @@ private static void checkMarkSupported(@Nonnull final InputStream is) throws IOE
331350

332351
private static OutputStreamMappingFunction getOutputStreamMapper(
333352
@Nonnull CompressionType compressionType) {
334-
// currently only gzip and off are supported
335-
// this needs to be updated once more compression types are added
336-
compressionType =
337-
(Platform.isNativeImage() && compressionType != CompressionType.OFF
338-
? CompressionType.GZIP
339-
: compressionType);
353+
// Handle native image compatibility
354+
if (Platform.isNativeImage() && compressionType != CompressionType.OFF) {
355+
compressionType = CompressionType.GZIP;
356+
}
357+
340358
switch (compressionType) {
341359
case GZIP:
342360
{
@@ -346,6 +364,10 @@ private static OutputStreamMappingFunction getOutputStreamMapper(
346364
{
347365
return out -> out;
348366
}
367+
case ZSTD:
368+
{
369+
return CompressingRequestBody::toZstdStream;
370+
}
349371
case ON:
350372
case LZ4:
351373
default:
@@ -355,6 +377,12 @@ private static OutputStreamMappingFunction getOutputStreamMapper(
355377
}
356378
}
357379

380+
private static OutputStream toZstdStream(@Nonnull OutputStream os) throws IOException {
381+
// Default compression level is 3 which provides a good balance between performance and
382+
// compression ratio
383+
return new ZstdOutputStream(os);
384+
}
385+
358386
private static OutputStream toLz4Stream(@Nonnull OutputStream os) throws IOException {
359387
return new LZ4FrameOutputStream(
360388
os,

dd-java-agent/agent-profiling/profiling-uploader/src/main/java/com/datadog/profiling/uploader/CompressionType.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ enum CompressionType {
1212
/** Lower compression ratio with less CPU overhead * */
1313
LZ4,
1414
/** Better compression ratio for the price of higher CPU usage * */
15-
GZIP;
15+
GZIP,
16+
/** High compression ratio with reasonable CPU usage * */
17+
ZSTD;
1618

1719
private static final Logger log = LoggerFactory.getLogger(CompressionType.class);
1820

@@ -30,8 +32,10 @@ static CompressionType of(String type) {
3032
return LZ4;
3133
case "gzip":
3234
return GZIP;
35+
case "zstd":
36+
return ZSTD;
3337
default:
34-
log.warn("Unrecognizable compression type: {}. Defaulting to 'on'.", type);
38+
log.warn("Unrecognizable compression type: {}. Defaulting to 'lz4'.", type);
3539
return ON;
3640
}
3741
}

dd-java-agent/agent-profiling/profiling-uploader/src/test/java/com/datadog/profiling/uploader/CompressingRequestBodyTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import static org.mockito.Mockito.when;
77

88
import datadog.trace.api.profiling.RecordingInputStream;
9+
import io.airlift.compress.zstd.ZstdInputStream;
10+
import io.airlift.compress.zstd.ZstdOutputStream;
911
import java.io.BufferedInputStream;
1012
import java.io.ByteArrayInputStream;
1113
import java.io.ByteArrayOutputStream;
@@ -186,6 +188,15 @@ void writeTo(CompressionType compressionType) throws IOException {
186188
assertEquals(compressed.length, instance.getWrittenBytes());
187189
break;
188190
}
191+
case ZSTD:
192+
{
193+
assertTrue(CompressingRequestBody.isZstd(compressedStream));
194+
byte[] uncompressed = IOUtils.toByteArray(new ZstdInputStream(compressedStream));
195+
assertArrayEquals(recordingData, uncompressed);
196+
assertEquals(recordingData.length, instance.getReadBytes());
197+
assertEquals(compressed.length, instance.getWrittenBytes());
198+
break;
199+
}
189200
}
190201
}
191202

@@ -210,6 +221,11 @@ void writeToRecompression(CompressionType targetType) throws IOException {
210221
compressedStream = new GZIPOutputStream(baos);
211222
break;
212223
}
224+
case ZSTD:
225+
{
226+
compressedStream = new ZstdOutputStream(baos);
227+
break;
228+
}
213229
}
214230
assertNotNull(compressedStream);
215231

dd-java-agent/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ tasks.register('checkAgentJarSize').configure {
306306
doLast {
307307
// Arbitrary limit to prevent unintentional increases to the agent jar size
308308
// Raise or lower as required
309-
assert shadowJar.archiveFile.get().getAsFile().length() <= 31 * 1024 * 1024
309+
assert shadowJar.archiveFile.get().getAsFile().length() <= 32 * 1024 * 1024
310310
}
311311

312312
dependsOn "shadowJar"

dd-trace-api/src/main/java/datadog/trace/api/config/ProfilingConfig.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,20 @@ public final class ProfilingConfig {
3333
"profiling.jfr-template-override-file";
3434
public static final String PROFILING_UPLOAD_TIMEOUT = "profiling.upload.timeout";
3535
public static final int PROFILING_UPLOAD_TIMEOUT_DEFAULT = 30;
36+
/**
37+
* @deprecated Use {@link #PROFILING_DEBUG_UPLOAD_COMPRESSION} instead. This will be removed in a
38+
* future release.
39+
*/
40+
@Deprecated
3641
public static final String PROFILING_UPLOAD_COMPRESSION = "profiling.upload.compression";
37-
public static final String PROFILING_UPLOAD_COMPRESSION_DEFAULT = "on";
42+
/**
43+
* Default compression value. Supported values are: - "on": equivalent to "lz4", will later be
44+
* "zstd" - "off": disables compression - "lz4": uses LZ4 compression (fast with moderate
45+
* compression ratio) - "gzip": uses GZIP compression (higher compression ratio but slower) -
46+
* "zstd": uses ZSTD compression (high compression ratio with reasonable performance)
47+
*/
48+
public static final String PROFILING_DEBUG_UPLOAD_COMPRESSION_DEFAULT = "lz4";
49+
3850
public static final String PROFILING_PROXY_HOST = "profiling.proxy.host";
3951
public static final String PROFILING_PROXY_PORT = "profiling.proxy.port";
4052
public static final int PROFILING_PROXY_PORT_DEFAULT = 8080;
@@ -192,6 +204,14 @@ public final class ProfilingConfig {
192204

193205
public static final String PROFILING_DEBUG_DUMP_PATH = "profiling.debug.dump_path";
194206
public static final String PROFILING_DEBUG_JFR_DISABLED = "profiling.debug.jfr.disabled";
207+
/**
208+
* Configuration for profile upload compression. Supported values are: - "on": equivalent to "lz4"
209+
* - "off": disables compression - "lz4": uses LZ4 compression (fast with moderate compression
210+
* ratio) - "gzip": uses GZIP compression (higher compression ratio but slower) - "zstd": uses
211+
* ZSTD compression (high compression ratio with reasonable performance)
212+
*/
213+
public static final String PROFILING_DEBUG_UPLOAD_COMPRESSION =
214+
"profiling.debug.upload.compression";
195215

196216
public static final String PROFILING_CONTEXT_ATTRIBUTES = "profiling.context.attributes";
197217

internal-api/src/main/java/datadog/trace/api/Config.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1259,7 +1259,7 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment())
12591259
configProvider.getInteger(PROFILING_UPLOAD_TIMEOUT, PROFILING_UPLOAD_TIMEOUT_DEFAULT);
12601260
profilingUploadCompression =
12611261
configProvider.getString(
1262-
PROFILING_UPLOAD_COMPRESSION, PROFILING_UPLOAD_COMPRESSION_DEFAULT);
1262+
PROFILING_UPLOAD_COMPRESSION, PROFILING_DEBUG_UPLOAD_COMPRESSION_DEFAULT);
12631263
profilingProxyHost = configProvider.getString(PROFILING_PROXY_HOST);
12641264
profilingProxyPort =
12651265
configProvider.getInteger(PROFILING_PROXY_PORT, PROFILING_PROXY_PORT_DEFAULT);

0 commit comments

Comments
 (0)