diff --git a/apps/opik-backend/config.yml b/apps/opik-backend/config.yml index ad91f3000a..ec31499b02 100644 --- a/apps/opik-backend/config.yml +++ b/apps/opik-backend/config.yml @@ -63,3 +63,5 @@ authentication: server: enableVirtualThreads: ${ENABLE_VIRTUAL_THREADS:-false} + gzip: + enabled: true diff --git a/apps/opik-backend/src/main/java/com/comet/opik/infrastructure/instrumentation/InstrumentAsyncUtils.java b/apps/opik-backend/src/main/java/com/comet/opik/infrastructure/instrumentation/InstrumentAsyncUtils.java index db32cab7e2..cc6f81b4df 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/infrastructure/instrumentation/InstrumentAsyncUtils.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/infrastructure/instrumentation/InstrumentAsyncUtils.java @@ -6,8 +6,6 @@ import lombok.extern.slf4j.Slf4j; import reactor.core.scheduler.Schedulers; -import java.lang.reflect.Method; - @Slf4j public class InstrumentAsyncUtils { @@ -30,8 +28,7 @@ public static void endSegment(Segment segment) { Schedulers.boundedElastic().schedule(() -> { try { // End the segment - Method endMethod = segment.getClass().getMethod("end"); - endMethod.invoke(segment); + segment.end(); } catch (Exception e) { log.warn("Failed to end segment", e); } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/utils/JsonUtils.java b/apps/opik-backend/src/main/java/com/comet/opik/utils/JsonUtils.java index 71703630d6..cf36565de7 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/utils/JsonUtils.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/utils/JsonUtils.java @@ -11,6 +11,8 @@ import lombok.NonNull; import lombok.experimental.UtilityClass; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.UncheckedIOException; import java.math.BigDecimal; @@ -51,4 +53,12 @@ public String writeValueAsString(@NonNull Object value) { throw new UncheckedIOException(exception); } } + + public void writeValueAsString(@NonNull ByteArrayOutputStream baos, @NonNull Object value) { + try { + MAPPER.writeValue(baos, value); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } } diff --git a/apps/opik-backend/src/test/java/com/comet/opik/api/resources/utils/ClientSupportUtils.java b/apps/opik-backend/src/test/java/com/comet/opik/api/resources/utils/ClientSupportUtils.java index 11efac3e6e..cd9e434f14 100644 --- a/apps/opik-backend/src/test/java/com/comet/opik/api/resources/utils/ClientSupportUtils.java +++ b/apps/opik-backend/src/test/java/com/comet/opik/api/resources/utils/ClientSupportUtils.java @@ -10,6 +10,8 @@ private ClientSupportUtils() { } public static void config(ClientSupport client) { + client.getClient().register(new ConditionalGZipFilter()); + client.getClient().getConfiguration().property(ClientProperties.READ_TIMEOUT, 35_000); client.getClient().getConfiguration().connectorProvider(new GrizzlyConnectorProvider()); // Required for PATCH: // https://github.com/dropwizard/dropwizard/discussions/6431/ Required for PATCH: diff --git a/apps/opik-backend/src/test/java/com/comet/opik/api/resources/utils/ConditionalGZipFilter.java b/apps/opik-backend/src/test/java/com/comet/opik/api/resources/utils/ConditionalGZipFilter.java new file mode 100644 index 0000000000..24ce718593 --- /dev/null +++ b/apps/opik-backend/src/test/java/com/comet/opik/api/resources/utils/ConditionalGZipFilter.java @@ -0,0 +1,43 @@ +package com.comet.opik.api.resources.utils; + + +import com.comet.opik.utils.JsonUtils; +import jakarta.ws.rs.client.ClientRequestContext; +import jakarta.ws.rs.client.ClientRequestFilter; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.GZIPOutputStream; + +public class ConditionalGZipFilter implements ClientRequestFilter { + + private static final int GZIP_THRESHOLD_500KB = 500 * 1024; + + @Override + public void filter(ClientRequestContext requestContext) throws IOException { + + if (requestContext.hasEntity() && MediaType.APPLICATION_JSON_TYPE.equals(requestContext.getMediaType())) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + JsonUtils.writeValueAsString(baos, requestContext.getEntity()); // Serialize the entity to byte array + + byte[] entityBytes = baos.toByteArray(); + int entitySize = entityBytes.length; + + if (entitySize > GZIP_THRESHOLD_500KB) { + ByteArrayOutputStream compressedBaos = new ByteArrayOutputStream(); + try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(compressedBaos)) { + gzipOutputStream.write(entityBytes); + } + + byte[] compressedEntity = compressedBaos.toByteArray(); + requestContext.setEntity(compressedEntity, null, MediaType.APPLICATION_JSON_TYPE); + requestContext.getHeaders().add(HttpHeaders.CONTENT_ENCODING, "gzip"); + } else { + // Use the original entity if below the threshold + requestContext.setEntity(entityBytes, null, MediaType.APPLICATION_JSON_TYPE); + } + } + } +} diff --git a/apps/opik-backend/src/test/resources/config-test.yml b/apps/opik-backend/src/test/resources/config-test.yml index a77cec3b07..412ecf9cd5 100644 --- a/apps/opik-backend/src/test/resources/config-test.yml +++ b/apps/opik-backend/src/test/resources/config-test.yml @@ -63,3 +63,5 @@ authentication: server: enableVirtualThreads: ${ENABLE_VIRTUAL_THREADS:-false} + gzip: + enabled: true