From c3d385bb051199173f60b9d8c71bf5d90ad69f25 Mon Sep 17 00:00:00 2001 From: "Mateusz \"Serafin\" Gajewski" Date: Thu, 16 Oct 2025 21:34:47 +0200 Subject: [PATCH] Make query JSON more compact for UI - Serialize data sizes using succinct form (only query json download) - Do not output raw digest in query.json - Do not serialize type information for metrics - Do not serialize type information for operator infos - Cleanup old prunning logic to reduce copying --- .../trino/execution/DistributionSnapshot.java | 17 ----- .../java/io/trino/execution/QueryInfo.java | 41 ----------- .../java/io/trino/execution/QueryStats.java | 6 +- .../java/io/trino/execution/StageInfo.java | 16 ----- .../java/io/trino/execution/StageStats.java | 69 +------------------ .../java/io/trino/execution/StagesInfo.java | 7 -- .../java/io/trino/execution/TaskInfo.java | 5 -- .../execution/buffer/OutputBufferInfo.java | 18 ----- .../java/io/trino/operator/OperatorStats.java | 46 ------------- .../java/io/trino/operator/PipelineStats.java | 47 ------------- .../java/io/trino/operator/TaskStats.java | 50 -------------- .../io/trino/server/DataSizeSerializer.java | 43 ++++++++++++ .../io/trino/server/ServerMainModule.java | 3 + .../io/trino/server/ui/UiQueryResource.java | 57 ++++++++++++++- .../plugin/base/metrics/TDigestHistogram.java | 8 ++- 15 files changed, 109 insertions(+), 324 deletions(-) create mode 100644 core/trino-main/src/main/java/io/trino/server/DataSizeSerializer.java diff --git a/core/trino-main/src/main/java/io/trino/execution/DistributionSnapshot.java b/core/trino-main/src/main/java/io/trino/execution/DistributionSnapshot.java index ca8f718b14af..a992325eaa82 100644 --- a/core/trino-main/src/main/java/io/trino/execution/DistributionSnapshot.java +++ b/core/trino-main/src/main/java/io/trino/execution/DistributionSnapshot.java @@ -18,13 +18,10 @@ import com.google.common.base.MoreObjects.ToStringHelper; import io.trino.spi.metrics.Distribution; import io.trino.spi.metrics.Metric; -import io.trino.spi.metrics.Metrics; import java.util.Locale; -import java.util.Map; import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.collect.ImmutableMap.toImmutableMap; import static java.lang.String.format; @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) // Do not add @class property @@ -32,20 +29,6 @@ public record DistributionSnapshot(long total, double min, double max, double p01, double p05, double p10, double p25, double p50, double p75, double p90, double p95, double p99) implements Metric { - public static Metrics pruneMetrics(Metrics metrics) - { - return new Metrics(metrics.getMetrics().entrySet().stream() - .collect(toImmutableMap( - Map.Entry::getKey, - entry -> { - Metric metric = entry.getValue(); - if (metric instanceof Distribution) { - return new DistributionSnapshot((Distribution) metric); - } - return metric; - }))); - } - public DistributionSnapshot(Distribution distribution) { this( diff --git a/core/trino-main/src/main/java/io/trino/execution/QueryInfo.java b/core/trino-main/src/main/java/io/trino/execution/QueryInfo.java index 0b8f07cdeef8..efd508c6582b 100644 --- a/core/trino-main/src/main/java/io/trino/execution/QueryInfo.java +++ b/core/trino-main/src/main/java/io/trino/execution/QueryInfo.java @@ -448,45 +448,4 @@ public String toString() .add("fieldNames", fieldNames) .toString(); } - - public QueryInfo pruneDigests() - { - return new QueryInfo( - queryId, - session, - state, - self, - fieldNames, - query, - preparedQuery, - queryStats, - setCatalog, - setSchema, - setPath, - setAuthorizationUser, - resetAuthorizationUser, - setOriginalRoles, - setSessionProperties, - resetSessionProperties, - setRoles, - addedPreparedStatements, - deallocatedPreparedStatements, - startedTransactionId, - clearTransactionId, - updateType, - stages.map(StagesInfo::pruneDigests), - failureInfo, - errorCode, - warnings, - inputs, - output, - referencedTables, - routines, - finalQueryInfo, - resourceGroupId, - queryType, - retryPolicy, - pruned, - version); - } } diff --git a/core/trino-main/src/main/java/io/trino/execution/QueryStats.java b/core/trino-main/src/main/java/io/trino/execution/QueryStats.java index c940dd064921..e0827bb8dbe0 100644 --- a/core/trino-main/src/main/java/io/trino/execution/QueryStats.java +++ b/core/trino-main/src/main/java/io/trino/execution/QueryStats.java @@ -32,7 +32,6 @@ import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static io.airlift.units.DataSize.succinctBytes; import static io.trino.server.DynamicFilterService.DynamicFiltersStats; import static java.util.Objects.requireNonNull; @@ -323,10 +322,7 @@ public QueryStats( this.stageGcStatistics = ImmutableList.copyOf(requireNonNull(stageGcStatistics, "stageGcStatistics is null")); this.dynamicFiltersStats = requireNonNull(dynamicFiltersStats, "dynamicFiltersStats is null"); - - requireNonNull(operatorSummaries, "operatorSummaries is null"); - this.operatorSummaries = operatorSummaries.stream().map(OperatorStats::pruneDigests).collect(toImmutableList()); - + this.operatorSummaries = ImmutableList.copyOf(operatorSummaries); this.optimizerRulesSummaries = ImmutableList.copyOf(requireNonNull(optimizerRulesSummaries, "optimizerRulesSummaries is null")); } diff --git a/core/trino-main/src/main/java/io/trino/execution/StageInfo.java b/core/trino-main/src/main/java/io/trino/execution/StageInfo.java index ea2f3e540bb8..5aab4fb8b46f 100644 --- a/core/trino-main/src/main/java/io/trino/execution/StageInfo.java +++ b/core/trino-main/src/main/java/io/trino/execution/StageInfo.java @@ -28,7 +28,6 @@ import java.util.Map; import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; @Immutable @@ -167,21 +166,6 @@ public StageInfo withSubStages(List subStages) failureCause); } - public StageInfo pruneDigests() - { - return new StageInfo( - stageId, - state, - plan, - coordinatorOnly, - types, - stageStats.pruneDigests(), - tasks.stream().map(TaskInfo::pruneDigests).collect(toImmutableList()), - subStages, - tables, - failureCause); - } - public static StageInfo createInitial(QueryId queryId, StageState state, PlanFragment fragment) { return new StageInfo( diff --git a/core/trino-main/src/main/java/io/trino/execution/StageStats.java b/core/trino-main/src/main/java/io/trino/execution/StageStats.java index 21b63673a991..e8e3db5441ac 100644 --- a/core/trino-main/src/main/java/io/trino/execution/StageStats.java +++ b/core/trino-main/src/main/java/io/trino/execution/StageStats.java @@ -36,9 +36,7 @@ import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static io.airlift.units.DataSize.Unit.BYTE; -import static io.trino.execution.DistributionSnapshot.pruneMetrics; import static io.trino.execution.StageState.RUNNING; import static java.lang.Math.min; import static java.util.Objects.requireNonNull; @@ -277,9 +275,7 @@ public StageStats( this.failedPhysicalWrittenDataSize = requireNonNull(failedPhysicalWrittenDataSize, "failedPhysicalWrittenDataSize is null"); this.gcInfo = requireNonNull(gcInfo, "gcInfo is null"); - - requireNonNull(operatorSummaries, "operatorSummaries is null"); - this.operatorSummaries = operatorSummaries.stream().map(OperatorStats::pruneDigests).collect(toImmutableList()); + this.operatorSummaries = ImmutableList.copyOf(operatorSummaries); } @JsonProperty @@ -661,69 +657,6 @@ public BasicStageStats toBasicStageStats(StageState stageState) runningPercentage); } - public StageStats pruneDigests() - { - return new StageStats( - schedulingComplete, - getSplitDistribution, - splitSourceMetrics, - totalTasks, - runningTasks, - completedTasks, - failedTasks, - totalDrivers, - queuedDrivers, - runningDrivers, - blockedDrivers, - completedDrivers, - cumulativeUserMemory, - failedCumulativeUserMemory, - userMemoryReservation, - revocableMemoryReservation, - totalMemoryReservation, - peakUserMemoryReservation, - peakRevocableMemoryReservation, - spilledDataSize, - totalScheduledTime, - failedScheduledTime, - totalCpuTime, - failedCpuTime, - totalBlockedTime, - fullyBlocked, - blockedReasons, - physicalInputDataSize, - failedPhysicalInputDataSize, - physicalInputPositions, - failedPhysicalInputPositions, - physicalInputReadTime, - failedPhysicalInputReadTime, - internalNetworkInputDataSize, - failedInternalNetworkInputDataSize, - internalNetworkInputPositions, - failedInternalNetworkInputPositions, - processedInputDataSize, - failedProcessedInputDataSize, - processedInputPositions, - failedProcessedInputPositions, - inputBlockedTime, - failedInputBlockedTime, - bufferedDataSize, - outputBufferUtilization, - outputDataSize, - failedOutputDataSize, - outputPositions, - failedOutputPositions, - pruneMetrics(outputBufferMetrics), - outputBlockedTime, - failedOutputBlockedTime, - physicalWrittenDataSize, - failedPhysicalWrittenDataSize, - gcInfo, - operatorSummaries.stream() - .map(OperatorStats::pruneDigests) - .collect(toImmutableList())); - } - public static StageStats createInitial() { DataSize zeroBytes = DataSize.of(0, BYTE); diff --git a/core/trino-main/src/main/java/io/trino/execution/StagesInfo.java b/core/trino-main/src/main/java/io/trino/execution/StagesInfo.java index 1c7775b33885..4567b7864afe 100644 --- a/core/trino-main/src/main/java/io/trino/execution/StagesInfo.java +++ b/core/trino-main/src/main/java/io/trino/execution/StagesInfo.java @@ -66,13 +66,6 @@ public List getStages() return stages; } - public StagesInfo pruneDigests() - { - return new StagesInfo( - outputStageId, - stages.stream().map(StageInfo::pruneDigests).collect(toImmutableList())); - } - @JsonIgnore public StageInfo getOutputStage() { diff --git a/core/trino-main/src/main/java/io/trino/execution/TaskInfo.java b/core/trino-main/src/main/java/io/trino/execution/TaskInfo.java index 2ce9234395a8..a19879b97835 100644 --- a/core/trino-main/src/main/java/io/trino/execution/TaskInfo.java +++ b/core/trino-main/src/main/java/io/trino/execution/TaskInfo.java @@ -64,11 +64,6 @@ public TaskInfo pruneSpoolingOutputStats() return new TaskInfo(taskStatus, lastHeartbeat, outputBuffers.pruneSpoolingOutputStats(), noMoreSplits, stats, estimatedMemory, needsPlan); } - public TaskInfo pruneDigests() - { - return new TaskInfo(taskStatus, lastHeartbeat, outputBuffers.pruneDigests(), noMoreSplits, stats.pruneDigests(), estimatedMemory, needsPlan); - } - @Override public String toString() { diff --git a/core/trino-main/src/main/java/io/trino/execution/buffer/OutputBufferInfo.java b/core/trino-main/src/main/java/io/trino/execution/buffer/OutputBufferInfo.java index 45f88834fd69..a3a3ab96ac2e 100644 --- a/core/trino-main/src/main/java/io/trino/execution/buffer/OutputBufferInfo.java +++ b/core/trino-main/src/main/java/io/trino/execution/buffer/OutputBufferInfo.java @@ -16,7 +16,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; -import io.trino.execution.DistributionSnapshot; import io.trino.plugin.base.metrics.TDigestHistogram; import io.trino.spi.metrics.Metrics; @@ -193,23 +192,6 @@ public OutputBufferInfo pruneSpoolingOutputStats() metrics); } - public OutputBufferInfo pruneDigests() - { - return new OutputBufferInfo( - type, - state, - canAddBuffers, - canAddPages, - totalBufferedBytes, - totalBufferedPages, - totalRowsSent, - totalPagesSent, - pipelinedBufferStates, - Optional.empty(), - spoolingOutputStats, - metrics.map(DistributionSnapshot::pruneMetrics)); - } - @Override public String toString() { diff --git a/core/trino-main/src/main/java/io/trino/operator/OperatorStats.java b/core/trino-main/src/main/java/io/trino/operator/OperatorStats.java index 5f17674ba2c5..e847879cebd0 100644 --- a/core/trino-main/src/main/java/io/trino/operator/OperatorStats.java +++ b/core/trino-main/src/main/java/io/trino/operator/OperatorStats.java @@ -29,7 +29,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Verify.verify; -import static io.trino.execution.DistributionSnapshot.pruneMetrics; import static java.lang.Math.max; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -636,51 +635,6 @@ private static Mergeable mergeInfos(Mergeable base, List others) return (Mergeable) base.mergeWith(others); } - public OperatorStats pruneDigests() - { - return new OperatorStats( - stageId, - pipelineId, - operatorId, - planNodeId, - sourceId, - operatorType, - totalDrivers, - addInputCalls, - addInputWall, - addInputCpu, - physicalInputDataSize, - physicalInputPositions, - physicalInputReadTime, - internalNetworkInputDataSize, - internalNetworkInputPositions, - inputDataSize, - inputPositions, - sumSquaredInputPositions, - getOutputCalls, - getOutputWall, - getOutputCpu, - outputDataSize, - outputPositions, - dynamicFilterSplitsProcessed, - pruneMetrics(metrics), - pruneMetrics(connectorMetrics), - pruneMetrics(pipelineMetrics), - physicalWrittenDataSize, - blockedWall, - finishCalls, - finishWall, - finishCpu, - userMemoryReservation, - revocableMemoryReservation, - peakUserMemoryReservation, - peakRevocableMemoryReservation, - peakTotalMemoryReservation, - spilledDataSize, - blockedReason, - info); - } - public OperatorStats summarize() { if (info == null || info.isFinal()) { diff --git a/core/trino-main/src/main/java/io/trino/operator/PipelineStats.java b/core/trino-main/src/main/java/io/trino/operator/PipelineStats.java index bd2bb109ce4a..7f5ad8e5f1a8 100644 --- a/core/trino-main/src/main/java/io/trino/operator/PipelineStats.java +++ b/core/trino-main/src/main/java/io/trino/operator/PipelineStats.java @@ -28,7 +28,6 @@ import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; @Immutable @@ -496,52 +495,6 @@ public PipelineStats summarize() ImmutableList.of()); } - public PipelineStats pruneDigests() - { - return new PipelineStats( - pipelineId, - firstStartTime, - lastStartTime, - lastEndTime, - inputPipeline, - outputPipeline, - totalDrivers, - queuedDrivers, - queuedPartitionedDrivers, - queuedPartitionedSplitsWeight, - runningDrivers, - runningPartitionedDrivers, - runningPartitionedSplitsWeight, - blockedDrivers, - completedDrivers, - userMemoryReservation, - revocableMemoryReservation, - spilledDataSize, - queuedTime, - elapsedTime, - totalScheduledTime, - totalCpuTime, - totalBlockedTime, - fullyBlocked, - blockedReasons, - physicalInputDataSize, - physicalInputPositions, - physicalInputReadTime, - internalNetworkInputDataSize, - internalNetworkInputPositions, - processedInputDataSize, - processedInputPositions, - inputBlockedTime, - outputDataSize, - outputPositions, - outputBlockedTime, - physicalWrittenDataSize, - operatorSummaries.stream() - .map(OperatorStats::pruneDigests) - .collect(toImmutableList()), - drivers); - } - private static List summarizeOperatorStats(List operatorSummaries) { // Use an exact size ImmutableList builder to avoid a redundant copy in the PipelineStats constructor diff --git a/core/trino-main/src/main/java/io/trino/operator/TaskStats.java b/core/trino-main/src/main/java/io/trino/operator/TaskStats.java index 1c7ce12c97b5..e4660f1696e0 100644 --- a/core/trino-main/src/main/java/io/trino/operator/TaskStats.java +++ b/core/trino-main/src/main/java/io/trino/operator/TaskStats.java @@ -27,7 +27,6 @@ import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -643,55 +642,6 @@ public TaskStats summarizeFinal() summarizePipelineStats(pipelines)); } - public TaskStats pruneDigests() - { - return new TaskStats( - createTime, - firstStartTime, - lastStartTime, - terminatingStartTime, - lastEndTime, - endTime, - elapsedTime, - queuedTime, - totalDrivers, - queuedDrivers, - queuedPartitionedDrivers, - queuedPartitionedSplitsWeight, - runningDrivers, - runningPartitionedDrivers, - runningPartitionedSplitsWeight, - blockedDrivers, - completedDrivers, - cumulativeUserMemory, - userMemoryReservation, - peakUserMemoryReservation, - revocableMemoryReservation, - spilledDataSize, - totalScheduledTime, - totalCpuTime, - totalBlockedTime, - fullyBlocked, - blockedReasons, - physicalInputDataSize, - physicalInputPositions, - physicalInputReadTime, - internalNetworkInputDataSize, - internalNetworkInputPositions, - processedInputDataSize, - processedInputPositions, - inputBlockedTime, - outputDataSize, - outputPositions, - outputBlockedTime, - writerInputDataSize, - physicalWrittenDataSize, - maxWriterCount, - fullGcCount, - fullGcTime, - pipelines.stream().map(PipelineStats::pruneDigests).collect(toImmutableList())); - } - private static List summarizePipelineStats(List pipelines) { // Use an exact size ImmutableList builder to avoid a redundant copy in the TaskStats constructor diff --git a/core/trino-main/src/main/java/io/trino/server/DataSizeSerializer.java b/core/trino-main/src/main/java/io/trino/server/DataSizeSerializer.java new file mode 100644 index 000000000000..a71ea56f1e5b --- /dev/null +++ b/core/trino-main/src/main/java/io/trino/server/DataSizeSerializer.java @@ -0,0 +1,43 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.server; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import io.airlift.units.DataSize; + +import java.io.IOException; + +public class DataSizeSerializer + extends JsonSerializer +{ + public static final String SUCCINCT_DATA_SIZE_ENABLED = "dataSize.succinct.enabled"; + + @Override + public void serialize(DataSize dataSize, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) + throws IOException + { + if (dataSize == null) { + jsonGenerator.writeNull(); + return; + } + + if (Boolean.TRUE.equals(serializerProvider.getAttribute(SUCCINCT_DATA_SIZE_ENABLED))) { + jsonGenerator.writeString(dataSize.succinct().toString()); + return; + } + jsonGenerator.writeString(dataSize.toBytesValueString()); + } +} diff --git a/core/trino-main/src/main/java/io/trino/server/ServerMainModule.java b/core/trino-main/src/main/java/io/trino/server/ServerMainModule.java index 32ba910f978f..1ec28ce39a97 100644 --- a/core/trino-main/src/main/java/io/trino/server/ServerMainModule.java +++ b/core/trino-main/src/main/java/io/trino/server/ServerMainModule.java @@ -410,6 +410,9 @@ protected void setup(Binder binder) jsonBinder(binder).addSerializerBinding(Slice.class).to(SliceSerializer.class); jsonBinder(binder).addDeserializerBinding(Slice.class).to(SliceDeserializer.class); + // configurable DataSize serialization + jsonBinder(binder).addSerializerBinding(DataSize.class).to(DataSizeSerializer.class); + // node version binder.bind(NodeVersion.class).toInstance(new NodeVersion(nodeVersion)); diff --git a/core/trino-main/src/main/java/io/trino/server/ui/UiQueryResource.java b/core/trino-main/src/main/java/io/trino/server/ui/UiQueryResource.java index c600efb4dc1f..56b9a87e3286 100644 --- a/core/trino-main/src/main/java/io/trino/server/ui/UiQueryResource.java +++ b/core/trino-main/src/main/java/io/trino/server/ui/UiQueryResource.java @@ -13,11 +13,18 @@ */ package io.trino.server.ui; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.cfg.ContextAttributes; import com.google.common.collect.ImmutableList; import com.google.inject.Inject; +import io.airlift.json.JsonCodec; +import io.airlift.json.JsonCodecFactory; import io.trino.dispatcher.DispatchManager; import io.trino.execution.QueryInfo; import io.trino.execution.QueryState; +import io.trino.operator.OperatorInfo; +import io.trino.plugin.base.metrics.TDigestHistogram; import io.trino.security.AccessControl; import io.trino.server.BasicQueryInfo; import io.trino.server.DisableHttpCache; @@ -26,6 +33,7 @@ import io.trino.server.security.ResourceSecurity; import io.trino.spi.QueryId; import io.trino.spi.TrinoException; +import io.trino.spi.metrics.Metric; import io.trino.spi.security.AccessDeniedException; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.ForbiddenException; @@ -44,12 +52,16 @@ import java.util.NoSuchElementException; import java.util.Optional; +import static com.fasterxml.jackson.annotation.JsonIgnoreProperties.Value.forIgnoredProperties; import static io.trino.connector.system.KillQueryProcedure.createKillQueryException; import static io.trino.connector.system.KillQueryProcedure.createPreemptQueryException; +import static io.trino.plugin.base.metrics.TDigestHistogram.DIGEST_PROPERTY; import static io.trino.security.AccessControlUtil.checkCanKillQueryOwnedBy; import static io.trino.security.AccessControlUtil.checkCanViewQueryOwnedBy; import static io.trino.security.AccessControlUtil.filterQueries; +import static io.trino.server.DataSizeSerializer.SUCCINCT_DATA_SIZE_ENABLED; import static io.trino.server.security.ResourceSecurity.AccessType.WEB_UI; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; import static java.util.Objects.requireNonNull; @Path("/ui/api/query") @@ -57,13 +69,17 @@ @DisableHttpCache public class UiQueryResource { + private final JsonCodec queryInfoCodec; + private final JsonCodec prettyQueryInfoCodec; private final DispatchManager dispatchManager; private final AccessControl accessControl; private final HttpRequestSessionContextFactory sessionContextFactory; @Inject - public UiQueryResource(DispatchManager dispatchManager, AccessControl accessControl, HttpRequestSessionContextFactory sessionContextFactory) + public UiQueryResource(ObjectMapper objectMapper, DispatchManager dispatchManager, AccessControl accessControl, HttpRequestSessionContextFactory sessionContextFactory) { + this.queryInfoCodec = buildQueryInfoCodec(objectMapper, false); + this.prettyQueryInfoCodec = buildQueryInfoCodec(objectMapper, true); this.dispatchManager = requireNonNull(dispatchManager, "dispatchManager is null"); this.accessControl = requireNonNull(accessControl, "accessControl is null"); this.sessionContextFactory = requireNonNull(sessionContextFactory, "sessionContextFactory is null"); @@ -96,7 +112,12 @@ public Response getQueryInfo(@PathParam("queryId") QueryId queryId, @Context Htt if (queryInfo.isPresent()) { try { checkCanViewQueryOwnedBy(sessionContextFactory.extractAuthorizedIdentity(servletRequest, httpHeaders), queryInfo.get().getSession().toIdentity(), accessControl); - return Response.ok(queryInfo.get().pruneDigests()).build(); + + if (servletRequest.getQueryString().contains("pretty")) { + // Use pretty JSON codec that reduces noise + return Response.ok(prettyQueryInfoCodec.toJson(queryInfo.get()), APPLICATION_JSON_TYPE).build(); + } + return Response.ok(queryInfoCodec.toJson(queryInfo.get()), APPLICATION_JSON_TYPE).build(); } catch (AccessDeniedException e) { throw new ForbiddenException(); @@ -144,4 +165,36 @@ private Response failQuery(QueryId queryId, TrinoException queryException, HttpS throw new GoneException(); } } + + private JsonCodec buildQueryInfoCodec(ObjectMapper objectMapper, boolean pretty) + { + JsonCodecFactory jsonCodecFactory = new JsonCodecFactory(() -> { + // Enable succinct DataSize serialization for QueryInfo to make it more human friendly + ContextAttributes attrs = ContextAttributes.getEmpty(); + if (pretty) { + attrs = attrs.withSharedAttribute(SUCCINCT_DATA_SIZE_ENABLED, Boolean.TRUE); + } + + ObjectMapper mapper = objectMapper + .copy() + .setDefaultAttributes(attrs); + // Don't serialize TDigestHistogram.digest which isn't useful and human readable + mapper.configOverride(TDigestHistogram.class).setIgnorals(forIgnoredProperties(DIGEST_PROPERTY)); + + // Do not output @class property for metric types + mapper.addMixIn(Metric.class, DropTypeInfo.class); + // Do not output @type property for OperatorInfo + mapper.addMixIn(OperatorInfo.class, DropTypeInfo.class); + return mapper; + }); + + if (pretty) { + jsonCodecFactory = jsonCodecFactory.prettyPrint(); + } + + return jsonCodecFactory.jsonCodec(QueryInfo.class); + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) + public interface DropTypeInfo {} } diff --git a/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/metrics/TDigestHistogram.java b/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/metrics/TDigestHistogram.java index 0c668f1e0583..e9fc606d597a 100644 --- a/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/metrics/TDigestHistogram.java +++ b/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/metrics/TDigestHistogram.java @@ -33,6 +33,10 @@ public class TDigestHistogram implements Distribution { + // This is important so that we can instruct Jackson to ignore this property + // in certain places (e.g. UiQueryResource) + public static final String DIGEST_PROPERTY = "digest"; + private final TDigest digest; public static TDigestHistogram fromValue(double value) @@ -57,7 +61,7 @@ public synchronized TDigest getDigest() return TDigest.copyOf(digest); } - @JsonProperty("digest") + @JsonProperty(DIGEST_PROPERTY) public synchronized byte[] serialize() { return digest.serialize().getBytes(); @@ -65,7 +69,7 @@ public synchronized byte[] serialize() @JsonCreator @DoNotCall - public static TDigestHistogram deserialize(@JsonProperty("digest") byte[] digest) + public static TDigestHistogram deserialize(@JsonProperty(DIGEST_PROPERTY) byte[] digest) { return new TDigestHistogram(TDigest.deserialize(wrappedBuffer(digest))); }