diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientIntegrationTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientIntegrationTest.java index 3f24984f90e..84a0283ee79 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientIntegrationTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientIntegrationTest.java @@ -17,7 +17,6 @@ package org.apache.solr.client.solrj.impl; import java.io.IOException; -import java.io.UncheckedIOException; import java.lang.invoke.MethodHandles; import java.nio.file.Files; import java.nio.file.Path; @@ -35,6 +34,8 @@ import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.SolrResponseBase; import org.apache.solr.common.SolrInputDocument; +import org.apache.solr.common.util.ExecutorUtil; +import org.apache.solr.common.util.SolrNamedThreadFactory; import org.apache.solr.embedded.JettyConfig; import org.apache.solr.embedded.JettySolrRunner; import org.apache.solr.util.LogLevel; @@ -110,7 +111,12 @@ private void addDocs(SolrInstance solrInstance) throws IOException, SolrServerEx public void tearDown() throws Exception { for (SolrInstance aSolr : solr) { if (aSolr != null) { - aSolr.tearDown(); + try { + aSolr.tearDown(); + } catch (Exception e) { + // Log but don't fail the test due to teardown issues + log.warn("Exception during tearDown of {}", aSolr.name, e); + } } } super.tearDown(); @@ -159,8 +165,17 @@ public void testSimple() throws Exception { assertEquals(3, names.size()); // Kill a server and test again - solr[1].jetty.stop(); - solr[1].jetty = null; + try { + solr[1].jetty.stop(); + } catch (Exception e) { + log.warn("Exception stopping jetty during test", e); + } finally { + solr[1].jetty = null; + } + + // Give some time for the LB client to detect the server is down + Thread.sleep(100); + names.clear(); for (int i = 0; i < solr.length; i++) { resp = h.lbClient.query(solrQuery); @@ -187,8 +202,17 @@ public void testTwoServers() throws Exception { try (var h = client(baseSolrEndpoints)) { SolrQuery solrQuery = new SolrQuery("*:*"); QueryResponse resp = null; - solr[0].jetty.stop(); - solr[0].jetty = null; + + try { + solr[0].jetty.stop(); + } catch (Exception e) { + log.warn("Exception stopping jetty[0] during test", e); + } finally { + solr[0].jetty = null; + } + + Thread.sleep(100); // Give LB client time to detect server down + resp = h.lbClient.query(solrQuery); String name = resp.getResults().get(0).getFieldValue("name").toString(); assertEquals("solr/collection11", name); @@ -196,8 +220,14 @@ public void testTwoServers() throws Exception { name = resp.getResults().get(0).getFieldValue("name").toString(); assertEquals("solr/collection11", name); - solr[1].jetty.stop(); - solr[1].jetty = null; + try { + solr[1].jetty.stop(); + } catch (Exception e) { + log.warn("Exception stopping jetty[1] during test", e); + } finally { + solr[1].jetty = null; + } + startJettyAndWaitForAliveCheckQuery(solr[0]); resp = h.lbClient.query(solrQuery); @@ -224,6 +254,33 @@ private void startJettyAndWaitForAliveCheckQuery(SolrInstance solrInstance) thro } } + private static void stopJettyWithTimeout(JettySolrRunner jetty, long timeoutMs) throws Exception { + final var executor = + ExecutorUtil.newMDCAwareSingleThreadExecutor(new SolrNamedThreadFactory("jetty-shutdown")); + + try { + final var future = + java.util.concurrent.CompletableFuture.runAsync( + () -> { + try { + jetty.stop(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }, + executor); + + try { + future.get(timeoutMs, TimeUnit.MILLISECONDS); + } catch (java.util.concurrent.TimeoutException e) { + future.cancel(true); + throw new Exception("Jetty shutdown timed out after " + timeoutMs + "ms", e); + } + } finally { + executor.shutdown(); + } + } + private static class SolrInstance { String name; Path homeDir; @@ -292,7 +349,16 @@ public void setUp() throws Exception { } public void tearDown() throws Exception { - if (jetty != null) jetty.stop(); + if (jetty != null) { + try { + // Use a timeout wrapper for shutdown + stopJettyWithTimeout(jetty, 5000); + } catch (Exception e) { + log.warn("Exception stopping jetty for {}", name, e); + } finally { + jetty = null; + } + } IOUtils.rm(homeDir); } @@ -326,11 +392,16 @@ private static class LBClientHolder implements AutoCloseable { @Override public void close() { - lbClient.close(); + try { + lbClient.close(); + } catch (Exception e) { + log.warn("Exception closing LB client", e); + } try { delegate.close(); } catch (IOException ioe) { - throw new UncheckedIOException(ioe); + log.warn("Exception closing delegate client", ioe); + // Don't throw exception during cleanup to avoid masking test failures } } }