From 30061db7fdfd12ed30aa3806e04c961493b51f98 Mon Sep 17 00:00:00 2001 From: Arup Chauhan Date: Sat, 31 Oct 2026 18:34:25 -0500 Subject: [PATCH] SOLR-17665: Prepare benchmark controls for search executor overhead --- .../org/apache/solr/bench/SolrBenchState.java | 31 +++++++++ .../search/ExitableDirectoryReaderSearch.java | 67 +++++++++++++------ 2 files changed, 76 insertions(+), 22 deletions(-) diff --git a/solr/benchmark/src/java/org/apache/solr/bench/SolrBenchState.java b/solr/benchmark/src/java/org/apache/solr/bench/SolrBenchState.java index 3caf03a2f539..8b3b4304112c 100755 --- a/solr/benchmark/src/java/org/apache/solr/bench/SolrBenchState.java +++ b/solr/benchmark/src/java/org/apache/solr/bench/SolrBenchState.java @@ -32,9 +32,11 @@ import java.util.ArrayList; import java.util.List; import java.util.SplittableRandom; +import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.apache.solr.client.solrj.SolrServerException; @@ -259,6 +261,14 @@ public void metricsEnabled(boolean metricsEnabled) { * @param nodeCount the node count */ public void startSolr(int nodeCount) { + startSolr(nodeCount, 0); + } + + /** Starts the benchmark nodes with an explicit search worker count. */ + public void startSolr(int nodeCount, int searchThreads) { + if (searchThreads < 0) { + throw new IllegalArgumentException("searchThreads must be non-negative"); + } log("starting mini cluster at base directory: " + miniClusterBaseDir.toAbsolutePath()); if (!allowClusterReuse && Files.exists(miniClusterBaseDir)) { @@ -277,6 +287,12 @@ public void startSolr(int nodeCount) { try { cluster = new MiniSolrCloudCluster.Builder(nodeCount, miniClusterBaseDir) + .withSolrXml( + MiniSolrCloudCluster.DEFAULT_CLOUD_SOLR_XML.replace( + "", + "" + + searchThreads + + "")) .formatZkServer(false) .addConfig("conf", getFile("src/resources/configs/cloud-minimal/conf")) .configure(); @@ -341,6 +357,21 @@ public void createCollection(String collection, int numShards, int numReplicas) } } + /** Returns completed search-pool tasks; direct execution without a pool contributes zero. */ + public long completedSearchTasks() { + long completed = 0; + for (JettySolrRunner jetty : cluster.getJettySolrRunners()) { + Executor executor = jetty.getCoreContainer().getIndexSearcherExecutor(); + if (executor != null) { + if (!(executor instanceof ThreadPoolExecutor pool)) { + throw new IllegalStateException("Unsupported search executor: " + executor.getClass()); + } + completed += pool.getCompletedTaskCount(); + } + } + return completed; + } + /** Setting useHttp1 to true will make the {@link #client} use http1 */ public void setUseHttp1(boolean useHttp1) { if (client != null) { diff --git a/solr/benchmark/src/java/org/apache/solr/bench/search/ExitableDirectoryReaderSearch.java b/solr/benchmark/src/java/org/apache/solr/bench/search/ExitableDirectoryReaderSearch.java index 0365fe004105..4055939ab9c4 100644 --- a/solr/benchmark/src/java/org/apache/solr/bench/search/ExitableDirectoryReaderSearch.java +++ b/solr/benchmark/src/java/org/apache/solr/bench/search/ExitableDirectoryReaderSearch.java @@ -63,46 +63,61 @@ public static class BenchState { Docs queryFields; - int NUM_DOCS = 500_000; - int WORDS = NUM_DOCS / 100; + @Param({"500000"}) + int numDocs; + + @Param({"0"}) + int searchThreads; + + @Param({"false"}) + boolean multiThreaded; + + @Param({"1000"}) + int timeAllowed; + + int words; + long tasksBeforeIteration; @Setup(Level.Trial) public void setupTrial(SolrBenchState solrBenchState) throws Exception { + if (numDocs < 1) { + throw new IllegalArgumentException("numDocs must be positive"); + } + words = Math.max(1, numDocs / 100); solrBenchState.setUseHttp1(true); System.setProperty("documentCache.enabled", "false"); System.setProperty("queryResultCache.enabled", "false"); System.setProperty("filterCache.enabled", "false"); - System.setProperty("miniClusterBaseDir", "build/work/mini-cluster"); // create a lot of small segments System.setProperty("segmentsPerTier", "200"); System.setProperty("maxBufferedDocs", "100"); - solrBenchState.startSolr(1); + solrBenchState.startSolr(1, searchThreads); log("######### Creating index ..."); solrBenchState.createCollection(COLLECTION, 1, 1); // create a lot of large-ish fields to scan positions Docs docs = Docs.docs(1234567890L) .field("id", integers().incrementing()) - .field("f1_ts", strings().alpha().maxCardinality(WORDS).ofLengthBetween(3, 10)) + .field("f1_ts", strings().alpha().maxCardinality(words).ofLengthBetween(3, 10)) .field( - "f2_ts", strings().alpha().maxCardinality(WORDS).multi(50).ofLengthBetween(3, 10)) + "f2_ts", strings().alpha().maxCardinality(words).multi(50).ofLengthBetween(3, 10)) .field( - "f3_ts", strings().alpha().maxCardinality(WORDS).multi(50).ofLengthBetween(3, 10)) + "f3_ts", strings().alpha().maxCardinality(words).multi(50).ofLengthBetween(3, 10)) .field( - "f4_ts", strings().alpha().maxCardinality(WORDS).multi(50).ofLengthBetween(3, 10)) + "f4_ts", strings().alpha().maxCardinality(words).multi(50).ofLengthBetween(3, 10)) .field( - "f5_ts", strings().alpha().maxCardinality(WORDS).multi(50).ofLengthBetween(3, 10)) + "f5_ts", strings().alpha().maxCardinality(words).multi(50).ofLengthBetween(3, 10)) .field( - "f6_ts", strings().alpha().maxCardinality(WORDS).multi(50).ofLengthBetween(3, 10)) + "f6_ts", strings().alpha().maxCardinality(words).multi(50).ofLengthBetween(3, 10)) .field( - "f7_ts", strings().alpha().maxCardinality(WORDS).multi(50).ofLengthBetween(3, 10)) + "f7_ts", strings().alpha().maxCardinality(words).multi(50).ofLengthBetween(3, 10)) .field( - "f8_ts", strings().alpha().maxCardinality(WORDS).multi(50).ofLengthBetween(3, 10)) + "f8_ts", strings().alpha().maxCardinality(words).multi(50).ofLengthBetween(3, 10)) .field( "f9_ts", - strings().alpha().maxCardinality(WORDS).multi(50).ofLengthBetween(3, 10)); - solrBenchState.index(COLLECTION, docs, NUM_DOCS, true); + strings().alpha().maxCardinality(words).multi(50).ofLengthBetween(3, 10)); + solrBenchState.index(COLLECTION, docs, numDocs, false); solrBenchState.forceMerge(COLLECTION, 200); solrBenchState.dumpCoreInfo(); } @@ -123,16 +138,21 @@ public void setupQueries(SolrBenchState solrBenchState) throws Exception { CollectionAdminRequest.Reload reload = CollectionAdminRequest.reloadCollection(COLLECTION); solrBenchState.client.request(reload); + tasksBeforeIteration = solrBenchState.completedSearchTasks(); + queryFields = Docs.docs(1234567890L) .field("id", integers().incrementing()) - .field("f1_ts", strings().alpha().maxCardinality(WORDS).ofLengthBetween(3, 10)) + .field("f1_ts", strings().alpha().maxCardinality(words).ofLengthBetween(3, 10)) .field( - "f2_ts", strings().alpha().maxCardinality(WORDS).multi(5).ofLengthBetween(3, 10)); + "f2_ts", strings().alpha().maxCardinality(words).multi(5).ofLengthBetween(3, 10)); } @TearDown(Level.Iteration) - public void tearDownTrial() throws Exception { + public void tearDownTrial(SolrBenchState solrBenchState) throws Exception { + log( + "Completed search-pool tasks in iteration: " + + (solrBenchState.completedSearchTasks() - tasksBeforeIteration)); if (verifyEDRInUse) { CallerSpecificQueryLimit queryLimit = (CallerSpecificQueryLimit) TestInjection.queryTimeout; if (queryLimit == null) { @@ -151,9 +171,12 @@ public void tearDownTrial() throws Exception { } } - private static ModifiableSolrParams createInitialParams() { - ModifiableSolrParams params = - SolrBenchState.params("rows", "100", "timeAllowed", "1000", "fl", "*"); + private static ModifiableSolrParams createInitialParams(BenchState state) { + ModifiableSolrParams params = SolrBenchState.params("rows", "100", "fl", "*"); + params.set("multiThreaded", state.multiThreaded); + if (state.timeAllowed > 0) { + params.set("timeAllowed", state.timeAllowed); + } return params; } @@ -161,7 +184,7 @@ private static ModifiableSolrParams createInitialParams() { public void testShortQuery(SolrBenchState solrBenchState, Blackhole bh, BenchState state) throws Exception { SolrInputDocument queryDoc = state.queryFields.inputDocument(); - ModifiableSolrParams params = createInitialParams(); + ModifiableSolrParams params = createInitialParams(state); params.set("q", "f1_ts:" + queryDoc.getFieldValue("f1_ts").toString()); QueryRequest queryRequest = new QueryRequest(params); QueryResponse rsp = queryRequest.process(solrBenchState.client, COLLECTION); @@ -172,7 +195,7 @@ public void testShortQuery(SolrBenchState solrBenchState, Blackhole bh, BenchSta public void testLongQuery(SolrBenchState solrBenchState, Blackhole bh, BenchState state) throws Exception { SolrInputDocument queryDoc = state.queryFields.inputDocument(); - ModifiableSolrParams params = createInitialParams(); + ModifiableSolrParams params = createInitialParams(state); StringBuilder query = new StringBuilder(); for (int i = 2; i < 10; i++) { if (query.length() > 0) {