This is an automated email from the ASF dual-hosted git repository. dsmiley pushed a commit to branch branch_10x in repository https://gitbox.apache.org/repos/asf/solr.git
commit cf6cb4345c6a1d88f3c99d57178790b8bb60df49 Author: David Smiley <[email protected]> AuthorDate: Tue Apr 14 11:31:46 2026 -0400 SOLR-18168: Simplify JettySolrRunner (#4262) * Remove GzipHandler from JettySolrRunner. Migrate the test to BATS. * Refactor JettySolrRunner filter access to use generic getFilter(). Broaden getExtraRequestFilters() return type from SortedMap to SequencedMap. * Remove Servlet404. * Move/Rename DebugFilter to ServletFixtures as DelayFilter --- .../org/apache/solr/cloud/ShardRoutingTest.java | 13 +- .../solr/servlet/TestRequestRateLimiter.java | 5 +- .../solr/update/TestInPlaceUpdatesDistrib.java | 26 ++- solr/packaging/test/test_compression.bats | 47 ++++++ .../apache/solr/BaseDistributedSearchTestCase.java | 3 +- .../org/apache/solr/embedded/JettySolrRunner.java | 186 ++++----------------- .../java/org/apache/solr/util/ServletFixtures.java | 73 ++++++++ .../solr/cloud/MiniSolrCloudClusterTest.java | 3 +- 8 files changed, 191 insertions(+), 165 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/cloud/ShardRoutingTest.java b/solr/core/src/test/org/apache/solr/cloud/ShardRoutingTest.java index cbb8df7d37f..1d705df29fe 100644 --- a/solr/core/src/test/org/apache/solr/cloud/ShardRoutingTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/ShardRoutingTest.java @@ -16,14 +16,18 @@ */ package org.apache.solr.cloud; +import jakarta.servlet.Filter; import java.lang.invoke.MethodHandles; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.SequencedMap; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.params.ShardParams; import org.apache.solr.embedded.JettySolrRunner; +import org.apache.solr.util.ServletFixtures; import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; @@ -327,10 +331,15 @@ public class ShardRoutingTest extends AbstractFullDistribZkTestBase { } } + @Override + public SequencedMap<Class<? extends Filter>, String> getExtraRequestFilters() { + return new LinkedHashMap<>(Map.of(ServletFixtures.DelayServlet.class, "/*")); + } + long getNumRequests() { - long n = controlJetty.getDebugFilter().getTotalRequests(); + long n = controlJetty.getFilter(ServletFixtures.DelayServlet.class).getTotalRequests(); for (JettySolrRunner jetty : jettys) { - n += jetty.getDebugFilter().getTotalRequests(); + n += jetty.getFilter(ServletFixtures.DelayServlet.class).getTotalRequests(); } return n; } diff --git a/solr/core/src/test/org/apache/solr/servlet/TestRequestRateLimiter.java b/solr/core/src/test/org/apache/solr/servlet/TestRequestRateLimiter.java index d551862227c..43f627d917a 100644 --- a/solr/core/src/test/org/apache/solr/servlet/TestRequestRateLimiter.java +++ b/solr/core/src/test/org/apache/solr/servlet/TestRequestRateLimiter.java @@ -72,7 +72,8 @@ public class TestRequestRateLimiter extends SolrCloudTestCase { CollectionAdminRequest.createCollection(FIRST_COLLECTION, 1, 1).process(client); cluster.waitForActiveCollection(FIRST_COLLECTION, 1, 1); - RateLimitFilter rateLimitFilter = cluster.getJettySolrRunner(0).getSolrRateLimitFilter(); + RateLimitFilter rateLimitFilter = + cluster.getJettySolrRunner(0).getFilter(RateLimitFilter.class); RateLimiterConfig rateLimiterConfig = new RateLimiterConfig( @@ -291,7 +292,7 @@ public class TestRequestRateLimiter extends SolrCloudTestCase { CollectionAdminRequest.createCollection(SECOND_COLLECTION, 1, 1).process(client); cluster.waitForActiveCollection(SECOND_COLLECTION, 1, 1); - RateLimitFilter rateLimitFilter = cluster.getJettySolrRunner(0).getSolrRateLimitFilter(); + var rateLimitFilter = cluster.getJettySolrRunner(0).getFilter(RateLimitFilter.class); RateLimiterConfig queryRateLimiterConfig = new RateLimiterConfig( diff --git a/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java b/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java index 8576af70208..011bad9e556 100644 --- a/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java +++ b/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java @@ -19,14 +19,17 @@ package org.apache.solr.update; import static org.hamcrest.core.StringContains.containsString; +import jakarta.servlet.Filter; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.SequencedMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -62,6 +65,7 @@ import org.apache.solr.embedded.JettySolrRunner; import org.apache.solr.index.NoMergePolicyFactory; import org.apache.solr.update.processor.DistributedUpdateProcessor; import org.apache.solr.util.RefCounted; +import org.apache.solr.util.ServletFixtures; import org.apache.solr.util.TimeOut; import org.apache.zookeeper.KeeperException; import org.junit.BeforeClass; @@ -202,9 +206,14 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase { // reorderedDBQsUsingUpdatedValueFromADroppedUpdate(); } + @Override + public SequencedMap<Class<? extends Filter>, String> getExtraRequestFilters() { + return new LinkedHashMap<>(Map.of(ServletFixtures.DelayServlet.class, "/*")); + } + private void resetDelays() { for (JettySolrRunner j : jettys) { - j.getDebugFilter().unsetDelay(); + j.getFilter(ServletFixtures.DelayServlet.class).unsetDelay(); } } @@ -1236,7 +1245,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase { .get(SHARD1) .get(1) .jetty - .getDebugFilter() + .getFilter(ServletFixtures.DelayServlet.class) .addDelay("Waiting for dependant update to timeout", 1, 6000); ExecutorService threadpool = @@ -1324,7 +1333,12 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase { { clearIndex(); commit(); - shardToJetty.get(SHARD1).get(1).jetty.getDebugFilter().unsetDelay(); + shardToJetty + .get(SHARD1) + .getFirst() + .jetty + .getFilter(ServletFixtures.DelayServlet.class) + .unsetDelay(); updates.add(regularDeleteRequest(1)); @@ -1332,13 +1346,13 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase { .get(SHARD1) .get(1) .jetty - .getDebugFilter() + .getFilter(ServletFixtures.DelayServlet.class) .addDelay("Waiting for dependant update to timeout", 1, 5999); // the first update shardToJetty .get(SHARD1) .get(1) .jetty - .getDebugFilter() + .getFilter(ServletFixtures.DelayServlet.class) .addDelay("Waiting for dependant update to timeout", 4, 5998); // the delete update threadpool = @@ -1635,7 +1649,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase { .get(SHARD1) .get(1) .jetty - .getDebugFilter() + .getFilter(ServletFixtures.DelayServlet.class) .addDelay("Waiting for dependant update to timeout", 2, 8000); ExecutorService threadpool = diff --git a/solr/packaging/test/test_compression.bats b/solr/packaging/test/test_compression.bats new file mode 100644 index 00000000000..575490c8f99 --- /dev/null +++ b/solr/packaging/test/test_compression.bats @@ -0,0 +1,47 @@ +#!/usr/bin/env bats + +# 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. + +load bats_helper + +setup_file() { + common_clean_setup + solr start -e films +} + +setup() { + common_setup +} + +teardown_file() { + save_home_on_failure + solr stop --all >/dev/null 2>&1 +} + +@test "server does not compress response without Accept-Encoding header" { + run curl -s -D - -o /dev/null "http://localhost:${SOLR_PORT}/solr/films/select?q=*:*&rows=100" + refute_output --partial "Content-Encoding:" +} + +@test "server compresses response when Accept-Encoding: gzip is requested" { + run curl -s -D - -o /dev/null -H "Accept-Encoding: gzip" "http://localhost:${SOLR_PORT}/solr/films/select?q=*:*&rows=100" + assert_output --partial "gzip" +} + +@test "compressed response can be decompressed and parsed" { + run curl -s --compressed "http://localhost:${SOLR_PORT}/solr/films/select?q=*:*&rows=100" + assert_output --partial '"status":0' +} diff --git a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java index 726c736fda7..e72ffaa213c 100644 --- a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java +++ b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java @@ -37,6 +37,7 @@ import java.util.Map; import java.util.Objects; import java.util.Properties; import java.util.Random; +import java.util.SequencedMap; import java.util.Set; import java.util.SortedMap; import java.util.concurrent.ConcurrentHashMap; @@ -439,7 +440,7 @@ public abstract class BaseDistributedSearchTestCase extends SolrTestCaseJ4 { * Override this method to insert extra filters into the JettySolrRunners that are created using * createJetty() */ - public SortedMap<Class<? extends Filter>, String> getExtraRequestFilters() { + public SequencedMap<Class<? extends Filter>, String> getExtraRequestFilters() { return null; } diff --git a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java index 9428b6878c4..f355b057aa4 100644 --- a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java +++ b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java @@ -19,16 +19,9 @@ package org.apache.solr.embedded; import io.prometheus.metrics.expositionformats.PrometheusTextFormatWriter; import jakarta.servlet.DispatcherType; import jakarta.servlet.Filter; -import jakarta.servlet.FilterChain; -import jakarta.servlet.FilterConfig; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; import jakarta.servlet.UnavailableException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintStream; import java.lang.invoke.MethodHandles; @@ -37,18 +30,17 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.util.ArrayList; +import java.util.Arrays; import java.util.EnumSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Properties; import java.util.Random; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; import org.apache.solr.SolrBackend; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrServerException; @@ -75,6 +67,7 @@ import org.apache.solr.util.TimeOut; import org.apache.solr.util.configuration.SSLConfigurationsFactory; import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; import org.eclipse.jetty.ee10.servlet.FilterHolder; +import org.eclipse.jetty.ee10.servlet.FilterMapping; import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.ee10.servlet.Source; @@ -92,7 +85,6 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.GracefulHandler; -import org.eclipse.jetty.server.handler.gzip.GzipHandler; import org.eclipse.jetty.session.DefaultSessionIdManager; import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -117,12 +109,7 @@ public class JettySolrRunner implements SolrBackend { private Server server; - volatile FilterHolder debugFilter; - volatile FilterHolder requiredFilter; - volatile FilterHolder rateLimitFilter; - volatile FilterHolder authFilter; - volatile ServletHolder solrServlet; - private FilterHolder tracingFilter; + private volatile ServletHolder solrServlet; private int jettyPort = -1; @@ -132,8 +119,6 @@ public class JettySolrRunner implements SolrBackend { private volatile boolean startedBefore = false; - private List<FilterHolder> extraFilters; - private int proxyPort = -1; private final boolean enableProxy; @@ -148,69 +133,6 @@ public class JettySolrRunner implements SolrBackend { private volatile boolean started = false; - public static class DebugFilter implements Filter { - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - private AtomicLong nRequests = new AtomicLong(); - - List<Delay> delays = new ArrayList<>(); - - public long getTotalRequests() { - return nRequests.get(); - } - - /** - * Introduce a delay of specified milliseconds for the specified request. - * - * @param reason Info message logged when delay occurs - * @param count The count-th request will experience a delay - * @param delay There will be a delay of this many milliseconds - */ - public void addDelay(String reason, int count, int delay) { - delays.add(new Delay(reason, count, delay)); - } - - /** Remove any delay introduced before. */ - public void unsetDelay() { - delays.clear(); - } - - @Override - public void init(FilterConfig filterConfig) throws ServletException {} - - @Override - public void doFilter( - ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) - throws IOException, ServletException { - nRequests.incrementAndGet(); - executeDelay(); - filterChain.doFilter(servletRequest, servletResponse); - } - - @Override - public void destroy() {} - - private void executeDelay() { - int delayMs = 0; - for (Delay delay : delays) { - log.info("Delaying {}, for reason: {}", delay.delayValue, delay.reason); - if (delay.counter.decrementAndGet() == 0) { - delayMs += delay.delayValue; - } - } - - if (delayMs > 0) { - log.info("Pausing this socket connection for {}ms...", delayMs); - try { - Thread.sleep(delayMs); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - log.info("Waking up after the delay of {}ms...", delayMs); - } - } - } - /** * Create a new JettySolrRunner. * @@ -372,7 +294,7 @@ public class JettySolrRunner implements SolrBackend { { // Initialize the servlets final ServletContextHandler root = - new ServletContextHandler("/solr", ServletContextHandler.SESSIONS); + new ServletContextHandler("/solr", ServletContextHandler.NO_SESSIONS); root.setServer(server); root.setBaseResource(ResourceFactory.of(server).newResource(".")); root.addEventListener( @@ -398,11 +320,8 @@ public class JettySolrRunner implements SolrBackend { } }); - debugFilter = root.addFilter(DebugFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); - extraFilters = new ArrayList<>(); for (Map.Entry<Class<? extends Filter>, String> entry : config.extraFilters.entrySet()) { - extraFilters.add( - root.addFilter(entry.getKey(), entry.getValue(), EnumSet.of(DispatcherType.REQUEST))); + root.addFilter(entry.getKey(), entry.getValue(), EnumSet.of(DispatcherType.REQUEST)); } for (Map.Entry<ServletHolder, String> entry : config.extraServlets.entrySet()) { @@ -411,43 +330,29 @@ public class JettySolrRunner implements SolrBackend { // TODO: This needs to be driven by a parsing of web.xml eventually // though we still want to avoid classpath scanning. - // required request setup - requiredFilter = root.getServletHandler().newFilterHolder(Source.EMBEDDED); - requiredFilter.setHeldClass(RequiredSolrRequestFilter.class); - - // Ratelimit Requests - rateLimitFilter = root.getServletHandler().newFilterHolder(Source.EMBEDDED); - rateLimitFilter.setHeldClass(RateLimitFilter.class); - - // Trace Requests - tracingFilter = root.getServletHandler().newFilterHolder(Source.EMBEDDED); - tracingFilter.setHeldClass(TracingFilter.class); - - // Authenticate Requests - authFilter = root.getServletHandler().newFilterHolder(Source.EMBEDDED); - authFilter.setHeldClass(AuthenticationFilter.class); - - // Map filters in same path as in web.xml - root.addFilter(requiredFilter, "/*", EnumSet.of(DispatcherType.REQUEST)); - root.addFilter(rateLimitFilter, "/*", EnumSet.of(DispatcherType.REQUEST)); - root.addFilter(tracingFilter, "/*", EnumSet.of(DispatcherType.REQUEST)); - root.addFilter(authFilter, "/*", EnumSet.of(DispatcherType.REQUEST)); - // This is our main workhorse - now a servlet instead of filter solrServlet = root.getServletHandler().newServletHolder(Source.EMBEDDED); + solrServlet.setName("SolrServlet"); solrServlet.setHeldClass(SolrServlet.class); root.addServlet(solrServlet, "/*"); - // Default servlet as a fall-through - ServletHolder defaultHolder = root.getServletHandler().newServletHolder(Source.EMBEDDED); - - // considered adding DefaultServlet.class here but perhaps that might grant our unit tests - // the power to serve static resources on the build machines? Not sure, so I'll just give a - // name to our existing hack. The tests passed without this, but it will ensure that if anyone - // ever hits the PathExcludeFilter in the unit test they get a 404 as before not a 500 - defaultHolder.setHeldClass(Servlet404.class); - defaultHolder.setName("default"); - root.addServlet(defaultHolder, "/"); + // Map filters to SolrServlet by name (same order as web.xml) + for (var filterClass : + List.<Class<? extends Filter>>of( + RequiredSolrRequestFilter.class, + RateLimitFilter.class, + TracingFilter.class, + AuthenticationFilter.class)) { + FilterHolder fh = root.getServletHandler().newFilterHolder(Source.EMBEDDED); + fh.setName(filterClass.getSimpleName()); + fh.setHeldClass(filterClass); + root.getServletHandler().addFilter(fh); + FilterMapping fm = new FilterMapping(); + fm.setFilterName(fh.getName()); + fm.setServletNames(new String[] {"SolrServlet"}); + fm.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST)); + root.getServletHandler().addFilterMapping(fm); + } // TODO: end area that should be driven by web.xml and webdefault.xml chain = root; @@ -463,13 +368,7 @@ public class JettySolrRunner implements SolrBackend { chain = rwh; } - GzipHandler gzipHandler = new GzipHandler(); - gzipHandler.setHandler(chain); - - gzipHandler.setMinGzipSize(23); // https://github.com/eclipse/jetty.project/issues/4191 - gzipHandler.setIncludedMethods("GET"); - - server.setHandler(gzipHandler); + server.setHandler(chain); // Mimic "graceful.mod" GracefulHandler graceful = new GracefulHandler(); @@ -486,10 +385,15 @@ public class JettySolrRunner implements SolrBackend { } /** - * @return the {@link RateLimitFilter} for this node + * @return the first filter implemented by the specified class, or throws an exception */ - public RateLimitFilter getSolrRateLimitFilter() { - return (RateLimitFilter) rateLimitFilter.getFilter(); + public <T extends Filter> T getFilter(Class<T> filterClass) { + return Arrays.stream(solrServlet.getServletHandler().getFilters()) + .filter(fh -> fh.getHeldClass() == filterClass) + .map(fh -> filterClass.cast(fh.getFilter())) + .findFirst() + .orElseThrow( + () -> new NoSuchElementException("No filter of class: " + filterClass.getName())); } @Override @@ -857,21 +761,9 @@ public class JettySolrRunner implements SolrBackend { .build(); } - public DebugFilter getDebugFilter() { - return (DebugFilter) debugFilter.getFilter(); - } - // -------------------------------------------------------------- // -------------------------------------------------------------- - /** This is a stupid hack to give jetty something to attach to */ - public static class Servlet404 extends HttpServlet { - @Override - public void service(HttpServletRequest req, HttpServletResponse res) throws IOException { - res.sendError(404, "Can not find: " + req.getRequestURI()); - } - } - /** A main class that starts jetty+solr This is useful for debugging */ public static void main(String[] args) throws Exception { JettySolrRunner jetty = new JettySolrRunner(".", 8983); @@ -900,18 +792,6 @@ public class JettySolrRunner implements SolrBackend { cores.waitForLoadingCoresToFinish(timeoutMs); } - static class Delay { - final AtomicInteger counter; - final int delayValue; - final String reason; - - public Delay(String reason, int counter, int delay) { - this.reason = reason; - this.counter = new AtomicInteger(counter); - this.delayValue = delay; - } - } - public SocketProxy getProxy() { return proxy; } diff --git a/solr/test-framework/src/java/org/apache/solr/util/ServletFixtures.java b/solr/test-framework/src/java/org/apache/solr/util/ServletFixtures.java index b3551fa8a90..cb1b66f6eee 100644 --- a/solr/test-framework/src/java/org/apache/solr/util/ServletFixtures.java +++ b/solr/test-framework/src/java/org/apache/solr/util/ServletFixtures.java @@ -17,12 +17,17 @@ package org.apache.solr.util; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; +import java.lang.invoke.MethodHandles; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Enumeration; @@ -31,13 +36,81 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.IntStream; import org.apache.solr.common.util.SuppressForbidden; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ServletFixtures { private ServletFixtures() {} + /** A Servlet {@link Filter} that adds delays. */ + public static class DelayServlet implements Filter { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private AtomicLong nRequests = new AtomicLong(); + + List<Delay> delays = new ArrayList<>(); + + public long getTotalRequests() { + return nRequests.get(); + } + + /** + * Introduce a delay of specified milliseconds for the specified request. + * + * @param reason Info message logged when delay occurs + * @param count The count-th request will experience a delay + * @param delayMs There will be a delay of this many milliseconds + */ + public void addDelay(String reason, int count, int delayMs) { + delays.add(new Delay(reason, count, delayMs)); + } + + /** Remove any delay introduced before. */ + public void unsetDelay() { + delays.clear(); + } + + @Override + public void doFilter( + ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + nRequests.incrementAndGet(); + executeDelay(); + filterChain.doFilter(servletRequest, servletResponse); + } + + private void executeDelay() { + int delayMs = 0; + for (Delay delay : delays) { + log.info("Delaying {}, for reason: {}", delay.delayMsValue, delay.reason); + if (delay.counter.decrementAndGet() == 0) { + delayMs += delay.delayMsValue; + } + } + + if (delayMs > 0) { + log.info("Pausing this socket connection for {}ms...", delayMs); + try { + Thread.sleep(delayMs); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + log.info("Waking up after the delay of {}ms...", delayMs); + } + } + + record Delay(String reason, AtomicInteger counter, int delayMsValue) { + Delay(String reason, int counter, int delayMsValue) { + this(reason, new AtomicInteger(counter), delayMsValue); + } + } + } + public static class RedirectServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) diff --git a/solr/test-framework/src/test/org/apache/solr/cloud/MiniSolrCloudClusterTest.java b/solr/test-framework/src/test/org/apache/solr/cloud/MiniSolrCloudClusterTest.java index 605e56bf5a9..c5f5bb51043 100644 --- a/solr/test-framework/src/test/org/apache/solr/cloud/MiniSolrCloudClusterTest.java +++ b/solr/test-framework/src/test/org/apache/solr/cloud/MiniSolrCloudClusterTest.java @@ -32,6 +32,7 @@ import org.apache.solr.core.SolrCore; import org.apache.solr.embedded.JettyConfig; import org.apache.solr.embedded.JettySolrRunner; import org.apache.solr.util.RevertDefaultThreadHandlerRule; +import org.apache.solr.util.ServletFixtures; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.RuleChain; @@ -103,7 +104,7 @@ public class MiniSolrCloudClusterTest extends SolrTestCaseJ4 { public void testExtraFilters() throws Exception { JettyConfig.Builder jettyConfig = JettyConfig.builder(); jettyConfig.waitForLoadingCoresToFinish(null); - jettyConfig.withFilter(JettySolrRunner.DebugFilter.class, "*"); + jettyConfig.withFilter(ServletFixtures.DelayServlet.class, "*"); MiniSolrCloudCluster cluster = new MiniSolrCloudCluster(random().nextInt(3) + 1, createTempDir(), jettyConfig.build()); cluster.shutdown();
