| /* |
| * 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. |
| */ |
| package org.apache.solr.handler; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.FilenameFilter; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.OutputStreamWriter; |
| import java.io.Writer; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.Set; |
| import java.util.Date; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.apache.commons.io.IOUtils; |
| import org.apache.lucene.index.DirectoryReader; |
| import org.apache.lucene.index.IndexReader; |
| import org.apache.lucene.search.IndexSearcher; |
| import org.apache.lucene.search.MatchAllDocsQuery; |
| import org.apache.lucene.search.TopDocs; |
| import org.apache.lucene.store.Directory; |
| import org.apache.lucene.store.SimpleFSDirectory; |
| import org.apache.lucene.util.LuceneTestCase.Slow; |
| import org.apache.solr.BaseDistributedSearchTestCase; |
| import org.apache.solr.SolrTestCaseJ4; |
| import org.apache.solr.client.solrj.SolrServer; |
| import org.apache.solr.client.solrj.SolrServerException; |
| import org.apache.solr.client.solrj.embedded.JettySolrRunner; |
| import org.apache.solr.client.solrj.impl.HttpSolrServer; |
| import org.apache.solr.client.solrj.request.UpdateRequest; |
| import org.apache.solr.client.solrj.request.QueryRequest; |
| import org.apache.solr.client.solrj.response.QueryResponse; |
| import org.apache.solr.client.solrj.response.UpdateResponse; |
| import org.apache.solr.common.SolrException; |
| import org.apache.solr.common.SolrDocument; |
| import org.apache.solr.common.SolrDocumentList; |
| import org.apache.solr.common.SolrInputDocument; |
| import org.apache.solr.common.params.SolrParams; |
| import org.apache.solr.common.params.ModifiableSolrParams; |
| import org.apache.solr.common.util.NamedList; |
| import org.apache.solr.common.util.SimpleOrderedMap; |
| import org.apache.solr.core.CachingDirectoryFactory; |
| import org.apache.solr.core.CoreContainer; |
| import org.apache.solr.core.SolrCore; |
| import org.apache.solr.core.StandardDirectoryFactory; |
| import org.apache.solr.servlet.SolrDispatchFilter; |
| import org.apache.solr.util.AbstractSolrTestCase; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| /** |
| * Test for ReplicationHandler |
| * |
| * |
| * @since 1.4 |
| */ |
| @Slow |
| public class TestReplicationHandler extends SolrTestCaseJ4 { |
| |
| |
| private static final String CONF_DIR = "solr" |
| + File.separator + "collection1" + File.separator + "conf" |
| + File.separator; |
| |
| JettySolrRunner masterJetty, slaveJetty, repeaterJetty; |
| SolrServer masterClient, slaveClient, repeaterClient; |
| SolrInstance master = null, slave = null, repeater = null; |
| |
| static String context = "/solr"; |
| |
| // number of docs to index... decremented for each test case to tell if we accidentally reuse |
| // index from previous test method |
| static int nDocs = 500; |
| |
| |
| @Before |
| public void setUp() throws Exception { |
| super.setUp(); |
| // System.setProperty("solr.directoryFactory", "solr.StandardDirectoryFactory"); |
| // For manual testing only |
| // useFactory(null); // force an FS factory |
| master = new SolrInstance("master", null); |
| master.setUp(); |
| masterJetty = createJetty(master); |
| masterClient = createNewSolrServer(masterJetty.getLocalPort()); |
| |
| slave = new SolrInstance("slave", masterJetty.getLocalPort()); |
| slave.setUp(); |
| slaveJetty = createJetty(slave); |
| slaveClient = createNewSolrServer(slaveJetty.getLocalPort()); |
| } |
| |
| public void clearIndexWithReplication() throws Exception { |
| if (numFound(query("*:*", masterClient)) != 0) { |
| masterClient.deleteByQuery("*:*"); |
| masterClient.commit(); |
| // wait for replication to sync & verify |
| assertEquals(0, numFound(rQuery(0, "*:*", slaveClient))); |
| } |
| } |
| |
| @Override |
| @After |
| public void tearDown() throws Exception { |
| super.tearDown(); |
| masterJetty.stop(); |
| slaveJetty.stop(); |
| master.tearDown(); |
| slave.tearDown(); |
| masterJetty = slaveJetty = null; |
| master = slave = null; |
| masterClient = slaveClient = null; |
| } |
| |
| private static JettySolrRunner createJetty(SolrInstance instance) throws Exception { |
| System.setProperty("solr.data.dir", instance.getDataDir()); |
| |
| JettySolrRunner jetty = new JettySolrRunner(instance.getHomeDir(), "/solr", 0); |
| |
| jetty.start(); |
| return jetty; |
| } |
| |
| private static SolrServer createNewSolrServer(int port) { |
| try { |
| // setup the server... |
| String url = "http://127.0.0.1:" + port + context; |
| HttpSolrServer s = new HttpSolrServer(url); |
| s.setConnectionTimeout(15000); |
| s.setSoTimeout(60000); |
| s.setDefaultMaxConnectionsPerHost(100); |
| s.setMaxTotalConnections(100); |
| return s; |
| } |
| catch (Exception ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| int index(SolrServer s, Object... fields) throws Exception { |
| SolrInputDocument doc = new SolrInputDocument(); |
| for (int i = 0; i < fields.length; i += 2) { |
| doc.addField((String) (fields[i]), fields[i + 1]); |
| } |
| return s.add(doc).getStatus(); |
| } |
| |
| NamedList query(String query, SolrServer s) throws SolrServerException { |
| NamedList res = new SimpleOrderedMap(); |
| ModifiableSolrParams params = new ModifiableSolrParams(); |
| |
| params.add("q", query); |
| params.add("sort","id desc"); |
| |
| QueryResponse qres = s.query(params); |
| |
| res = qres.getResponse(); |
| |
| return res; |
| } |
| |
| /** will sleep up to 30 seconds, looking for expectedDocCount */ |
| private NamedList rQuery(int expectedDocCount, String query, SolrServer server) throws Exception { |
| int timeSlept = 0; |
| NamedList res = query(query, server); |
| while (expectedDocCount != numFound(res) |
| && timeSlept < 30000) { |
| log.info("Waiting for " + expectedDocCount + " docs"); |
| timeSlept += 100; |
| Thread.sleep(100); |
| res = query(query, server); |
| } |
| return res; |
| } |
| |
| private long numFound(NamedList res) { |
| return ((SolrDocumentList) res.get("response")).getNumFound(); |
| } |
| |
| private NamedList<Object> getDetails(SolrServer s) throws Exception { |
| |
| |
| ModifiableSolrParams params = new ModifiableSolrParams(); |
| params.set("command","details"); |
| params.set("_trace","getDetails"); |
| params.set("qt","/replication"); |
| QueryRequest req = new QueryRequest(params); |
| |
| NamedList<Object> res = s.request(req); |
| |
| assertNotNull("null response from server", res); |
| |
| @SuppressWarnings("unchecked") NamedList<Object> details |
| = (NamedList<Object>) res.get("details"); |
| |
| assertNotNull("null details", details); |
| |
| return details; |
| } |
| |
| private NamedList<Object> getCommits(SolrServer s) throws Exception { |
| |
| |
| ModifiableSolrParams params = new ModifiableSolrParams(); |
| params.set("command","commits"); |
| params.set("_trace","getCommits"); |
| params.set("qt","/replication"); |
| QueryRequest req = new QueryRequest(params); |
| |
| NamedList<Object> res = s.request(req); |
| |
| assertNotNull("null response from server", res); |
| |
| |
| return res; |
| } |
| |
| private NamedList<Object> getIndexVersion(SolrServer s) throws Exception { |
| |
| ModifiableSolrParams params = new ModifiableSolrParams(); |
| params.set("command","indexversion"); |
| params.set("_trace","getIndexVersion"); |
| params.set("qt","/replication"); |
| QueryRequest req = new QueryRequest(params); |
| |
| NamedList<Object> res = s.request(req); |
| |
| assertNotNull("null response from server", res); |
| |
| |
| return res; |
| } |
| |
| private NamedList<Object> reloadCore(SolrServer s, String core) throws Exception { |
| |
| ModifiableSolrParams params = new ModifiableSolrParams(); |
| params.set("action","reload"); |
| params.set("core", core); |
| params.set("qt","/admin/cores"); |
| QueryRequest req = new QueryRequest(params); |
| |
| NamedList<Object> res = s.request(req); |
| |
| assertNotNull("null response from server", res); |
| |
| return res; |
| } |
| |
| @Test |
| public void doTestDetails() throws Exception { |
| clearIndexWithReplication(); |
| { |
| NamedList<Object> details = getDetails(masterClient); |
| |
| assertEquals("master isMaster?", |
| "true", details.get("isMaster")); |
| assertEquals("master isSlave?", |
| "false", details.get("isSlave")); |
| assertNotNull("master has master section", |
| details.get("master")); |
| } |
| |
| // check details on the slave a couple of times before & after fetching |
| for (int i = 0; i < 3; i++) { |
| NamedList<Object> details = getDetails(slaveClient); |
| |
| assertEquals("slave isMaster?", |
| "false", details.get("isMaster")); |
| assertEquals("slave isSlave?", |
| "true", details.get("isSlave")); |
| assertNotNull("slave has slave section", |
| details.get("slave")); |
| // SOLR-2677: assert not false negatives |
| Object timesFailed = ((NamedList)details.get("slave")).get(SnapPuller.TIMES_FAILED); |
| assertEquals("slave has fetch error count", |
| null, timesFailed); |
| |
| if (3 != i) { |
| // index & fetch |
| index(masterClient, "id", i, "name", "name = " + i); |
| masterClient.commit(); |
| pullFromTo(masterJetty, slaveJetty); |
| } |
| } |
| |
| SolrInstance repeater = null; |
| JettySolrRunner repeaterJetty = null; |
| SolrServer repeaterClient = null; |
| try { |
| repeater = new SolrInstance("repeater", masterJetty.getLocalPort()); |
| repeater.setUp(); |
| repeaterJetty = createJetty(repeater); |
| repeaterClient = createNewSolrServer(repeaterJetty.getLocalPort()); |
| |
| |
| NamedList<Object> details = getDetails(repeaterClient); |
| |
| assertEquals("repeater isMaster?", |
| "true", details.get("isMaster")); |
| assertEquals("repeater isSlave?", |
| "true", details.get("isSlave")); |
| assertNotNull("repeater has master section", |
| details.get("master")); |
| assertNotNull("repeater has slave section", |
| details.get("slave")); |
| |
| } finally { |
| try { |
| if (repeaterJetty != null) repeaterJetty.stop(); |
| } catch (Exception e) { /* :NOOP: */ } |
| try { |
| if (repeater != null) repeater.tearDown(); |
| } catch (Exception e) { /* :NOOP: */ } |
| } |
| } |
| |
| /** |
| * Verify that empty commits and/or commits with openSearcher=false |
| * on the master do not cause subsequent replication problems on the slave |
| */ |
| public void testEmptyCommits() throws Exception { |
| clearIndexWithReplication(); |
| |
| // add a doc to master and commit |
| index(masterClient, "id", "1", "name", "empty1"); |
| emptyUpdate(masterClient, "commit", "true"); |
| // force replication |
| pullFromMasterToSlave(); |
| // verify doc is on slave |
| rQuery(1, "name:empty1", slaveClient); |
| assertVersions(masterClient, slaveClient); |
| |
| // do a completely empty commit on master and force replication |
| emptyUpdate(masterClient, "commit", "true"); |
| pullFromMasterToSlave(); |
| |
| // add another doc and verify slave gets it |
| index(masterClient, "id", "2", "name", "empty2"); |
| emptyUpdate(masterClient, "commit", "true"); |
| // force replication |
| pullFromMasterToSlave(); |
| |
| rQuery(1, "name:empty2", slaveClient); |
| assertVersions(masterClient, slaveClient); |
| |
| // add a third doc but don't open a new searcher on master |
| index(masterClient, "id", "3", "name", "empty3"); |
| emptyUpdate(masterClient, "commit", "true", "openSearcher", "false"); |
| pullFromMasterToSlave(); |
| |
| // verify slave can search the doc, but master doesn't |
| rQuery(0, "name:empty3", masterClient); |
| rQuery(1, "name:empty3", slaveClient); |
| |
| // final doc with hard commit, slave and master both showing all docs |
| index(masterClient, "id", "4", "name", "empty4"); |
| emptyUpdate(masterClient, "commit", "true"); |
| pullFromMasterToSlave(); |
| |
| String q = "name:(empty1 empty2 empty3 empty4)"; |
| rQuery(4, q, masterClient); |
| rQuery(4, q, slaveClient); |
| assertVersions(masterClient, slaveClient); |
| |
| } |
| |
| @Test |
| public void doTestReplicateAfterWrite2Slave() throws Exception { |
| clearIndexWithReplication(); |
| nDocs--; |
| for (int i = 0; i < nDocs; i++) { |
| index(masterClient, "id", i, "name", "name = " + i); |
| } |
| |
| invokeReplicationCommand(masterJetty.getLocalPort(), "disableReplication"); |
| invokeReplicationCommand(slaveJetty.getLocalPort(), "disablepoll"); |
| |
| masterClient.commit(); |
| |
| assertEquals(nDocs, numFound(rQuery(nDocs, "*:*", masterClient))); |
| |
| // Make sure that both the index version and index generation on the slave is |
| // higher than that of the master, just to make the test harder. |
| |
| index(slaveClient, "id", 551, "name", "name = " + 551); |
| slaveClient.commit(true, true); |
| index(slaveClient, "id", 552, "name", "name = " + 552); |
| slaveClient.commit(true, true); |
| index(slaveClient, "id", 553, "name", "name = " + 553); |
| slaveClient.commit(true, true); |
| index(slaveClient, "id", 554, "name", "name = " + 554); |
| slaveClient.commit(true, true); |
| index(slaveClient, "id", 555, "name", "name = " + 555); |
| slaveClient.commit(true, true); |
| |
| //this doc is added to slave so it should show an item w/ that result |
| assertEquals(1, numFound(rQuery(1, "id:555", slaveClient))); |
| |
| //Let's fetch the index rather than rely on the polling. |
| invokeReplicationCommand(masterJetty.getLocalPort(), "enablereplication"); |
| invokeReplicationCommand(slaveJetty.getLocalPort(), "fetchindex"); |
| |
| /* |
| //the slave should have done a full copy of the index so the doc with id:555 should not be there in the slave now |
| slaveQueryRsp = rQuery(0, "id:555", slaveClient); |
| slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| assertEquals(0, slaveQueryResult.getNumFound()); |
| |
| // make sure we replicated the correct index from the master |
| slaveQueryRsp = rQuery(nDocs, "*:*", slaveClient); |
| slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| assertEquals(nDocs, slaveQueryResult.getNumFound()); |
| |
| */ |
| } |
| |
| //Simple function to wrap the invocation of replication commands on the various |
| //jetty servers. |
| private void invokeReplicationCommand(int pJettyPort, String pCommand) throws IOException |
| { |
| String masterUrl = "http://127.0.0.1:" + pJettyPort + "/solr/replication?command=" + pCommand; |
| try { |
| URL u = new URL(masterUrl); |
| InputStream stream = u.openStream(); |
| stream.close(); |
| } catch (IOException e) { |
| //e.printStackTrace(); |
| } |
| } |
| |
| @Test |
| public void doTestIndexAndConfigReplication() throws Exception { |
| clearIndexWithReplication(); |
| |
| nDocs--; |
| for (int i = 0; i < nDocs; i++) |
| index(masterClient, "id", i, "name", "name = " + i); |
| |
| masterClient.commit(); |
| |
| NamedList masterQueryRsp = rQuery(nDocs, "*:*", masterClient); |
| SolrDocumentList masterQueryResult = (SolrDocumentList) masterQueryRsp.get("response"); |
| assertEquals(nDocs, numFound(masterQueryRsp)); |
| |
| //get docs from slave and check if number is equal to master |
| NamedList slaveQueryRsp = rQuery(nDocs, "*:*", slaveClient); |
| SolrDocumentList slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| assertEquals(nDocs, numFound(slaveQueryRsp)); |
| |
| //compare results |
| String cmp = BaseDistributedSearchTestCase.compare(masterQueryResult, slaveQueryResult, 0, null); |
| assertEquals(null, cmp); |
| |
| assertVersions(masterClient, slaveClient); |
| |
| //start config files replication test |
| masterClient.deleteByQuery("*:*"); |
| masterClient.commit(); |
| |
| //change the schema on master |
| master.copyConfigFile(CONF_DIR + "schema-replication2.xml", "schema.xml"); |
| |
| masterJetty.stop(); |
| |
| masterJetty = createJetty(master); |
| masterClient = createNewSolrServer(masterJetty.getLocalPort()); |
| |
| slave.setTestPort(masterJetty.getLocalPort()); |
| slave.copyConfigFile(slave.getSolrConfigFile(), "solrconfig.xml"); |
| |
| slaveJetty.stop(); |
| |
| // setup an xslt dir to force subdir file replication |
| File masterXsltDir = new File(master.getConfDir() + File.separator + "xslt"); |
| File masterXsl = new File(masterXsltDir, "dummy.xsl"); |
| assertTrue("could not make dir " + masterXsltDir, masterXsltDir.mkdirs()); |
| assertTrue(masterXsl.createNewFile()); |
| |
| File slaveXsltDir = new File(slave.getConfDir() + File.separator + "xslt"); |
| File slaveXsl = new File(slaveXsltDir, "dummy.xsl"); |
| assertFalse(slaveXsltDir.exists()); |
| |
| slaveJetty = createJetty(slave); |
| slaveClient = createNewSolrServer(slaveJetty.getLocalPort()); |
| |
| //add a doc with new field and commit on master to trigger snappull from slave. |
| index(masterClient, "id", "2000", "name", "name = " + 2000, "newname", "newname = " + 2000); |
| masterClient.commit(); |
| |
| assertEquals(1, numFound( rQuery(1, "*:*", masterClient))); |
| |
| assertVersions(masterClient, slaveClient); |
| |
| slaveQueryRsp = rQuery(1, "*:*", slaveClient); |
| SolrDocument d = ((SolrDocumentList) slaveQueryRsp.get("response")).get(0); |
| assertEquals("newname = 2000", (String) d.getFieldValue("newname")); |
| |
| assertTrue(slaveXsltDir.isDirectory()); |
| assertTrue(slaveXsl.exists()); |
| |
| checkForSingleIndex(masterJetty); |
| checkForSingleIndex(slaveJetty); |
| |
| } |
| |
| @Test |
| public void doTestStopPoll() throws Exception { |
| clearIndexWithReplication(); |
| |
| // Test: |
| // setup master/slave. |
| // stop polling on slave, add a doc to master and verify slave hasn't picked it. |
| nDocs--; |
| for (int i = 0; i < nDocs; i++) |
| index(masterClient, "id", i, "name", "name = " + i); |
| |
| masterClient.commit(); |
| |
| NamedList masterQueryRsp = rQuery(nDocs, "*:*", masterClient); |
| SolrDocumentList masterQueryResult = (SolrDocumentList) masterQueryRsp.get("response"); |
| assertEquals(nDocs, numFound(masterQueryRsp)); |
| |
| //get docs from slave and check if number is equal to master |
| NamedList slaveQueryRsp = rQuery(nDocs, "*:*", slaveClient); |
| SolrDocumentList slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| assertEquals(nDocs, numFound(slaveQueryRsp)); |
| |
| //compare results |
| String cmp = BaseDistributedSearchTestCase.compare(masterQueryResult, slaveQueryResult, 0, null); |
| assertEquals(null, cmp); |
| |
| // start stop polling test |
| invokeReplicationCommand(slaveJetty.getLocalPort(), "disablepoll"); |
| |
| index(masterClient, "id", 501, "name", "name = " + 501); |
| masterClient.commit(); |
| |
| //get docs from master and check if number is equal to master |
| assertEquals(nDocs+1, numFound(rQuery(nDocs+1, "*:*", masterClient))); |
| |
| // NOTE: this test is wierd, we want to verify it DOESNT replicate... |
| // for now, add a sleep for this.., but the logic is wierd. |
| Thread.sleep(3000); |
| |
| //get docs from slave and check if number is not equal to master; polling is disabled |
| assertEquals(nDocs, numFound(rQuery(nDocs, "*:*", slaveClient))); |
| |
| // re-enable replication |
| invokeReplicationCommand(slaveJetty.getLocalPort(), "enablepoll"); |
| |
| assertEquals(nDocs+1, numFound(rQuery(nDocs+1, "*:*", slaveClient))); |
| } |
| |
| @Test |
| public void doTestSnapPullWithMasterUrl() throws Exception { |
| //change solrconfig on slave |
| //this has no entry for pollinginterval |
| slave.copyConfigFile(CONF_DIR + "solrconfig-slave1.xml", "solrconfig.xml"); |
| slaveJetty.stop(); |
| slaveJetty = createJetty(slave); |
| slaveClient = createNewSolrServer(slaveJetty.getLocalPort()); |
| |
| masterClient.deleteByQuery("*:*"); |
| slaveClient.deleteByQuery("*:*"); |
| slaveClient.commit(); |
| nDocs--; |
| for (int i = 0; i < nDocs; i++) |
| index(masterClient, "id", i, "name", "name = " + i); |
| |
| // make sure prepareCommit doesn't mess up commit (SOLR-3938) |
| |
| // todo: make SolrJ easier to pass arbitrary params to |
| // TODO: precommit WILL screw with the rest of this test |
| String masterUrl = "http://127.0.0.1:" + masterJetty.getLocalPort() + "/solr/update?prepareCommit=true"; |
| URL url = new URL(masterUrl); |
| // InputStream stream = url.openStream(); |
| // try { |
| // stream.close(); |
| // } catch (IOException e) { |
| // //e.printStackTrace(); |
| // } |
| |
| masterClient.commit(); |
| |
| NamedList masterQueryRsp = rQuery(nDocs, "*:*", masterClient); |
| SolrDocumentList masterQueryResult = (SolrDocumentList) masterQueryRsp.get("response"); |
| assertEquals(nDocs, masterQueryResult.getNumFound()); |
| |
| // snappull |
| masterUrl = "http://127.0.0.1:" + slaveJetty.getLocalPort() + "/solr/replication?command=fetchindex&masterUrl="; |
| masterUrl += "http://127.0.0.1:" + masterJetty.getLocalPort() + "/solr/replication"; |
| url = new URL(masterUrl); |
| InputStream stream = url.openStream(); |
| try { |
| stream.close(); |
| } catch (IOException e) { |
| //e.printStackTrace(); |
| } |
| |
| //get docs from slave and check if number is equal to master |
| NamedList slaveQueryRsp = rQuery(nDocs, "*:*", slaveClient); |
| SolrDocumentList slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| assertEquals(nDocs, slaveQueryResult.getNumFound()); |
| //compare results |
| String cmp = BaseDistributedSearchTestCase.compare(masterQueryResult, slaveQueryResult, 0, null); |
| assertEquals(null, cmp); |
| |
| // snappull from the slave to the master |
| |
| for (int i = nDocs; i < nDocs + 3; i++) |
| index(slaveClient, "id", i, "name", "name = " + i); |
| |
| slaveClient.commit(); |
| |
| pullFromSlaveToMaster(); |
| rQuery(nDocs + 3, "*:*", masterClient); |
| |
| //get docs from slave and check if number is equal to master |
| slaveQueryRsp = rQuery(nDocs + 3, "*:*", slaveClient); |
| slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| assertEquals(nDocs + 3, slaveQueryResult.getNumFound()); |
| //compare results |
| masterQueryRsp = rQuery(nDocs + 3, "*:*", masterClient); |
| masterQueryResult = (SolrDocumentList) masterQueryRsp.get("response"); |
| cmp = BaseDistributedSearchTestCase.compare(masterQueryResult, slaveQueryResult, 0, null); |
| assertEquals(null, cmp); |
| |
| assertVersions(masterClient, slaveClient); |
| |
| pullFromSlaveToMaster(); |
| |
| //get docs from slave and check if number is equal to master |
| slaveQueryRsp = rQuery(nDocs + 3, "*:*", slaveClient); |
| slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| assertEquals(nDocs + 3, slaveQueryResult.getNumFound()); |
| //compare results |
| masterQueryRsp = rQuery(nDocs + 3, "*:*", masterClient); |
| masterQueryResult = (SolrDocumentList) masterQueryRsp.get("response"); |
| cmp = BaseDistributedSearchTestCase.compare(masterQueryResult, slaveQueryResult, 0, null); |
| assertEquals(null, cmp); |
| |
| assertVersions(masterClient, slaveClient); |
| |
| // now force a new index directory |
| for (int i = nDocs + 3; i < nDocs + 7; i++) |
| index(masterClient, "id", i, "name", "name = " + i); |
| |
| masterClient.commit(); |
| |
| pullFromSlaveToMaster(); |
| rQuery((int) slaveQueryResult.getNumFound(), "*:*", masterClient); |
| |
| //get docs from slave and check if number is equal to master |
| slaveQueryRsp = rQuery(nDocs + 3, "*:*", slaveClient); |
| slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| assertEquals(nDocs + 3, slaveQueryResult.getNumFound()); |
| //compare results |
| masterQueryRsp = rQuery(nDocs + 3, "*:*", masterClient); |
| masterQueryResult = (SolrDocumentList) masterQueryRsp.get("response"); |
| cmp = BaseDistributedSearchTestCase.compare(masterQueryResult, slaveQueryResult, 0, null); |
| assertEquals(null, cmp); |
| |
| assertVersions(masterClient, slaveClient); |
| pullFromSlaveToMaster(); |
| |
| //get docs from slave and check if number is equal to master |
| slaveQueryRsp = rQuery(nDocs + 3, "*:*", slaveClient); |
| slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| assertEquals(nDocs + 3, slaveQueryResult.getNumFound()); |
| //compare results |
| masterQueryRsp = rQuery(nDocs + 3, "*:*", masterClient); |
| masterQueryResult = (SolrDocumentList) masterQueryRsp.get("response"); |
| cmp = BaseDistributedSearchTestCase.compare(masterQueryResult, slaveQueryResult, 0, null); |
| assertEquals(null, cmp); |
| |
| assertVersions(masterClient, slaveClient); |
| |
| NamedList<Object> details = getDetails(masterClient); |
| |
| details = getDetails(slaveClient); |
| |
| checkForSingleIndex(masterJetty); |
| checkForSingleIndex(slaveJetty); |
| } |
| |
| |
| @Test |
| public void doTestStressReplication() throws Exception { |
| // change solrconfig on slave |
| // this has no entry for pollinginterval |
| |
| // get us a straight standard fs dir rather than mock*dir |
| boolean useStraightStandardDirectory = random().nextBoolean(); |
| |
| if (useStraightStandardDirectory) { |
| useFactory(null); |
| } |
| final String SLAVE_SCHEMA_1 = "schema-replication1.xml"; |
| final String SLAVE_SCHEMA_2 = "schema-replication2.xml"; |
| String slaveSchema = SLAVE_SCHEMA_1; |
| |
| try { |
| |
| slave.copyConfigFile(CONF_DIR +"solrconfig-slave1.xml", "solrconfig.xml"); |
| slave.copyConfigFile(CONF_DIR +slaveSchema, "schema.xml"); |
| slaveJetty.stop(); |
| slaveJetty = createJetty(slave); |
| slaveClient = createNewSolrServer(slaveJetty.getLocalPort()); |
| |
| master.copyConfigFile(CONF_DIR + "solrconfig-master3.xml", |
| "solrconfig.xml"); |
| masterJetty.stop(); |
| masterJetty = createJetty(master); |
| masterClient = createNewSolrServer(masterJetty.getLocalPort()); |
| |
| masterClient.deleteByQuery("*:*"); |
| slaveClient.deleteByQuery("*:*"); |
| slaveClient.commit(); |
| |
| int maxDocs = TEST_NIGHTLY ? 1000 : 200; |
| int rounds = TEST_NIGHTLY ? 80 : 8; |
| int totalDocs = 0; |
| int id = 0; |
| for (int x = 0; x < rounds; x++) { |
| |
| final boolean confCoreReload = random().nextBoolean(); |
| if (confCoreReload) { |
| // toggle the schema file used |
| |
| slaveSchema = slaveSchema.equals(SLAVE_SCHEMA_1) ? |
| SLAVE_SCHEMA_2 : SLAVE_SCHEMA_1; |
| master.copyConfigFile(CONF_DIR + slaveSchema, "schema.xml"); |
| } |
| |
| int docs = random().nextInt(maxDocs); |
| for (int i = 0; i < docs; i++) { |
| index(masterClient, "id", id++, "name", "name = " + i); |
| } |
| |
| totalDocs += docs; |
| masterClient.commit(); |
| |
| NamedList masterQueryRsp = rQuery(totalDocs, "*:*", masterClient); |
| SolrDocumentList masterQueryResult = (SolrDocumentList) masterQueryRsp |
| .get("response"); |
| assertEquals(totalDocs, masterQueryResult.getNumFound()); |
| |
| // snappull |
| Date slaveCoreStart = watchCoreStartAt(slaveClient, 30*1000, null); |
| pullFromMasterToSlave(); |
| if (confCoreReload) { |
| watchCoreStartAt(slaveClient, 30*1000, slaveCoreStart); |
| } |
| |
| // get docs from slave and check if number is equal to master |
| NamedList slaveQueryRsp = rQuery(totalDocs, "*:*", slaveClient); |
| SolrDocumentList slaveQueryResult = (SolrDocumentList) slaveQueryRsp |
| .get("response"); |
| assertEquals(totalDocs, slaveQueryResult.getNumFound()); |
| // compare results |
| String cmp = BaseDistributedSearchTestCase.compare(masterQueryResult, |
| slaveQueryResult, 0, null); |
| assertEquals(null, cmp); |
| |
| assertVersions(masterClient, slaveClient); |
| |
| checkForSingleIndex(masterJetty); |
| checkForSingleIndex(slaveJetty); |
| |
| if (random().nextBoolean()) { |
| // move the slave ahead |
| for (int i = 0; i < 3; i++) { |
| index(slaveClient, "id", id++, "name", "name = " + i); |
| } |
| slaveClient.commit(); |
| } |
| |
| } |
| |
| } finally { |
| if (useStraightStandardDirectory) { |
| resetFactory(); |
| } |
| } |
| } |
| |
| private void checkForSingleIndex(JettySolrRunner jetty) { |
| CoreContainer cores = ((SolrDispatchFilter) jetty.getDispatchFilter().getFilter()).getCores(); |
| Collection<SolrCore> theCores = cores.getCores(); |
| for (SolrCore core : theCores) { |
| String ddir = core.getDataDir(); |
| CachingDirectoryFactory dirFactory = (CachingDirectoryFactory) core.getDirectoryFactory(); |
| synchronized (dirFactory) { |
| Set<String> livePaths = dirFactory.getLivePaths(); |
| // one for data, one for hte index under data |
| assertEquals(livePaths.toString(), 2, livePaths.size()); |
| // :TODO: assert that one of the paths is a subpath of hte other |
| } |
| if (dirFactory instanceof StandardDirectoryFactory) { |
| System.out.println(Arrays.asList(new File(ddir).list())); |
| assertEquals(Arrays.asList(new File(ddir).list()).toString(), 1, indexDirCount(ddir)); |
| } |
| } |
| } |
| |
| private int indexDirCount(String ddir) { |
| String[] list = new File(ddir).list(); |
| int cnt = 0; |
| for (String file : list) { |
| if (!file.endsWith(".properties")) { |
| cnt++; |
| } |
| } |
| return cnt; |
| } |
| |
| private void pullFromMasterToSlave() throws MalformedURLException, |
| IOException { |
| pullFromTo(masterJetty, slaveJetty); |
| } |
| |
| @Test |
| public void doTestRepeater() throws Exception { |
| // no polling |
| slave.copyConfigFile(CONF_DIR + "solrconfig-slave1.xml", "solrconfig.xml"); |
| slaveJetty.stop(); |
| slaveJetty = createJetty(slave); |
| slaveClient = createNewSolrServer(slaveJetty.getLocalPort()); |
| |
| try { |
| repeater = new SolrInstance("repeater", null); |
| repeater.setUp(); |
| repeater.copyConfigFile(CONF_DIR + "solrconfig-repeater.xml", |
| "solrconfig.xml"); |
| repeaterJetty = createJetty(repeater); |
| repeaterClient = createNewSolrServer(repeaterJetty.getLocalPort()); |
| |
| for (int i = 0; i < 3; i++) |
| index(masterClient, "id", i, "name", "name = " + i); |
| |
| masterClient.commit(); |
| |
| pullFromTo(masterJetty, repeaterJetty); |
| |
| rQuery(3, "*:*", repeaterClient); |
| |
| pullFromTo(repeaterJetty, slaveJetty); |
| |
| rQuery(3, "*:*", slaveClient); |
| |
| assertVersions(masterClient, repeaterClient); |
| assertVersions(repeaterClient, slaveClient); |
| |
| for (int i = 0; i < 4; i++) |
| index(repeaterClient, "id", i, "name", "name = " + i); |
| repeaterClient.commit(); |
| |
| pullFromTo(masterJetty, repeaterJetty); |
| |
| rQuery(3, "*:*", repeaterClient); |
| |
| pullFromTo(repeaterJetty, slaveJetty); |
| |
| rQuery(3, "*:*", slaveClient); |
| |
| for (int i = 3; i < 6; i++) |
| index(masterClient, "id", i, "name", "name = " + i); |
| |
| masterClient.commit(); |
| |
| pullFromTo(masterJetty, repeaterJetty); |
| |
| rQuery(6, "*:*", repeaterClient); |
| |
| pullFromTo(repeaterJetty, slaveJetty); |
| |
| rQuery(6, "*:*", slaveClient); |
| |
| } finally { |
| if (repeater != null) { |
| repeaterJetty.stop(); |
| repeater.tearDown(); |
| repeaterJetty = null; |
| } |
| } |
| |
| } |
| |
| private void assertVersions(SolrServer client1, SolrServer client2) throws Exception { |
| NamedList<Object> details = getDetails(client1); |
| ArrayList<NamedList<Object>> commits = (ArrayList<NamedList<Object>>) details.get("commits"); |
| Long maxVersionClient1 = getVersion(client1); |
| Long maxVersionClient2 = getVersion(client2); |
| |
| if (maxVersionClient1 > 0 && maxVersionClient2 > 0) { |
| assertEquals(maxVersionClient1, maxVersionClient2); |
| } |
| |
| // check vs /replication?command=indexversion call |
| ModifiableSolrParams params = new ModifiableSolrParams(); |
| params.set("qt", "/replication"); |
| params.set("_trace", "assertVersions"); |
| params.set("command", "indexversion"); |
| QueryRequest req = new QueryRequest(params); |
| NamedList<Object> resp = client1.request(req); |
| |
| Long version = (Long) resp.get("indexversion"); |
| assertEquals(maxVersionClient1, version); |
| |
| // check vs /replication?command=indexversion call |
| resp = client2.request(req); |
| version = (Long) resp.get("indexversion"); |
| assertEquals(maxVersionClient2, version); |
| } |
| |
| private Long getVersion(SolrServer client) throws Exception { |
| NamedList<Object> details; |
| ArrayList<NamedList<Object>> commits; |
| details = getDetails(client); |
| commits = (ArrayList<NamedList<Object>>) details.get("commits"); |
| Long maxVersionSlave= 0L; |
| for(NamedList<Object> commit : commits) { |
| Long version = (Long) commit.get("indexVersion"); |
| maxVersionSlave = Math.max(version, maxVersionSlave); |
| } |
| return maxVersionSlave; |
| } |
| |
| private void pullFromSlaveToMaster() throws MalformedURLException, |
| IOException { |
| pullFromTo(slaveJetty, masterJetty); |
| } |
| |
| private void pullFromTo(JettySolrRunner from, JettySolrRunner to) throws MalformedURLException, IOException { |
| String masterUrl; |
| URL url; |
| InputStream stream; |
| masterUrl = "http://127.0.0.1:" + to.getLocalPort() |
| + "/solr/replication?wait=true&command=fetchindex&masterUrl="; |
| masterUrl += "http://127.0.0.1:" + from.getLocalPort() |
| + "/solr/replication"; |
| url = new URL(masterUrl); |
| stream = url.openStream(); |
| try { |
| stream.close(); |
| } catch (IOException e) { |
| // e.printStackTrace(); |
| } |
| } |
| |
| @Test |
| public void doTestReplicateAfterStartup() throws Exception { |
| //stop slave |
| slaveJetty.stop(); |
| |
| nDocs--; |
| masterClient.deleteByQuery("*:*"); |
| |
| masterClient.commit(); |
| |
| |
| |
| //change solrconfig having 'replicateAfter startup' option on master |
| master.copyConfigFile(CONF_DIR + "solrconfig-master2.xml", |
| "solrconfig.xml"); |
| |
| masterJetty.stop(); |
| |
| masterJetty = createJetty(master); |
| masterClient = createNewSolrServer(masterJetty.getLocalPort()); |
| |
| for (int i = 0; i < nDocs; i++) |
| index(masterClient, "id", i, "name", "name = " + i); |
| |
| masterClient.commit(); |
| |
| NamedList masterQueryRsp = rQuery(nDocs, "*:*", masterClient); |
| SolrDocumentList masterQueryResult = (SolrDocumentList) masterQueryRsp.get("response"); |
| assertEquals(nDocs, masterQueryResult.getNumFound()); |
| |
| |
| slave.setTestPort(masterJetty.getLocalPort()); |
| slave.copyConfigFile(slave.getSolrConfigFile(), "solrconfig.xml"); |
| |
| //start slave |
| slaveJetty = createJetty(slave); |
| slaveClient = createNewSolrServer(slaveJetty.getLocalPort()); |
| |
| //get docs from slave and check if number is equal to master |
| NamedList slaveQueryRsp = rQuery(nDocs, "*:*", slaveClient); |
| SolrDocumentList slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| assertEquals(nDocs, slaveQueryResult.getNumFound()); |
| |
| //compare results |
| String cmp = BaseDistributedSearchTestCase.compare(masterQueryResult, slaveQueryResult, 0, null); |
| assertEquals(null, cmp); |
| |
| } |
| |
| @Test |
| public void doTestReplicateAfterStartupWithNoActivity() throws Exception { |
| useFactory(null); |
| try { |
| |
| // stop slave |
| slaveJetty.stop(); |
| |
| nDocs--; |
| masterClient.deleteByQuery("*:*"); |
| |
| masterClient.commit(); |
| |
| // change solrconfig having 'replicateAfter startup' option on master |
| master.copyConfigFile(CONF_DIR + "solrconfig-master2.xml", |
| "solrconfig.xml"); |
| |
| masterJetty.stop(); |
| |
| masterJetty = createJetty(master); |
| masterClient = createNewSolrServer(masterJetty.getLocalPort()); |
| |
| for (int i = 0; i < nDocs; i++) |
| index(masterClient, "id", i, "name", "name = " + i); |
| |
| masterClient.commit(); |
| |
| // now we restart to test what happens with no activity before the slave |
| // tries to |
| // replicate |
| masterJetty.stop(); |
| masterJetty.start(true); |
| |
| // masterClient = createNewSolrServer(masterJetty.getLocalPort()); |
| |
| NamedList masterQueryRsp = rQuery(nDocs, "*:*", masterClient); |
| SolrDocumentList masterQueryResult = (SolrDocumentList) masterQueryRsp |
| .get("response"); |
| assertEquals(nDocs, masterQueryResult.getNumFound()); |
| |
| slave.setTestPort(masterJetty.getLocalPort()); |
| slave.copyConfigFile(slave.getSolrConfigFile(), "solrconfig.xml"); |
| |
| // start slave |
| slaveJetty = createJetty(slave); |
| slaveClient = createNewSolrServer(slaveJetty.getLocalPort()); |
| |
| // get docs from slave and check if number is equal to master |
| NamedList slaveQueryRsp = rQuery(nDocs, "*:*", slaveClient); |
| SolrDocumentList slaveQueryResult = (SolrDocumentList) slaveQueryRsp |
| .get("response"); |
| assertEquals(nDocs, slaveQueryResult.getNumFound()); |
| |
| // compare results |
| String cmp = BaseDistributedSearchTestCase.compare(masterQueryResult, |
| slaveQueryResult, 0, null); |
| assertEquals(null, cmp); |
| |
| } finally { |
| resetFactory(); |
| } |
| } |
| |
| @Test |
| public void doTestReplicateAfterCoreReload() throws Exception { |
| int docs = TEST_NIGHTLY ? 200000 : 0; |
| |
| //stop slave |
| slaveJetty.stop(); |
| |
| |
| //change solrconfig having 'replicateAfter startup' option on master |
| master.copyConfigFile(CONF_DIR + "solrconfig-master3.xml", |
| "solrconfig.xml"); |
| |
| masterJetty.stop(); |
| |
| masterJetty = createJetty(master); |
| masterClient = createNewSolrServer(masterJetty.getLocalPort()); |
| |
| masterClient.deleteByQuery("*:*"); |
| for (int i = 0; i < docs; i++) |
| index(masterClient, "id", i, "name", "name = " + i); |
| |
| masterClient.commit(); |
| |
| NamedList masterQueryRsp = rQuery(docs, "*:*", masterClient); |
| SolrDocumentList masterQueryResult = (SolrDocumentList) masterQueryRsp.get("response"); |
| assertEquals(docs, masterQueryResult.getNumFound()); |
| |
| slave.setTestPort(masterJetty.getLocalPort()); |
| slave.copyConfigFile(slave.getSolrConfigFile(), "solrconfig.xml"); |
| |
| //start slave |
| slaveJetty = createJetty(slave); |
| slaveClient = createNewSolrServer(slaveJetty.getLocalPort()); |
| |
| //get docs from slave and check if number is equal to master |
| NamedList slaveQueryRsp = rQuery(docs, "*:*", slaveClient); |
| SolrDocumentList slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| assertEquals(docs, slaveQueryResult.getNumFound()); |
| |
| //compare results |
| String cmp = BaseDistributedSearchTestCase.compare(masterQueryResult, slaveQueryResult, 0, null); |
| assertEquals(null, cmp); |
| |
| Object version = getIndexVersion(masterClient).get("indexversion"); |
| NamedList<Object> commits = getCommits(masterClient); |
| |
| reloadCore(masterClient, "collection1"); |
| |
| assertEquals(version, getIndexVersion(masterClient).get("indexversion")); |
| assertEquals(commits.get("commits"), getCommits(masterClient).get("commits")); |
| |
| index(masterClient, "id", docs + 10, "name", "name = 1"); |
| index(masterClient, "id", docs + 20, "name", "name = 2"); |
| |
| masterClient.commit(); |
| |
| NamedList resp = rQuery(docs + 2, "*:*", masterClient); |
| masterQueryResult = (SolrDocumentList) resp.get("response"); |
| assertEquals(docs + 2, masterQueryResult.getNumFound()); |
| |
| //get docs from slave and check if number is equal to master |
| slaveQueryRsp = rQuery(docs + 2, "*:*", slaveClient); |
| slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| assertEquals(docs + 2, slaveQueryResult.getNumFound()); |
| |
| } |
| |
| @Test |
| public void doTestIndexAndConfigAliasReplication() throws Exception { |
| clearIndexWithReplication(); |
| |
| nDocs--; |
| for (int i = 0; i < nDocs; i++) |
| index(masterClient, "id", i, "name", "name = " + i); |
| |
| masterClient.commit(); |
| |
| NamedList masterQueryRsp = rQuery(nDocs, "*:*", masterClient); |
| SolrDocumentList masterQueryResult = (SolrDocumentList) masterQueryRsp.get("response"); |
| assertEquals(nDocs, masterQueryResult.getNumFound()); |
| |
| //get docs from slave and check if number is equal to master |
| NamedList slaveQueryRsp = rQuery(nDocs, "*:*", slaveClient); |
| SolrDocumentList slaveQueryResult = (SolrDocumentList) slaveQueryRsp.get("response"); |
| |
| assertEquals(nDocs, slaveQueryResult.getNumFound()); |
| |
| //compare results |
| String cmp = BaseDistributedSearchTestCase.compare(masterQueryResult, slaveQueryResult, 0, null); |
| assertEquals(null, cmp); |
| |
| //start config files replication test |
| //clear master index |
| masterClient.deleteByQuery("*:*"); |
| masterClient.commit(); |
| rQuery(0, "*:*", masterClient); // sanity check w/retry |
| |
| //change solrconfig on master |
| master.copyConfigFile(CONF_DIR + "solrconfig-master1.xml", |
| "solrconfig.xml"); |
| |
| //change schema on master |
| master.copyConfigFile(CONF_DIR + "schema-replication2.xml", |
| "schema.xml"); |
| |
| //keep a copy of the new schema |
| master.copyConfigFile(CONF_DIR + "schema-replication2.xml", |
| "schema-replication2.xml"); |
| |
| masterJetty.stop(); |
| |
| masterJetty = createJetty(master); |
| masterClient = createNewSolrServer(masterJetty.getLocalPort()); |
| |
| slave.setTestPort(masterJetty.getLocalPort()); |
| slave.copyConfigFile(slave.getSolrConfigFile(), "solrconfig.xml"); |
| |
| slaveJetty.stop(); |
| slaveJetty = createJetty(slave); |
| slaveClient = createNewSolrServer(slaveJetty.getLocalPort()); |
| |
| slaveClient.deleteByQuery("*:*"); |
| slaveClient.commit(); |
| rQuery(0, "*:*", slaveClient); // sanity check w/retry |
| |
| // record collection1's start time on slave |
| final Date slaveStartTime = watchCoreStartAt(slaveClient, 30*1000, null); |
| |
| //add a doc with new field and commit on master to trigger snappull from slave. |
| index(masterClient, "id", "2000", "name", "name = " + 2000, "newname", "n2000"); |
| masterClient.commit(); |
| rQuery(1, "newname:n2000", masterClient); // sanity check |
| |
| // wait for slave to reload core by watching updated startTime |
| watchCoreStartAt(slaveClient, 30*1000, slaveStartTime); |
| |
| NamedList masterQueryRsp2 = rQuery(1, "id:2000", masterClient); |
| SolrDocumentList masterQueryResult2 = (SolrDocumentList) masterQueryRsp2.get("response"); |
| assertEquals(1, masterQueryResult2.getNumFound()); |
| |
| NamedList slaveQueryRsp2 = rQuery(1, "id:2000", slaveClient); |
| SolrDocumentList slaveQueryResult2 = (SolrDocumentList) slaveQueryRsp2.get("response"); |
| assertEquals(1, slaveQueryResult2.getNumFound()); |
| |
| index(slaveClient, "id", "2001", "name", "name = " + 2001, "newname", "n2001"); |
| slaveClient.commit(); |
| |
| slaveQueryRsp = rQuery(1, "id:2001", slaveClient); |
| SolrDocument d = ((SolrDocumentList) slaveQueryRsp.get("response")).get(0); |
| assertEquals("n2001", (String) d.getFieldValue("newname")); |
| |
| checkForSingleIndex(masterJetty); |
| checkForSingleIndex(slaveJetty); |
| } |
| |
| |
| @Test |
| public void doTestBackup() throws Exception { |
| String configFile = "solrconfig-master1.xml"; |
| boolean addNumberToKeepInRequest = true; |
| String backupKeepParamName = ReplicationHandler.NUMBER_BACKUPS_TO_KEEP_REQUEST_PARAM; |
| if(random().nextBoolean()) { |
| configFile = "solrconfig-master1-keepOneBackup.xml"; |
| addNumberToKeepInRequest = false; |
| backupKeepParamName = ReplicationHandler.NUMBER_BACKUPS_TO_KEEP_INIT_PARAM; |
| } |
| |
| masterJetty.stop(); |
| master.copyConfigFile(CONF_DIR + configFile, |
| "solrconfig.xml"); |
| |
| masterJetty = createJetty(master); |
| masterClient = createNewSolrServer(masterJetty.getLocalPort()); |
| |
| nDocs--; |
| masterClient.deleteByQuery("*:*"); |
| for (int i = 0; i < nDocs; i++) |
| index(masterClient, "id", i, "name", "name = " + i); |
| |
| masterClient.commit(); |
| |
| class BackupThread extends Thread { |
| volatile String fail = null; |
| final boolean addNumberToKeepInRequest; |
| String backupKeepParamName; |
| BackupThread(boolean addNumberToKeepInRequest, String backupKeepParamName) { |
| this.addNumberToKeepInRequest = addNumberToKeepInRequest; |
| this.backupKeepParamName = backupKeepParamName; |
| } |
| @Override |
| public void run() { |
| String masterUrl = |
| "http://127.0.0.1:" + masterJetty.getLocalPort() + "/solr/replication?command=" + ReplicationHandler.CMD_BACKUP + |
| (addNumberToKeepInRequest ? "&" + backupKeepParamName + "=1" : ""); |
| URL url; |
| InputStream stream = null; |
| try { |
| url = new URL(masterUrl); |
| stream = url.openStream(); |
| stream.close(); |
| } catch (Exception e) { |
| fail = e.getMessage(); |
| } finally { |
| IOUtils.closeQuietly(stream); |
| } |
| |
| }; |
| }; |
| |
| class CheckStatus extends Thread { |
| volatile String fail = null; |
| volatile String response = null; |
| volatile boolean success = false; |
| volatile String backupTimestamp = null; |
| final String lastBackupTimestamp; |
| final Pattern p = Pattern.compile("<str name=\"snapshotCompletedAt\">(.*?)</str>"); |
| |
| CheckStatus(String lastBackupTimestamp) { |
| this.lastBackupTimestamp = lastBackupTimestamp; |
| } |
| @Override |
| public void run() { |
| String masterUrl = "http://127.0.0.1:" + masterJetty.getLocalPort() + "/solr/replication?command=" + ReplicationHandler.CMD_DETAILS; |
| URL url; |
| InputStream stream = null; |
| try { |
| url = new URL(masterUrl); |
| stream = url.openStream(); |
| response = IOUtils.toString(stream, "UTF-8"); |
| if(response.contains("<str name=\"status\">success</str>")) { |
| Matcher m = p.matcher(response); |
| if(!m.find()) { |
| fail("could not find the completed timestamp in response."); |
| } |
| backupTimestamp = m.group(1); |
| if(!backupTimestamp.equals(lastBackupTimestamp)) { |
| success = true; |
| } |
| } |
| stream.close(); |
| } catch (Exception e) { |
| fail = e.getMessage(); |
| } finally { |
| IOUtils.closeQuietly(stream); |
| } |
| |
| }; |
| }; |
| |
| File[] snapDir = new File[2]; |
| String firstBackupTimestamp = null; |
| for(int i=0 ; i<2 ; i++) { |
| BackupThread backupThread = new BackupThread(addNumberToKeepInRequest, backupKeepParamName); |
| backupThread.start(); |
| |
| File dataDir = new File(master.getDataDir()); |
| |
| int waitCnt = 0; |
| CheckStatus checkStatus = new CheckStatus(firstBackupTimestamp); |
| while(true) { |
| checkStatus.run(); |
| if(checkStatus.fail != null) { |
| fail(checkStatus.fail); |
| } |
| if(checkStatus.success) { |
| if(i==0) { |
| firstBackupTimestamp = checkStatus.backupTimestamp; |
| Thread.sleep(1000); //ensure the next backup will have a different timestamp. |
| } |
| break; |
| } |
| Thread.sleep(200); |
| if(waitCnt == 20) { |
| fail("Backup success not detected:" + checkStatus.response); |
| } |
| waitCnt++; |
| } |
| |
| if(backupThread.fail != null) { |
| fail(backupThread.fail); |
| } |
| |
| File[] files = dataDir.listFiles(new FilenameFilter() { |
| |
| @Override |
| public boolean accept(File dir, String name) { |
| if(name.startsWith("snapshot")) { |
| return true; |
| } |
| return false; |
| } |
| }); |
| assertEquals(1, files.length); |
| snapDir[i] = files[0]; |
| Directory dir = new SimpleFSDirectory(snapDir[i].getAbsoluteFile()); |
| IndexReader reader = DirectoryReader.open(dir); |
| IndexSearcher searcher = new IndexSearcher(reader); |
| TopDocs hits = searcher.search(new MatchAllDocsQuery(), 1); |
| assertEquals(nDocs, hits.totalHits); |
| reader.close(); |
| dir.close(); |
| } |
| if(snapDir[0].exists()) { |
| fail("The first backup should have been cleaned up because " + backupKeepParamName + " was set to 1."); |
| } |
| |
| for(int i=0 ; i< snapDir.length ; i++) { |
| AbstractSolrTestCase.recurseDelete(snapDir[i]); // clean up the snap dir |
| } |
| } |
| |
| /* character copy of file using UTF-8 */ |
| private static void copyFile(File src, File dst) throws IOException { |
| copyFile(src, dst, null, false); |
| } |
| |
| /** |
| * character copy of file using UTF-8. If port is non-null, will be substituted any time "TEST_PORT" is found. |
| */ |
| private static void copyFile(File src, File dst, Integer port, boolean internalCompression) throws IOException { |
| BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(src), "UTF-8")); |
| Writer out = new OutputStreamWriter(new FileOutputStream(dst), "UTF-8"); |
| |
| for (String line = in.readLine(); null != line; line = in.readLine()) { |
| |
| if (null != port) |
| line = line.replace("TEST_PORT", port.toString()); |
| |
| line = line.replace("COMPRESSION", internalCompression?"internal":"false"); |
| |
| out.write(line); |
| } |
| in.close(); |
| out.close(); |
| } |
| |
| private UpdateResponse emptyUpdate(SolrServer client, String... params) |
| throws SolrServerException, IOException { |
| |
| UpdateRequest req = new UpdateRequest(); |
| req.setParams(params(params)); |
| return req.process(client); |
| } |
| |
| /** |
| * Polls the SolrCore stats using the specified client until the "startTime" |
| * time for collection is after the specified "min". Will loop for |
| * at most "timeout" milliseconds before throwing an assertion failure. |
| * |
| * @param client The SolrServer to poll |
| * @param timeout the max milliseconds to continue polling for |
| * @param min the startTime value must exceed this value before the method will return, if null this method will return the first startTime value encountered. |
| * @return the startTime value of collection |
| */ |
| private Date watchCoreStartAt(SolrServer client, final long timeout, |
| final Date min) throws InterruptedException, IOException, SolrServerException { |
| final long sleepInterval = 200; |
| long timeSlept = 0; |
| |
| SolrParams p = params("action","status", "core", "collection1"); |
| while (timeSlept < timeout) { |
| QueryRequest req = new QueryRequest(p); |
| req.setPath("/admin/cores"); |
| try { |
| NamedList data = client.request(req); |
| for (String k : new String[] {"status","collection1"}) { |
| Object o = data.get(k); |
| assertNotNull("core status rsp missing key: " + k, o); |
| data = (NamedList) o; |
| } |
| Date startTime = (Date) data.get("startTime"); |
| assertNotNull("core has null startTime", startTime); |
| if (null == min || startTime.after(min)) { |
| return startTime; |
| } |
| } catch (SolrException e) { |
| // workarround for SOLR-4668 |
| if (500 != e.code()) { |
| throw e; |
| } // else server possibly from the core reload in progress... |
| } |
| |
| timeSlept += sleepInterval; |
| Thread.sleep(sleepInterval); |
| } |
| fail("timed out waiting for collection1 startAt time to exceed: " + min); |
| return min; // compilation neccessity |
| } |
| |
| private static class SolrInstance { |
| |
| private String name; |
| private Integer testPort; |
| private File homeDir; |
| private File confDir; |
| private File dataDir; |
| |
| /** |
| * @param name used to pick new solr home dir, as well as which |
| * "solrconfig-${name}.xml" file gets copied |
| * to solrconfig.xml in new conf dir. |
| * @param testPort if not null, used as a replacement for |
| * TEST_PORT in the cloned config files. |
| */ |
| public SolrInstance(String name, Integer testPort) { |
| this.name = name; |
| this.testPort = testPort; |
| } |
| |
| public String getHomeDir() { |
| return homeDir.toString(); |
| } |
| |
| public String getSchemaFile() { |
| return CONF_DIR + "schema-replication1.xml"; |
| } |
| |
| public String getConfDir() { |
| return confDir.toString(); |
| } |
| |
| public String getDataDir() { |
| return dataDir.toString(); |
| } |
| |
| public String getSolrConfigFile() { |
| return CONF_DIR + "solrconfig-"+name+".xml"; |
| } |
| |
| /** If it needs to change */ |
| public void setTestPort(Integer testPort) { |
| this.testPort = testPort; |
| } |
| |
| public void setUp() throws Exception { |
| System.setProperty("solr.test.sys.prop1", "propone"); |
| System.setProperty("solr.test.sys.prop2", "proptwo"); |
| |
| File home = new File(TEMP_DIR, |
| getClass().getName() + "-" + |
| System.currentTimeMillis()); |
| |
| |
| homeDir = new File(home, name); |
| dataDir = new File(homeDir + "/collection1", "data"); |
| confDir = new File(homeDir + "/collection1", "conf"); |
| |
| homeDir.mkdirs(); |
| dataDir.mkdirs(); |
| confDir.mkdirs(); |
| |
| copyConfigFile(getSolrConfigFile(), "solrconfig.xml"); |
| copyConfigFile(getSchemaFile(), "schema.xml"); |
| } |
| |
| public void tearDown() throws Exception { |
| AbstractSolrTestCase.recurseDelete(homeDir.getParentFile()); |
| } |
| |
| public void copyConfigFile(String srcFile, String destFile) |
| throws IOException { |
| copyFile(getFile(srcFile), |
| new File(confDir, destFile), |
| testPort, random().nextBoolean()); |
| } |
| |
| } |
| } |