You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ro...@apache.org on 2015/03/09 21:51:28 UTC
svn commit: r1665353 - in /lucene/dev/trunk/solr: CHANGES.txt
core/src/test/org/apache/solr/cloud/TestMiniSolrCloudCluster.java
test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
Author: romseygeek
Date: Mon Mar 9 20:51:28 2015
New Revision: 1665353
URL: http://svn.apache.org/r1665353
Log:
SOLR-7180: MiniSolrCloudCluster starts up and shuts down its jetties in parallel
Modified:
lucene/dev/trunk/solr/CHANGES.txt
lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudCluster.java
lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1665353&r1=1665352&r2=1665353&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Mon Mar 9 20:51:28 2015
@@ -285,6 +285,9 @@ Other Changes
* SOLR-6804: Untangle SnapPuller and ReplicationHandler (Ramkumar Aiyengar)
+* SOLR-7180: MiniSolrCloudCluster will startup and shutdown its jetties in
+ parallel (Alan Woodward, Tomás Fernández Löbbe, Vamsee Yarlagadda)
+
================== 5.0.0 ==================
Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this release.
Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudCluster.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudCluster.java?rev=1665353&r1=1665352&r2=1665353&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudCluster.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudCluster.java Mon Mar 9 20:51:28 2015
@@ -22,9 +22,9 @@ import org.apache.lucene.util.LuceneTest
import org.apache.lucene.util.LuceneTestCase.SuppressSysoutChecks;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.SolrQuery;
+import org.apache.solr.client.solrj.embedded.JettyConfig;
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
-import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.cloud.ClusterState;
@@ -34,8 +34,6 @@ import org.apache.solr.common.cloud.Solr
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.util.RevertDefaultThreadHandlerRule;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
@@ -45,10 +43,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
+import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Test of the MiniSolrCloudCluster functionality. Keep in mind,
@@ -62,7 +62,6 @@ public class TestMiniSolrCloudCluster ex
private static final int NUM_SERVERS = 5;
private static final int NUM_SHARDS = 2;
private static final int REPLICATION_FACTOR = 2;
- private static MiniSolrCloudCluster miniCluster;
@Rule
public TestRule solrTestRules = RuleChain
@@ -73,99 +72,151 @@ public class TestMiniSolrCloudCluster ex
new SystemPropertiesRestoreRule()).around(
new RevertDefaultThreadHandlerRule());
- @BeforeClass
- public static void startup() throws Exception {
+ @Test
+ public void testBasics() throws Exception {
+
File solrXml = new File(SolrTestCaseJ4.TEST_HOME(), "solr-no-core.xml");
- miniCluster = new MiniSolrCloudCluster(NUM_SERVERS, null, createTempDir().toFile(), solrXml, null, null);
- }
+ MiniSolrCloudCluster miniCluster = new MiniSolrCloudCluster(NUM_SERVERS, null, createTempDir().toFile(), solrXml, null, null);
- @AfterClass
- public static void shutdown() throws Exception {
- if (miniCluster != null) {
+ try {
+ assertNotNull(miniCluster.getZkServer());
+ List<JettySolrRunner> jettys = miniCluster.getJettySolrRunners();
+ assertEquals(NUM_SERVERS, jettys.size());
+ for (JettySolrRunner jetty : jettys) {
+ assertTrue(jetty.isRunning());
+ }
+
+ // shut down a server
+ JettySolrRunner stoppedServer = miniCluster.stopJettySolrRunner(0);
+ assertTrue(stoppedServer.isStopped());
+ assertEquals(NUM_SERVERS - 1, miniCluster.getJettySolrRunners().size());
+
+ // create a server
+ JettySolrRunner startedServer = miniCluster.startJettySolrRunner(null, null, null);
+ assertTrue(startedServer.isRunning());
+ assertEquals(NUM_SERVERS, miniCluster.getJettySolrRunners().size());
+
+ // create collection
+ String collectionName = "testSolrCloudCollection";
+ String configName = "solrCloudCollectionConfig";
+ File configDir = new File(SolrTestCaseJ4.TEST_HOME() + File.separator + "collection1" + File.separator + "conf");
+ miniCluster.uploadConfigDir(configDir, configName);
+
+ Map<String, String> collectionProperties = new HashMap<>();
+ collectionProperties.put(CoreDescriptor.CORE_CONFIG, "solrconfig-tlog.xml");
+ collectionProperties.put("solr.tests.maxBufferedDocs", "100000");
+ collectionProperties.put("solr.tests.maxIndexingThreads", "-1");
+ collectionProperties.put("solr.tests.ramBufferSizeMB", "100");
+ // use non-test classes so RandomizedRunner isn't necessary
+ collectionProperties.put("solr.tests.mergePolicy", "org.apache.lucene.index.TieredMergePolicy");
+ collectionProperties.put("solr.tests.mergeScheduler", "org.apache.lucene.index.ConcurrentMergeScheduler");
+ collectionProperties.put("solr.directoryFactory", "solr.RAMDirectoryFactory");
+ miniCluster.createCollection(collectionName, NUM_SHARDS, REPLICATION_FACTOR, configName, collectionProperties);
+
+ try (SolrZkClient zkClient = new SolrZkClient
+ (miniCluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+ ZkStateReader zkStateReader = new ZkStateReader(zkClient);
+ AbstractDistribZkTestBase.waitForRecoveriesToFinish(collectionName, zkStateReader, true, true, 330);
+
+ // modify/query collection
+ CloudSolrClient cloudSolrClient = miniCluster.getSolrClient();
+ cloudSolrClient.setDefaultCollection(collectionName);
+ SolrInputDocument doc = new SolrInputDocument();
+ doc.setField("id", "1");
+ cloudSolrClient.add(doc);
+ cloudSolrClient.commit();
+ SolrQuery query = new SolrQuery();
+ query.setQuery("*:*");
+ QueryResponse rsp = cloudSolrClient.query(query);
+ assertEquals(1, rsp.getResults().getNumFound());
+
+ // remove a server not hosting any replicas
+ zkStateReader.updateClusterState(true);
+ ClusterState clusterState = zkStateReader.getClusterState();
+ HashMap<String, JettySolrRunner> jettyMap = new HashMap<String, JettySolrRunner>();
+ for (JettySolrRunner jetty : miniCluster.getJettySolrRunners()) {
+ String key = jetty.getBaseUrl().toString().substring((jetty.getBaseUrl().getProtocol() + "://").length());
+ jettyMap.put(key, jetty);
+ }
+ Collection<Slice> slices = clusterState.getSlices(collectionName);
+ // track the servers not host repliacs
+ for (Slice slice : slices) {
+ jettyMap.remove(slice.getLeader().getNodeName().replace("_solr", "/solr"));
+ for (Replica replica : slice.getReplicas()) {
+ jettyMap.remove(replica.getNodeName().replace("_solr", "/solr"));
+ }
+ }
+ assertTrue("Expected to find a node without a replica", jettyMap.size() > 0);
+ JettySolrRunner jettyToStop = jettyMap.entrySet().iterator().next().getValue();
+ jettys = miniCluster.getJettySolrRunners();
+ for (int i = 0; i < jettys.size(); ++i) {
+ if (jettys.get(i).equals(jettyToStop)) {
+ miniCluster.stopJettySolrRunner(i);
+ assertEquals(NUM_SERVERS - 1, miniCluster.getJettySolrRunners().size());
+ }
+ }
+ }
+ }
+ finally {
miniCluster.shutdown();
}
- miniCluster = null;
}
@Test
- public void testBasics() throws Exception {
- assertNotNull(miniCluster.getZkServer());
- List<JettySolrRunner> jettys = miniCluster.getJettySolrRunners();
- assertEquals(NUM_SERVERS, jettys.size());
- for (JettySolrRunner jetty : jettys) {
- assertTrue(jetty.isRunning());
- }
-
- // shut down a server
- JettySolrRunner stoppedServer = miniCluster.stopJettySolrRunner(0);
- assertTrue(stoppedServer.isStopped());
- assertEquals(NUM_SERVERS - 1, miniCluster.getJettySolrRunners().size());
-
- // create a server
- JettySolrRunner startedServer = miniCluster.startJettySolrRunner(null, null, null);
- assertTrue(startedServer.isRunning());
- assertEquals(NUM_SERVERS, miniCluster.getJettySolrRunners().size());
-
- // create collection
- String collectionName = "testSolrCloudCollection";
- String configName = "solrCloudCollectionConfig";
- File configDir = new File(SolrTestCaseJ4.TEST_HOME() + File.separator + "collection1" + File.separator + "conf");
- miniCluster.uploadConfigDir(configDir, configName);
-
- Map<String, String> collectionProperties = new HashMap<>();
- collectionProperties.put(CoreDescriptor.CORE_CONFIG, "solrconfig-tlog.xml");
- collectionProperties.put("solr.tests.maxBufferedDocs", "100000");
- collectionProperties.put("solr.tests.maxIndexingThreads", "-1");
- collectionProperties.put("solr.tests.ramBufferSizeMB", "100");
- // use non-test classes so RandomizedRunner isn't necessary
- collectionProperties.put("solr.tests.mergePolicy", "org.apache.lucene.index.TieredMergePolicy");
- collectionProperties.put("solr.tests.mergeScheduler", "org.apache.lucene.index.ConcurrentMergeScheduler");
- collectionProperties.put("solr.directoryFactory", "solr.RAMDirectoryFactory");
- miniCluster.createCollection(collectionName, NUM_SHARDS, REPLICATION_FACTOR, configName, collectionProperties);
-
- try(SolrZkClient zkClient = new SolrZkClient
- (miniCluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
- ZkStateReader zkStateReader = new ZkStateReader(zkClient);
- AbstractDistribZkTestBase.waitForRecoveriesToFinish(collectionName, zkStateReader, true, true, 330);
-
- // modify/query collection
- CloudSolrClient cloudSolrClient = miniCluster.getSolrClient();
- cloudSolrClient.setDefaultCollection(collectionName);
- SolrInputDocument doc = new SolrInputDocument();
- doc.setField("id", "1");
- cloudSolrClient.add(doc);
- cloudSolrClient.commit();
- SolrQuery query = new SolrQuery();
- query.setQuery("*:*");
- QueryResponse rsp = cloudSolrClient.query(query);
- assertEquals(1, rsp.getResults().getNumFound());
-
- // remove a server not hosting any replicas
- zkStateReader.updateClusterState(true);
- ClusterState clusterState = zkStateReader.getClusterState();
- HashMap<String, JettySolrRunner> jettyMap = new HashMap<String, JettySolrRunner>();
- for (JettySolrRunner jetty : miniCluster.getJettySolrRunners()) {
- String key = jetty.getBaseUrl().toString().substring((jetty.getBaseUrl().getProtocol() + "://").length());
- jettyMap.put(key, jetty);
- }
- Collection<Slice> slices = clusterState.getSlices(collectionName);
- // track the servers not host repliacs
- for (Slice slice : slices) {
- jettyMap.remove(slice.getLeader().getNodeName().replace("_solr", "/solr"));
- for (Replica replica : slice.getReplicas()) {
- jettyMap.remove(replica.getNodeName().replace("_solr", "/solr"));
+ public void testErrorsInStartup() throws Exception {
+
+ File solrXml = new File(SolrTestCaseJ4.TEST_HOME(), "solr-no-core.xml");
+ AtomicInteger jettyIndex = new AtomicInteger();
+
+ MiniSolrCloudCluster cluster = null;
+ try {
+ cluster = new MiniSolrCloudCluster(3, createTempDir().toFile(), solrXml, JettyConfig.builder().build()) {
+ @Override
+ public JettySolrRunner startJettySolrRunner(JettyConfig config) throws Exception {
+ if (jettyIndex.incrementAndGet() != 2)
+ return super.startJettySolrRunner(config);
+ throw new IOException("Fake exception on startup!");
}
- }
- assertTrue("Expected to find a node without a replica", jettyMap.size() > 0);
- JettySolrRunner jettyToStop = jettyMap.entrySet().iterator().next().getValue();
- jettys = miniCluster.getJettySolrRunners();
- for (int i = 0; i < jettys.size(); ++i) {
- if (jettys.get(i).equals(jettyToStop)) {
- miniCluster.stopJettySolrRunner(i);
- assertEquals(NUM_SERVERS - 1, miniCluster.getJettySolrRunners().size());
+ };
+ fail("Expected an exception to be thrown from MiniSolrCloudCluster");
+ }
+ catch (Exception e) {
+ assertEquals("Error starting up MiniSolrCloudCluster", e.getMessage());
+ assertEquals("Expected one suppressed exception", 1, e.getSuppressed().length);
+ assertEquals("Fake exception on startup!", e.getSuppressed()[0].getMessage());
+ }
+ finally {
+ if (cluster != null)
+ cluster.shutdown();
+ }
+ }
+
+ @Test
+ public void testErrorsInShutdown() throws Exception {
+
+ File solrXml = new File(SolrTestCaseJ4.TEST_HOME(), "solr-no-core.xml");
+ AtomicInteger jettyIndex = new AtomicInteger();
+
+ MiniSolrCloudCluster cluster = new MiniSolrCloudCluster(3, createTempDir().toFile(), solrXml, JettyConfig.builder().build()) {
+ @Override
+ protected JettySolrRunner stopJettySolrRunner(JettySolrRunner jetty) throws Exception {
+ JettySolrRunner j = super.stopJettySolrRunner(jetty);
+ if (jettyIndex.incrementAndGet() == 2)
+ throw new IOException("Fake IOException on shutdown!");
+ return j;
}
- }
+ };
+
+ try {
+ cluster.shutdown();
+ fail("Expected an exception to be thrown on MiniSolrCloudCluster shutdown");
+ }
+ catch (Exception e) {
+ assertEquals("Error shutting down MiniSolrCloudCluster", e.getMessage());
+ assertEquals("Expected one suppressed exception", 1, e.getSuppressed().length);
+ assertEquals("Fake IOException on shutdown!", e.getSuppressed()[0].getMessage());
}
+
}
}
Modified: lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java?rev=1665353&r1=1665352&r2=1665353&view=diff
==============================================================================
--- lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java (original)
+++ lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java Mon Mar 9 20:51:28 2015
@@ -31,6 +31,7 @@ import org.apache.solr.common.params.Col
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.SolrjNamedThreadFactory;
import org.apache.zookeeper.KeeperException;
import org.eclipse.jetty.servlet.ServletHolder;
import org.slf4j.Logger;
@@ -39,30 +40,46 @@ import org.slf4j.LoggerFactory;
import javax.servlet.Filter;
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
-
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * "Mini" SolrCloud cluster to be used for testing
+ */
public class MiniSolrCloudCluster {
private static Logger log = LoggerFactory.getLogger(MiniSolrCloudCluster.class);
private final ZkTestServer zkServer;
- private final List<JettySolrRunner> jettys;
+ private final List<JettySolrRunner> jettys = new LinkedList<>();
private final File testDir;
private final CloudSolrClient solrClient;
private final JettyConfig jettyConfig;
+ private final ExecutorService executor = Executors.newCachedThreadPool(new SolrjNamedThreadFactory("jetty-launcher"));
+
/**
- * "Mini" SolrCloud cluster to be used for testing
+ * Create a MiniSolrCloudCluster
+ *
* @param numServers number of Solr servers to start
* @param hostContext context path of Solr servers used by Jetty
* @param baseDir base directory that the mini cluster should be run from
* @param solrXml solr.xml file to be uploaded to ZooKeeper
* @param extraServlets Extra servlets to be started by Jetty
* @param extraRequestFilters extra filters to be started by Jetty
+ *
+ * @throws Exception if there was an error starting the cluster
*/
public MiniSolrCloudCluster(int numServers, String hostContext, File baseDir, File solrXml,
SortedMap<ServletHolder, String> extraServlets,
@@ -71,7 +88,8 @@ public class MiniSolrCloudCluster {
}
/**
- * "Mini" SolrCloud cluster to be used for testing
+ * Create a MiniSolrCloudCluster
+ *
* @param numServers number of Solr servers to start
* @param hostContext context path of Solr servers used by Jetty
* @param baseDir base directory that the mini cluster should be run from
@@ -79,6 +97,8 @@ public class MiniSolrCloudCluster {
* @param extraServlets Extra servlets to be started by Jetty
* @param extraRequestFilters extra filters to be started by Jetty
* @param sslConfig SSL configuration
+ *
+ * @throws Exception if there was an error starting the cluster
*/
public MiniSolrCloudCluster(int numServers, String hostContext, File baseDir, File solrXml,
SortedMap<ServletHolder, String> extraServlets,
@@ -92,6 +112,16 @@ public class MiniSolrCloudCluster {
.build());
}
+ /**
+ * Create a MiniSolrCloudCluster
+ *
+ * @param numServers number of Solr servers to start
+ * @param baseDir base directory that the mini cluster should be run from
+ * @param solrXml solr.xml file to be uploaded to ZooKeeper
+ * @param jettyConfig Jetty configuration
+ *
+ * @throws Exception if there was an error starting the cluster
+ */
public MiniSolrCloudCluster(int numServers, File baseDir, File solrXml, JettyConfig jettyConfig) throws Exception {
this.testDir = baseDir;
@@ -114,11 +144,28 @@ public class MiniSolrCloudCluster {
System.setProperty("solr.solrxml.location","zookeeper");
System.setProperty("zkHost", zkServer.getZkAddress());
- jettys = new LinkedList<>();
+ List<Callable<JettySolrRunner>> startups = new ArrayList<>(numServers);
for (int i = 0; i < numServers; ++i) {
- startJettySolrRunner(jettyConfig);
+ startups.add(new Callable<JettySolrRunner>() {
+ @Override
+ public JettySolrRunner call() throws Exception {
+ return startJettySolrRunner(jettyConfig);
+ }
+ });
}
-
+
+ Collection<Future<JettySolrRunner>> futures = executor.invokeAll(startups);
+ Exception startupError = checkForExceptions("Error starting up MiniSolrCloudCluster", futures);
+ if (startupError != null) {
+ try {
+ this.shutdown();
+ }
+ catch (Throwable t) {
+ startupError.addSuppressed(t);
+ }
+ throw startupError;
+ }
+
solrClient = buildSolrClient();
}
@@ -220,6 +267,11 @@ public class MiniSolrCloudCluster {
jettys.remove(index);
return jetty;
}
+
+ protected JettySolrRunner stopJettySolrRunner(JettySolrRunner jetty) throws Exception {
+ jetty.stop();
+ return jetty;
+ }
public void uploadConfigDir(File configDir, String configName) throws IOException, KeeperException, InterruptedException {
try(SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(),
@@ -254,11 +306,26 @@ public class MiniSolrCloudCluster {
*/
public void shutdown() throws Exception {
try {
- solrClient.close();
- for (int i = jettys.size() - 1; i >= 0; --i) {
- stopJettySolrRunner(i);
+ if (solrClient != null)
+ solrClient.close();
+ List<Callable<JettySolrRunner>> shutdowns = new ArrayList<>(jettys.size());
+ for (final JettySolrRunner jetty : jettys) {
+ shutdowns.add(new Callable<JettySolrRunner>() {
+ @Override
+ public JettySolrRunner call() throws Exception {
+ return stopJettySolrRunner(jetty);
+ }
+ });
+ }
+ jettys.clear();
+ Collection<Future<JettySolrRunner>> futures = executor.invokeAll(shutdowns);
+ Exception shutdownError = checkForExceptions("Error shutting down MiniSolrCloudCluster", futures);
+ if (shutdownError != null) {
+ throw shutdownError;
}
} finally {
+ executor.shutdown();
+ executor.awaitTermination(2, TimeUnit.SECONDS);
try {
zkServer.shutdown();
} finally {
@@ -282,4 +349,23 @@ public class MiniSolrCloudCluster {
if (!ctx.startsWith("/")) ctx = "/" + ctx;
return ctx;
}
+
+ private Exception checkForExceptions(String message, Collection<Future<JettySolrRunner>> futures) throws InterruptedException {
+ Exception parsed = new Exception(message);
+ boolean ok = true;
+ for (Future<JettySolrRunner> future : futures) {
+ try {
+ future.get();
+ }
+ catch (ExecutionException e) {
+ parsed.addSuppressed(e.getCause());
+ ok = false;
+ }
+ catch (InterruptedException e) {
+ Thread.interrupted();
+ throw e;
+ }
+ }
+ return ok ? null : parsed;
+ }
}