blob: b42a4aa8e13bea580a0a3a8d7372095815380dfb [file] [log] [blame]
/*
* 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.core;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.ExecutorUtil;
import org.apache.solr.handler.ReplicationHandler;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.handler.component.QueryComponent;
import org.apache.solr.handler.component.SpellCheckComponent;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.update.SolrCoreState;
import org.apache.solr.common.util.SolrNamedThreadFactory;
import org.apache.solr.util.RefCounted;
import org.apache.solr.util.plugin.SolrCoreAware;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class SolrCoreTest extends SolrTestCaseJ4 {
private static final String COLLECTION1 = "collection1";
@Override
public void setUp() throws Exception {
super.setUp();
initCore("solrconfig.xml", "schema.xml");
}
@Override
public void tearDown() throws Exception {
deleteCore();
super.tearDown();
}
@Test
public void testRequestHandlerRegistry() {
SolrCore core = h.getCore();
EmptyRequestHandler handler1 = new EmptyRequestHandler();
EmptyRequestHandler handler2 = new EmptyRequestHandler();
String path = "/this/is A path /that won't be registered!";
SolrRequestHandler old = core.registerRequestHandler( path, handler1 );
assertNull( old ); // should not be anything...
assertEquals( core.getRequestHandlers().get( path ), handler1 );
old = core.registerRequestHandler( path, handler2 );
assertEquals( old, handler1 ); // should pop out the old one
assertEquals( core.getRequestHandlers().get( path ), handler2 );
}
@Test
public void testImplicitPlugins() {
final SolrCore core = h.getCore();
final List<PluginInfo> implicitHandlers = core.getImplicitHandlers();
final Map<String,String> pathToClassMap = new HashMap<>(implicitHandlers.size());
for (PluginInfo implicitHandler : implicitHandlers) {
assertEquals("wrong type for "+implicitHandler.toString(),
SolrRequestHandler.TYPE, implicitHandler.type);
pathToClassMap.put(implicitHandler.name, implicitHandler.className);
}
int ihCount = 0;
{
++ihCount; assertEquals(pathToClassMap.get("/admin/file"), "solr.ShowFileRequestHandler");
++ihCount; assertEquals(pathToClassMap.get("/admin/logging"), "solr.LoggingHandler");
++ihCount; assertEquals(pathToClassMap.get("/admin/luke"), "solr.LukeRequestHandler");
++ihCount; assertEquals(pathToClassMap.get("/admin/mbeans"), "solr.SolrInfoMBeanHandler");
++ihCount; assertEquals(pathToClassMap.get("/admin/ping"), "solr.PingRequestHandler");
++ihCount; assertEquals(pathToClassMap.get("/admin/plugins"), "solr.PluginInfoHandler");
++ihCount; assertEquals(pathToClassMap.get("/admin/properties"), "solr.PropertiesRequestHandler");
++ihCount; assertEquals(pathToClassMap.get("/admin/segments"), "solr.SegmentsInfoRequestHandler");
++ihCount; assertEquals(pathToClassMap.get("/admin/system"), "solr.SystemInfoHandler");
++ihCount; assertEquals(pathToClassMap.get("/admin/threads"), "solr.ThreadDumpHandler");
++ihCount; assertEquals(pathToClassMap.get("/config"), "solr.SolrConfigHandler");
++ihCount; assertEquals(pathToClassMap.get("/export"), "solr.ExportHandler");
++ihCount; assertEquals(pathToClassMap.get("/terms"), "solr.SearchHandler");
++ihCount; assertEquals(pathToClassMap.get("/get"), "solr.RealTimeGetHandler");
++ihCount; assertEquals(pathToClassMap.get(ReplicationHandler.PATH), "solr.ReplicationHandler");
++ihCount; assertEquals(pathToClassMap.get("/schema"), "solr.SchemaHandler");
++ihCount; assertEquals(pathToClassMap.get("/sql"), "solr.SQLHandler");
++ihCount; assertEquals(pathToClassMap.get("/stream"), "solr.StreamHandler");
++ihCount; assertEquals(pathToClassMap.get("/graph"), "solr.GraphHandler");
++ihCount; assertEquals(pathToClassMap.get("/update"), "solr.UpdateRequestHandler");
++ihCount; assertEquals(pathToClassMap.get("/update/csv"), "solr.UpdateRequestHandler");
++ihCount; assertEquals(pathToClassMap.get("/update/json"), "solr.UpdateRequestHandler");
++ihCount; assertEquals(pathToClassMap.get("/update/json/docs"), "solr.UpdateRequestHandler");
++ihCount; assertEquals(pathToClassMap.get("/analysis/document"), "solr.DocumentAnalysisRequestHandler");
++ihCount; assertEquals(pathToClassMap.get("/analysis/field"), "solr.FieldAnalysisRequestHandler");
++ihCount; assertEquals(pathToClassMap.get("/debug/dump"), "solr.DumpRequestHandler");
++ihCount; assertEquals(pathToClassMap.get("update"), "solr.UpdateRequestHandlerApi");
}
assertEquals("wrong number of implicit handlers", ihCount, implicitHandlers.size());
}
@Test
public void testClose() throws Exception {
final CoreContainer cores = h.getCoreContainer();
SolrCore core = cores.getCore(SolrTestCaseJ4.DEFAULT_TEST_CORENAME);
ClosingRequestHandler handler1 = new ClosingRequestHandler();
handler1.inform( core );
String path = "/this/is A path /that won't be registered 2!!!!!!!!!!!";
SolrRequestHandler old = core.registerRequestHandler( path, handler1 );
assertNull( old ); // should not be anything...
assertEquals( core.getRequestHandlers().get( path ), handler1 );
core.close();
cores.shutdown();
assertTrue("Handler not closed", handler1.closed == true);
}
@Test
public void testRefCount() throws Exception {
SolrCore core = h.getCore();
assertTrue("Refcount != 1", core.getOpenCount() == 1);
final CoreContainer cores = h.getCoreContainer();
SolrCore c1 = cores.getCore(SolrTestCaseJ4.DEFAULT_TEST_CORENAME);
assertTrue("Refcount != 2", core.getOpenCount() == 2);
ClosingRequestHandler handler1 = new ClosingRequestHandler();
handler1.inform( core );
String path = "/this/is A path /that won't be registered!";
SolrRequestHandler old = core.registerRequestHandler( path, handler1 );
assertNull( old ); // should not be anything...
assertEquals( core.getRequestHandlers().get( path ), handler1 );
SolrCore c2 = cores.getCore(SolrTestCaseJ4.DEFAULT_TEST_CORENAME);
c1.close();
assertTrue("Refcount < 1", core.getOpenCount() >= 1);
assertTrue("Handler is closed", handler1.closed == false);
c1 = cores.getCore(SolrTestCaseJ4.DEFAULT_TEST_CORENAME);
assertTrue("Refcount < 2", core.getOpenCount() >= 2);
assertTrue("Handler is closed", handler1.closed == false);
c2.close();
assertTrue("Refcount < 1", core.getOpenCount() >= 1);
assertTrue("Handler is closed", handler1.closed == false);
c1.close();
cores.shutdown();
assertTrue("Refcount != 0", core.getOpenCount() == 0);
assertTrue("Handler not closed", core.isClosed() && handler1.closed == true);
}
@Test
public void testRefCountMT() throws Exception {
SolrCore core = h.getCore();
assertTrue("Refcount != 1", core.getOpenCount() == 1);
final ClosingRequestHandler handler1 = new ClosingRequestHandler();
handler1.inform(core);
String path = "/this/is A path /that won't be registered!";
SolrRequestHandler old = core.registerRequestHandler(path, handler1);
assertNull(old); // should not be anything...
assertEquals(core.getRequestHandlers().get(path), handler1);
final int LOOP = 100;
final int MT = 16;
ExecutorService service = ExecutorUtil.newMDCAwareFixedThreadPool(MT, new SolrNamedThreadFactory("refCountMT"));
List<Callable<Integer>> callees = new ArrayList<>(MT);
final CoreContainer cores = h.getCoreContainer();
for (int i = 0; i < MT; ++i) {
Callable<Integer> call = new Callable<Integer>() {
void yieldInt(int n) {
try {
Thread.sleep(0, (n % 13 + 1) * 10);
} catch (InterruptedException xint) {
}
}
@Override
public Integer call() {
SolrCore core = null;
int r = 0;
try {
for (int l = 0; l < LOOP; ++l) {
r += 1;
core = cores.getCore(SolrTestCaseJ4.DEFAULT_TEST_CORENAME);
// sprinkle concurrency hinting...
yieldInt(l);
assertTrue("Refcount < 1", core.getOpenCount() >= 1);
yieldInt(l);
assertTrue("Refcount > 17", core.getOpenCount() <= 17);
yieldInt(l);
assertTrue("Handler is closed", handler1.closed == false);
yieldInt(l);
core.close();
core = null;
yieldInt(l);
}
return r;
} finally {
if (core != null)
core.close();
}
}
};
callees.add(call);
}
List<Future<Integer>> results = service.invokeAll(callees);
for (Future<Integer> result : results) {
assertTrue("loop=" + result.get() +" < " + LOOP, result.get() >= LOOP);
}
cores.shutdown();
assertTrue("Refcount != 0", core.getOpenCount() == 0);
assertTrue("Handler not closed", core.isClosed() && handler1.closed == true);
service.shutdown();
assertTrue("Running for too long...", service.awaitTermination(60, TimeUnit.SECONDS));
}
@Test
public void testInfoRegistry() throws Exception {
//TEst that SolrInfoMBeans are registered, including SearchComponents
SolrCore core = h.getCore();
Map<String, SolrInfoBean> infoRegistry = core.getInfoRegistry();
assertTrue("infoRegistry Size: " + infoRegistry.size() + " is not greater than: " + 0, infoRegistry.size() > 0);
//try out some that we know are in the config
SolrInfoBean bean = infoRegistry.get(SpellCheckComponent.COMPONENT_NAME);
assertNotNull("bean not registered", bean);
//try a default one
bean = infoRegistry.get(QueryComponent.COMPONENT_NAME);
assertNotNull("bean not registered", bean);
//try a Req Handler, which are stored by name, not clas
bean = infoRegistry.get("/select");
assertNotNull("bean not registered", bean);
}
@Test
public void testConfiguration() throws Exception {
assertEquals("wrong config for slowQueryThresholdMillis", 2000, solrConfig.slowQueryThresholdMillis);
assertEquals("wrong config for maxBooleanClauses", 1024, solrConfig.booleanQueryMaxClauseCount);
assertEquals("wrong config for enableLazyFieldLoading", true, solrConfig.enableLazyFieldLoading);
assertEquals("wrong config for queryResultWindowSize", 10, solrConfig.queryResultWindowSize);
}
/**
* Test that's meant to be run with many iterations to expose a leak of SolrIndexSearcher when a core is closed
* due to a reload. Without the fix, this test fails with most iters=1000 runs.
*/
@Test
public void testReloadLeak() throws Exception {
final ExecutorService executor =
ExecutorUtil.newMDCAwareFixedThreadPool(1, new SolrNamedThreadFactory("testReloadLeak"));
// Continuously open new searcher while core is not closed, and reload core to try to reproduce searcher leak.
// While in practice we never continuously open new searchers, this is trying to make up for the fact that opening
// a searcher in this empty core is very fast by opening new searchers continuously to increase the likelihood
// for race.
SolrCore core = h.getCore();
assertTrue("Refcount != 1", core.getOpenCount() == 1);
executor.execute(new NewSearcherRunnable(core));
// Since we called getCore() vs getCoreInc() and don't own a refCount, the container should decRef the core
// and close it when we call reload.
h.reload();
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
// Check that all cores are closed and no searcher references are leaked.
assertTrue("SolrCore " + core + " is not closed", core.isClosed());
assertTrue(core.areAllSearcherReferencesEmpty());
}
private static class NewSearcherRunnable implements Runnable {
private final SolrCore core;
NewSearcherRunnable(SolrCore core) {
this.core = core;
}
@Override
public void run() {
while (!core.isClosed()) {
try {
RefCounted<SolrIndexSearcher> newSearcher = null;
try {
newSearcher = core.openNewSearcher(true, true);
} catch (SolrCoreState.CoreIsClosedException e) {
// closed
} finally {
if (newSearcher != null) {
newSearcher.decref();
}
}
} catch (SolrException e) {
if (!core.isClosed()) {
throw e;
}
}
}
}
}
}
class ClosingRequestHandler extends EmptyRequestHandler implements SolrCoreAware {
boolean closed = false;
@Override
public void inform(SolrCore core) {
core.addCloseHook( new CloseHook() {
@Override
public void preClose(SolrCore core) {
closed = true;
}
@Override
public void postClose(SolrCore core) {}
});
}
}
/**
* An empty handler for testing
*/
class EmptyRequestHandler extends RequestHandlerBase
{
@Override
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
// nothing!
}
@Override public String getDescription() { return null; }
}