diff --git a/solr/core/src/java/org/apache/solr/cloud/LeaderElector.java b/solr/core/src/java/org/apache/solr/cloud/LeaderElector.java index a2fd4c70e96..9335097bfd7 100644 --- a/solr/core/src/java/org/apache/solr/cloud/LeaderElector.java +++ b/solr/core/src/java/org/apache/solr/cloud/LeaderElector.java @@ -63,12 +63,20 @@ public class LeaderElector { protected SolrZkClient zkClient; private ZkCmdExecutor zkCmdExecutor; - + + // for tests + private volatile ElectionContext context; + public LeaderElector(SolrZkClient zkClient) { this.zkClient = zkClient; zkCmdExecutor = new ZkCmdExecutor((int) (zkClient.getZkClientTimeout()/1000.0 + 3000)); } + // for tests + public ElectionContext getContext() { + return context; + } + /** * Check if the candidate with the given n_* sequence number is the leader. * If it is, set the leaderId on the leader zk node. If it is not, start @@ -273,6 +281,7 @@ public class LeaderElector { */ public void setup(final ElectionContext context) throws InterruptedException, KeeperException { + this.context = context; String electZKPath = context.electionPath + LeaderElector.ELECTION_NODE; zkCmdExecutor.ensureExists(electZKPath, zkClient); diff --git a/solr/core/src/java/org/apache/solr/cloud/Overseer.java b/solr/core/src/java/org/apache/solr/cloud/Overseer.java index 7da3abb75b8..df2630193b4 100644 --- a/solr/core/src/java/org/apache/solr/cloud/Overseer.java +++ b/solr/core/src/java/org/apache/solr/cloud/Overseer.java @@ -935,9 +935,9 @@ public class Overseer { } - private OverseerThread ccThread; + private volatile OverseerThread ccThread; - private OverseerThread updaterThread; + private volatile OverseerThread updaterThread; private volatile boolean isClosed; @@ -970,6 +970,10 @@ public class Overseer { ccThread.start(); } + public OverseerThread getUpdaterThread() { + return updaterThread; + } + public void close() { isClosed = true; if (updaterThread != null) { diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java index d32f37db637..2b016a39edf 100644 --- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java +++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java @@ -1567,6 +1567,14 @@ public final class ZkController { return updateShardHandler; } + public Overseer getOverseer() { + return overseer; + } + + public LeaderElector getOverseerElector() { + return overseerElector; + } + /** * Returns the nodeName that should be used based on the specified properties. * diff --git a/solr/core/src/test/org/apache/solr/cloud/SliceStateUpdateTest.java b/solr/core/src/test/org/apache/solr/cloud/SliceStateUpdateTest.java index d896c75887e..c72273efafe 100644 --- a/solr/core/src/test/org/apache/solr/cloud/SliceStateUpdateTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/SliceStateUpdateTest.java @@ -19,6 +19,7 @@ package org.apache.solr.cloud; import org.apache.lucene.util.LuceneTestCase.Slow; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.cloud.Overseer.OverseerThread; import org.apache.solr.common.cloud.ClusterState; import org.apache.solr.common.cloud.DocCollection; import org.apache.solr.common.cloud.DocRouter; @@ -59,8 +60,6 @@ public class SliceStateUpdateTest extends SolrTestCaseJ4 { private File dataDir2; - private File dataDir3; - @BeforeClass public static void beforeClass() { System.setProperty("solrcloud.skip.autorecovery", "true"); @@ -105,9 +104,6 @@ public class SliceStateUpdateTest extends SolrTestCaseJ4 { dataDir2 = new File(dataDir + File.separator + "data2"); dataDir2.mkdirs(); - dataDir3 = new File(dataDir + File.separator + "data3"); - dataDir3.mkdirs(); - // set some system properties for use by tests System.setProperty("solr.test.sys.prop1", "propone"); System.setProperty("solr.test.sys.prop2", "proptwo"); @@ -116,21 +112,19 @@ public class SliceStateUpdateTest extends SolrTestCaseJ4 { System.setProperty("hostPort", "1661"); System.setProperty("solr.data.dir", SliceStateUpdateTest.this.dataDir1.getAbsolutePath()); container1 = new CoreContainer(); - + container1.load(); System.clearProperty("hostPort"); System.setProperty("hostPort", "1662"); System.setProperty("solr.data.dir", SliceStateUpdateTest.this.dataDir2.getAbsolutePath()); container2 = new CoreContainer(); + container2.load(); System.clearProperty("hostPort"); System.clearProperty("solr.solr.home"); - container1.load(); - container2.load(); log.info("####SETUP_END " + getTestName()); - } @@ -139,7 +133,14 @@ public class SliceStateUpdateTest extends SolrTestCaseJ4 { System.setProperty("solrcloud.update.delay", "1"); /* Get ClusterState, update slice state and publish it to Zookeeper */ - + container1.getZkController().getZkStateReader().updateClusterState(true); + + // we don't want to race with legit overseer updates + OverseerThread updaterThread = container1.getZkController().getOverseer().getUpdaterThread(); + closeThread(updaterThread); + updaterThread = container2.getZkController().getOverseer().getUpdaterThread(); + closeThread(updaterThread); + ClusterState clusterState = container1.getZkController().getClusterState(); Map collectionStates = new LinkedHashMap(clusterState.getCollectionStates()); @@ -167,7 +168,7 @@ public class SliceStateUpdateTest extends SolrTestCaseJ4 { ZkController zkController2 = container2.getZkController(); ClusterState clusterState2 = null; Map slices = null; - for (int i = 100; i > 0; i--) { + for (int i = 60; i > 0; i--) { clusterState2 = zkController2.getClusterState(); slices = clusterState2.getSlicesMap("collection1"); if (slices != null && slices.containsKey("shard1") @@ -181,6 +182,20 @@ public class SliceStateUpdateTest extends SolrTestCaseJ4 { assertEquals("shard1", slices.get("shard1").getName()); assertEquals("inactive", slices.get("shard1").getState()); + + container1.getZkController().getOverseerElector().getContext().cancelElection(); + container2.getZkController().getOverseerElector().getContext().cancelElection(); + } + + private void closeThread(OverseerThread updaterThread) { + if (updaterThread != null) { + try { + updaterThread.close(); + updaterThread.interrupt(); + } catch (Throwable t) { + log.error("Error closing updaterThread", t); + } + } } @Override