This is an automated email from the ASF dual-hosted git repository.
gianm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new 96f14930986 minor: multi segment benchmark with counter tracking for
msq-task and msq-dart queries (#20793)
96f14930986 is described below
commit 96f1493098674b9994e85f3be59015d04b9e18d4
Author: Lucas Capistrant <[email protected]>
AuthorDate: Fri Oct 30 14:37:04 2026 -0500
minor: multi segment benchmark with counter tracking for msq-task and
msq-dart queries (#20793)
---
benchmarks/pom.xml | 7 +
.../druid/benchmark/query/DartScanBenchmark.java | 116 ++++++
.../benchmark/query/MSQScanBenchmarkBase.java | 416 +++++++++++++++++++++
.../benchmark/query/MSQTaskScanBenchmark.java | 83 ++++
4 files changed, 622 insertions(+)
diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml
index 82d47ea4b0e..326f7bb3cbc 100644
--- a/benchmarks/pom.xml
+++ b/benchmarks/pom.xml
@@ -244,6 +244,13 @@
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
+ <dependency>
+ <!-- Needed by the MSQ test framework that MSQ benchmarks run on -->
+ <groupId>org.apache.druid.extensions.contrib</groupId>
+ <artifactId>druid-compressed-bigdecimal</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<properties>
diff --git
a/benchmarks/src/test/java/org/apache/druid/benchmark/query/DartScanBenchmark.java
b/benchmarks/src/test/java/org/apache/druid/benchmark/query/DartScanBenchmark.java
new file mode 100644
index 00000000000..65eee491346
--- /dev/null
+++
b/benchmarks/src/test/java/org/apache/druid/benchmark/query/DartScanBenchmark.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.druid.benchmark.query;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.druid.java.util.common.ISE;
+import org.apache.druid.msq.dart.controller.DartControllerRegistry;
+import org.apache.druid.msq.dart.controller.QueryInfoAndReport;
+import org.apache.druid.msq.dart.guice.DartModules;
+import org.apache.druid.msq.indexing.report.MSQTaskReport;
+import org.apache.druid.msq.indexing.report.MSQTaskReportPayload;
+import org.apache.druid.msq.test.DartComponentSupplier;
+import org.apache.druid.query.context.QueryContextParameters;
+import org.apache.druid.server.SpecificSegmentsQuerySegmentWalker;
+import org.apache.druid.sql.calcite.QueryTestBuilder;
+import org.apache.druid.sql.calcite.SqlTestFrameworkConfig;
+import org.apache.druid.sql.calcite.TempDirProducer;
+import org.openjdk.jmh.infra.Blackhole;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.UUID;
+
+/**
+ * {@link MSQScanBenchmarkBase} on the Dart engine.
+ */
[email protected](DartScanBenchmark.ComponentSupplier.class)
+public class DartScanBenchmark extends MSQScanBenchmarkBase
+{
+ @Override
+ protected MSQTaskReportPayload runQueryAndGetReport(
+ final String sql,
+ final Map<String, Object> context,
+ final Blackhole blackhole
+ )
+ {
+ final String sqlQueryId = UUID.randomUUID().toString();
+ final Map<String, Object> dartContext = new HashMap<>(context);
+ dartContext.put(QueryContextParameters.SQL_QUERY_ID.getName(), sqlQueryId);
+
+ final List<Object[]> rows = new QueryTestBuilder(createCalciteTestConfig())
+ .skipVectorize(true)
+ .queryContext(dartContext)
+ .sql(sql)
+ .results()
+ .results;
+ blackhole.consume(rows);
+
+ // The controller files its complete report as it deregisters, which can
happen just after the results are
+ // returned. Until then, the registry returns the running query's live
report.
+ final DartControllerRegistry registry =
queryFramework().injector().getInstance(DartControllerRegistry.class);
+ final long deadline = System.currentTimeMillis() + 10_000;
+ while (System.currentTimeMillis() < deadline) {
+ final QueryInfoAndReport details =
registry.getQueryDetailsBySqlQueryId(sqlQueryId);
+ if (details != null && details.getReportMap() != null) {
+ final MSQTaskReportPayload payload =
+ (MSQTaskReportPayload)
details.getReportMap().get(MSQTaskReport.REPORT_KEY).getPayload();
+ if (payload.getStatus().getStatus().isComplete()) {
+ return payload;
+ }
+ }
+ try {
+ Thread.sleep(1);
+ }
+ catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+ }
+ throw new ISE("No report for sqlQueryId[%s]", sqlQueryId);
+ }
+
+ protected static class ComponentSupplier extends DartComponentSupplier
+ {
+ public ComponentSupplier(final TempDirProducer tempFolderProducer)
+ {
+ super(tempFolderProducer);
+ }
+
+ @Override
+ public void gatherProperties(final Properties properties)
+ {
+ super.gatherProperties(properties);
+ // Retain the report of each finished query, so its counters can be read.
+ properties.put(DartModules.DART_PROPERTY_BASE +
".controller.maxRetainedReportCount", "10");
+ }
+
+ @Override
+ public SpecificSegmentsQuerySegmentWalker addSegmentsToWalker(
+ final SpecificSegmentsQuerySegmentWalker walker,
+ final ObjectMapper jsonMapper
+ )
+ {
+ return addSegments(super.addSegmentsToWalker(walker, jsonMapper));
+ }
+ }
+}
diff --git
a/benchmarks/src/test/java/org/apache/druid/benchmark/query/MSQScanBenchmarkBase.java
b/benchmarks/src/test/java/org/apache/druid/benchmark/query/MSQScanBenchmarkBase.java
new file mode 100644
index 00000000000..f6cf8b45647
--- /dev/null
+++
b/benchmarks/src/test/java/org/apache/druid/benchmark/query/MSQScanBenchmarkBase.java
@@ -0,0 +1,416 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.druid.benchmark.query;
+
+import
org.apache.druid.data.input.impl.ClusteredValueGroupsBaseTableProjectionSpec;
+import org.apache.druid.java.util.common.DateTimes;
+import org.apache.druid.java.util.common.ISE;
+import org.apache.druid.java.util.common.granularity.Granularities;
+import org.apache.druid.java.util.common.io.Closer;
+import org.apache.druid.msq.counters.ChannelCounters;
+import org.apache.druid.msq.counters.CounterNames;
+import org.apache.druid.msq.counters.CounterSnapshots;
+import org.apache.druid.msq.indexing.report.MSQTaskReportPayload;
+import org.apache.druid.query.QueryContext;
+import org.apache.druid.query.context.QueryContextParameters;
+import org.apache.druid.segment.IndexSpec;
+import org.apache.druid.segment.QueryableIndex;
+import org.apache.druid.segment.generator.GeneratorSchemaInfo;
+import org.apache.druid.segment.generator.SegmentGenerator;
+import org.apache.druid.segment.transform.TransformSpec;
+import org.apache.druid.server.SpecificSegmentsQuerySegmentWalker;
+import org.apache.druid.sql.calcite.BaseCalciteQueryTest;
+import org.apache.druid.sql.calcite.SqlTestFrameworkConfig;
+import org.apache.druid.sql.calcite.util.CalciteTests;
+import org.apache.druid.timeline.DataSegment;
+import org.apache.druid.timeline.partition.LinearShardSpec;
+import org.joda.time.DateTime;
+import org.joda.time.Interval;
+import org.openjdk.jmh.annotations.AuxCounters;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+import org.openjdk.jmh.infra.IterationParams;
+import org.openjdk.jmh.runner.IterationType;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Scan queries through an MSQ engine over a datasource split into one-day
time chunks, each holding one or more
+ * segments that span the whole chunk. Subclasses choose the engine. Rows and
the clustered layout
+ * come from {@link SqlBenchmarkDatasets#clusteringDatasource}, and {@link
#layout} chooses how segments store them.
+ * <p>
+ * Besides time per query, reports the work done by the scan stage (stage 0)
as JMH auxiliary counters, read from the
+ * query report: segments acquired, rows in those segments, and rows written,
each averaged over queries. Run with
+ * {@code -prof gc} to also report bytes allocated per query.
+ * <p>
+ * Segments default to V9, sorted by {@code __time} first. Choose other
layouts with the {@code layout} parameter, for
+ * example {@code -p
layout=V9_TIME_ORDERED,V10_CLUSTERED,V10_UNCLUSTERED_CLUSTERING_ORDER}; see
{@link Layout}.
+ * <p>
+ * To run, build the benchmarks jar and select benchmarks and parameters,
passing the JVM flags listed in the "Strong
+ * encapsulation" section of docs/operations/java.md:
+ * <pre>
+ * mvn package -pl benchmarks -am -DskipTests
+ * java -jar benchmarks/target/benchmarks.jar
'DartScanBenchmark.orderByTimeDescWithLimit' \
+ * -p numTimeChunks=30 -p limit=10000 -p layout=V10_CLUSTERED -prof gc \
+ * -jvmArgsAppend "--add-opens=java.base/java.nio=ALL-UNNAMED ..."
+ * </pre>
+ * Set the {@code DRUID_BENCHMARK_CACHE_DIR} environment variable to reuse
generated segments across runs.
+ */
+@State(Scope.Benchmark)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@Fork(value = 1)
+@Warmup(iterations = 1, time = 5)
+@Measurement(iterations = 3, time = 5)
+public abstract class MSQScanBenchmarkBase extends BaseCalciteQueryTest
+{
+ static final String DATASOURCE = "scan_ds";
+ private static final DateTime FIRST_DAY = DateTimes.of("2026-01-01");
+
+ /**
+ * How segments store their rows.
+ */
+ public enum Layout
+ {
+ /**
+ * V9 segment sorted by {@code __time} first.
+ */
+ V9_TIME_ORDERED,
+
+ /**
+ * V10 segment sorted by {@code __time} first.
+ */
+ V10_TIME_ORDERED,
+
+ /**
+ * V10 clustered base table: a value group per combination of clusterKey1
and clusterKey2, each sorted by
+ * {@code __time}.
+ */
+ V10_CLUSTERED,
+
+ /**
+ * V10 segment sorted by clusterKey1, clusterKey2, then {@code __time},
without value groups. Rows are in the same
+ * order as {@link #V10_CLUSTERED}, but the segment is not sorted by
{@code __time} first.
+ */
+ V10_UNCLUSTERED_CLUSTERING_ORDER
+ }
+
+ /**
+ * Segments generated by {@link #setup()}, attached to the walker by the
subclass's component supplier. Static
+ * because the test framework creates the component supplier itself.
+ */
+ static final Map<DataSegment, QueryableIndex> SEGMENTS = new
LinkedHashMap<>();
+
+ /**
+ * Number of one-day time chunks.
+ */
+ @Param({"1", "30"})
+ private int numTimeChunks;
+
+ /**
+ * Number of segments per time chunk. Each one spans its whole time chunk.
+ */
+ @Param({"1", "4"})
+ private int numPartitionsPerTimeChunk;
+
+ @Param({"100000"})
+ private int rowsPerSegment;
+
+ @Param({"10", "10000"})
+ private int limit;
+
+ @Param({"2"})
+ private int maxNumTasks;
+
+ @Param({"V9_TIME_ORDERED"})
+ private Layout layout;
+
+ /**
+ * Number of distinct values of clusterKey1, the primary clustering column.
+ */
+ @Param({"100"})
+ private int clusteringCardinality;
+
+ private final Closer closer = Closer.create();
+ private List<Annotation> annotations;
+
+ /**
+ * Mean work done by the scan stage per query. Work can vary between runs of
the same query, because concurrent
+ * processors that share a limit decide what to read based on each other's
progress.
+ * <p>
+ * JMH sums {@link AuxCounters.Type#EVENTS} counters over measurement
iterations, so the counters hold the mean of
+ * the first measurement iteration's queries and zero in the others. Their
sum is then the mean work of one query.
+ */
+ @State(Scope.Thread)
+ @AuxCounters(AuxCounters.Type.EVENTS)
+ public static class WorkCounters
+ {
+ public double segmentsAcquired;
+ public double rowsInAcquiredSegments;
+ public double rowsWritten;
+
+ private final long[] workSums = new long[3];
+ private long numQueries;
+ private boolean reported;
+ private boolean reporting;
+
+ @Setup(Level.Iteration)
+ public void startIteration(final IterationParams iterationParams)
+ {
+ segmentsAcquired = 0;
+ rowsInAcquiredSegments = 0;
+ rowsWritten = 0;
+ Arrays.fill(workSums, 0);
+ numQueries = 0;
+ reporting = !reported && iterationParams.getType() ==
IterationType.MEASUREMENT;
+ }
+
+ @TearDown(Level.Iteration)
+ public void endIteration()
+ {
+ reported |= reporting;
+ }
+
+ private void record(final long segmentsAcquired, final long
rowsInAcquiredSegments, final long rowsWritten)
+ {
+ if (!reporting) {
+ return;
+ }
+
+ workSums[0] += segmentsAcquired;
+ workSums[1] += rowsInAcquiredSegments;
+ workSums[2] += rowsWritten;
+ numQueries++;
+
+ // JMH reads the counters when the iteration ends, so keep them at the
running mean.
+ this.segmentsAcquired = (double) workSums[0] / numQueries;
+ this.rowsInAcquiredSegments = (double) workSums[1] / numQueries;
+ this.rowsWritten = (double) workSums[2] / numQueries;
+ }
+ }
+
+ @Setup(Level.Trial)
+ public void setup()
+ {
+ // JMH runs a generated subclass, so find the class that chose the engine.
+ Class<?> engineClass = getClass();
+ while
(!engineClass.isAnnotationPresent(SqlTestFrameworkConfig.ComponentSupplier.class))
{
+ engineClass = engineClass.getSuperclass();
+ }
+ annotations = Arrays.asList(engineClass.getAnnotations());
+
+ final String clusteredDatasource =
SqlBenchmarkDatasets.clusteringDatasource("CLUSTERED", clusteringCardinality,
2);
+ final SqlBenchmarkDatasets.BenchmarkSchema clustered =
SqlBenchmarkDatasets.getSchema(clusteredDatasource);
+ final GeneratorSchemaInfo schema = clustered.getGeneratorSchemaInfo();
+ final ClusteredValueGroupsBaseTableProjectionSpec clusterSpec =
clustered.getClusterSpec();
+ final SegmentGenerator segmentGenerator = closer.register(new
SegmentGenerator());
+ SEGMENTS.clear();
+ for (int chunk = 0; chunk < numTimeChunks; chunk++) {
+ final Interval day = new Interval(FIRST_DAY.plusDays(chunk),
FIRST_DAY.plusDays(chunk + 1));
+ final GeneratorSchemaInfo daySchema = new GeneratorSchemaInfo(
+ schema.getColumnSchemas(),
+ schema.getAggs(),
+ day,
+ schema.isWithRollup()
+ );
+ for (int partition = 0; partition < numPartitionsPerTimeChunk;
partition++) {
+ final DataSegment dataSegment = DataSegment.builder()
+ .dataSource(DATASOURCE)
+ .interval(day)
+ .version("1")
+ .shardSpec(new
LinearShardSpec(partition))
+ .size(0)
+ .build();
+ final QueryableIndex index = switch (layout) {
+ case V9_TIME_ORDERED -> segmentGenerator.generate(dataSegment,
daySchema, Granularities.NONE, rowsPerSegment);
+ case V10_TIME_ORDERED -> segmentGenerator.generateV10(
+ dataSegment,
+ daySchema,
+ daySchema.getDimensionsSpec(),
+ TransformSpec.NONE,
+ IndexSpec.getDefault(),
+ Granularities.NONE,
+ null,
+ rowsPerSegment,
+ CalciteTests.getJsonMapper()
+ );
+ case V10_UNCLUSTERED_CLUSTERING_ORDER ->
segmentGenerator.generateV10(
+ dataSegment,
+ daySchema,
+ clusterSpec.getDimensionsSpec(),
+ TransformSpec.NONE,
+ IndexSpec.getDefault(),
+ Granularities.NONE,
+ null,
+ rowsPerSegment,
+ CalciteTests.getJsonMapper()
+ );
+ case V10_CLUSTERED -> segmentGenerator.generateV10(
+ dataSegment,
+ daySchema,
+ clusterSpec.getDimensionsSpec(),
+ TransformSpec.NONE,
+ IndexSpec.getDefault(),
+ Granularities.NONE,
+ clusterSpec,
+ rowsPerSegment,
+ CalciteTests.getJsonMapper()
+ );
+ };
+ SEGMENTS.put(dataSegment, closer.register(index));
+ }
+ }
+ }
+
+ @TearDown(Level.Trial)
+ public void tearDown() throws Exception
+ {
+ closer.close();
+ }
+
+ @Benchmark
+ public void orderByTimeDescWithLimit(final WorkCounters counters, final
Blackhole blackhole)
+ {
+ runQuery("SELECT * FROM " + DATASOURCE + " ORDER BY __time DESC LIMIT " +
limit, counters, blackhole);
+ }
+
+ @Benchmark
+ public void orderByTimeAscWithLimit(final WorkCounters counters, final
Blackhole blackhole)
+ {
+ runQuery("SELECT * FROM " + DATASOURCE + " ORDER BY __time ASC LIMIT " +
limit, counters, blackhole);
+ }
+
+ @Benchmark
+ public void orderByTimeDescThenDimWithLimit(final WorkCounters counters,
final Blackhole blackhole)
+ {
+ runQuery(
+ "SELECT * FROM " + DATASOURCE + " ORDER BY __time DESC, dimSecondary
LIMIT " + limit,
+ counters,
+ blackhole
+ );
+ }
+
+ /**
+ * Newest rows for one value of the primary clustering column.
+ */
+ @Benchmark
+ public void whereClusterKeyOrderByTimeDescWithLimit(final WorkCounters
counters, final Blackhole blackhole)
+ {
+ runQuery(
+ "SELECT * FROM " + DATASOURCE + " WHERE clusterKey1 = '1' ORDER BY
__time DESC LIMIT " + limit,
+ counters,
+ blackhole
+ );
+ }
+
+ @Benchmark
+ public void unorderedWithLimit(final WorkCounters counters, final Blackhole
blackhole)
+ {
+ runQuery("SELECT * FROM " + DATASOURCE + " LIMIT " + limit, counters,
blackhole);
+ }
+
+ @Benchmark
+ public void orderByDimWithLimit(final WorkCounters counters, final Blackhole
blackhole)
+ {
+ runQuery("SELECT * FROM " + DATASOURCE + " ORDER BY dimSecondary LIMIT " +
limit, counters, blackhole);
+ }
+
+ /**
+ * Ordered by time without a limit; does not use {@link #limit}. The filter
keeps a small fraction of rows.
+ */
+ @Benchmark
+ public void whereDimOrderByTimeDescNoLimit(final WorkCounters counters,
final Blackhole blackhole)
+ {
+ runQuery(
+ "SELECT * FROM " + DATASOURCE + " WHERE dimSecondary = '7' ORDER BY
__time DESC",
+ counters,
+ blackhole
+ );
+ }
+
+ /**
+ * Runs {@code sql} and returns the complete report of the query that ran it.
+ */
+ protected abstract MSQTaskReportPayload runQueryAndGetReport(
+ String sql,
+ Map<String, Object> context,
+ Blackhole blackhole
+ );
+
+ private void runQuery(final String sql, final WorkCounters counters, final
Blackhole blackhole)
+ {
+ queryFrameworkRule.setConfig(new SqlTestFrameworkConfig(annotations));
+ final Map<String, Object> context =
QueryContext.ofMap(QueryContextParameters.MAX_NUM_TASKS, maxNumTasks);
+ final MSQTaskReportPayload payload = runQueryAndGetReport(sql, context,
blackhole);
+
+ if (!payload.getStatus().getStatus().isSuccess()) {
+ throw new ISE("Query failed: %s", payload.getStatus().getErrorReport());
+ }
+
+ // Stage 0 scans the datasource; sum its counters over workers.
+ final Map<Integer, CounterSnapshots> scanStageWorkers =
payload.getCounters().copyMap().get(0);
+ long segmentsAcquired = 0;
+ long rowsInAcquiredSegments = 0;
+ long rowsWritten = 0;
+ for (final CounterSnapshots worker : scanStageWorkers.values()) {
+ final ChannelCounters.Snapshot input =
+ (ChannelCounters.Snapshot)
worker.getMap().get(CounterNames.inputChannel(0));
+ final ChannelCounters.Snapshot output =
+ (ChannelCounters.Snapshot)
worker.getMap().get(CounterNames.outputChannel());
+ segmentsAcquired += sum(input.getFiles());
+ rowsInAcquiredSegments += sum(input.getRows());
+ rowsWritten += output == null ? 0 : sum(output.getRows());
+ }
+ counters.record(segmentsAcquired, rowsInAcquiredSegments, rowsWritten);
+ }
+
+ private static long sum(final long[] values)
+ {
+ return values == null ? 0 : Arrays.stream(values).sum();
+ }
+
+ /**
+ * Adds {@link #SEGMENTS} to {@code walker}.
+ */
+ static SpecificSegmentsQuerySegmentWalker addSegments(final
SpecificSegmentsQuerySegmentWalker walker)
+ {
+ for (final Map.Entry<DataSegment, QueryableIndex> entry :
SEGMENTS.entrySet()) {
+ walker.add(entry.getKey(), entry.getValue());
+ }
+ return walker;
+ }
+}
diff --git
a/benchmarks/src/test/java/org/apache/druid/benchmark/query/MSQTaskScanBenchmark.java
b/benchmarks/src/test/java/org/apache/druid/benchmark/query/MSQTaskScanBenchmark.java
new file mode 100644
index 00000000000..e45e671a102
--- /dev/null
+++
b/benchmarks/src/test/java/org/apache/druid/benchmark/query/MSQTaskScanBenchmark.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.druid.benchmark.query;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.druid.msq.indexing.report.MSQTaskReport;
+import org.apache.druid.msq.indexing.report.MSQTaskReportPayload;
+import org.apache.druid.msq.sql.MSQTaskSqlEngine;
+import org.apache.druid.msq.test.MSQTestOverlordServiceClient;
+import org.apache.druid.msq.test.StandardMSQComponentSupplier;
+import org.apache.druid.server.SpecificSegmentsQuerySegmentWalker;
+import org.apache.druid.sql.calcite.QueryTestBuilder;
+import org.apache.druid.sql.calcite.SqlTestFrameworkConfig;
+import org.apache.druid.sql.calcite.TempDirProducer;
+import org.openjdk.jmh.infra.Blackhole;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * {@link MSQScanBenchmarkBase} on the MSQ task engine.
+ */
[email protected](MSQTaskScanBenchmark.ComponentSupplier.class)
+public class MSQTaskScanBenchmark extends MSQScanBenchmarkBase
+{
+ @Override
+ protected MSQTaskReportPayload runQueryAndGetReport(
+ final String sql,
+ final Map<String, Object> context,
+ final Blackhole blackhole
+ )
+ {
+ // Without a results-extraction step, the task engine's only result row is
the task ID.
+ final List<Object[]> taskIdRows = new
QueryTestBuilder(createCalciteTestConfig())
+ .skipVectorize(true)
+ .queryContext(context)
+ .sql(sql)
+ .results()
+ .results;
+
+ final String taskId = taskIdRows.get(0)[0].toString();
+ final MSQTestOverlordServiceClient overlordClient =
+ (MSQTestOverlordServiceClient) ((MSQTaskSqlEngine)
queryFramework().engine()).overlordClient();
+ final MSQTaskReportPayload payload =
+ (MSQTaskReportPayload)
overlordClient.getReportForTask(taskId).get(MSQTaskReport.REPORT_KEY).getPayload();
+ blackhole.consume(payload.getResults());
+ return payload;
+ }
+
+ protected static class ComponentSupplier extends StandardMSQComponentSupplier
+ {
+ public ComponentSupplier(final TempDirProducer tempFolderProducer)
+ {
+ super(tempFolderProducer);
+ }
+
+ @Override
+ public SpecificSegmentsQuerySegmentWalker addSegmentsToWalker(
+ final SpecificSegmentsQuerySegmentWalker walker,
+ final ObjectMapper jsonMapper
+ )
+ {
+ return addSegments(super.addSegmentsToWalker(walker, jsonMapper));
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]