SOLR-647: refcount cores

git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@686780 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yonik Seeley 2008-08-18 15:08:28 +00:00
parent 0ab15a39be
commit de8c642def
12 changed files with 464 additions and 241 deletions

View File

@ -359,6 +359,9 @@ Changes in runtime behavior
different syntax. The solrj classes: MultiCore{Request/Response/Params} have been
renamed: CoreAdmin{Request/Response/Params} (hossman, ryan, Henri Biestro)
3. SOLR-647: reference count the SolrCore uses to prevent a premature
close while a core is still in use. (Henri Biestro, Noble Paul, yonik)
Optimizations
1. SOLR-276: improve JSON writer speed. (yonik)

View File

@ -36,6 +36,7 @@ import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.SolrCore;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.request.BinaryResponseWriter;
import org.apache.solr.request.QueryResponseWriter;
import org.apache.solr.request.SolrQueryRequest;
@ -54,38 +55,46 @@ import org.apache.solr.servlet.SolrRequestParsers;
*/
public class EmbeddedSolrServer extends SolrServer
{
protected final CoreContainer multicore; // either cores
protected final SolrCore core; // or single core
protected final String coreName; // use CoreContainer registry
protected final CoreContainer coreContainer;
protected final String coreName;
private final SolrRequestParsers _parser;
/**
* Use the other constructor using a CoreContainer and a name.
* @param core
* @deprecated
*/
@Deprecated
public EmbeddedSolrServer( SolrCore core )
{
if ( core == null ) {
throw new NullPointerException("SolrCore instance required");
}
this.core = core;
if (core.getCoreDescriptor() != null) {
this.multicore = core.getCoreDescriptor().getMultiCore();
this.coreName = core.getCoreDescriptor().getName();
} else {
this.multicore = null;
this.coreName = null;
}
CoreDescriptor dcore = core.getCoreDescriptor();
if (dcore == null)
throw new NullPointerException("CoreDescriptor required");
CoreContainer cores = dcore.getCoreContainer();
if (cores == null)
throw new NullPointerException("CoreContainer required");
coreName = dcore.getName();
coreContainer = cores;
_parser = new SolrRequestParsers( null );
}
public EmbeddedSolrServer( CoreContainer multicore, String coreName )
/**
* Creates a SolrServer.
* @param coreContainer the core container
* @param coreName the core name
*/
public EmbeddedSolrServer( CoreContainer coreContainer, String coreName )
{
if ( multicore == null ) {
if ( coreContainer == null ) {
throw new NullPointerException("CoreContainer instance required");
}
this.core = null;
this.multicore = multicore;
this.coreContainer = coreContainer;
this.coreName = coreName == null? "" : coreName;
_parser = new SolrRequestParsers( null );
}
@ -98,15 +107,10 @@ public class EmbeddedSolrServer extends SolrServer
}
// Check for cores action
SolrCore core = this.core;
if( core == null )
core = multicore.getCore( coreName );
// solr-647
//else
// core = core.open();
SolrCore core = coreContainer.getCore( coreName );
if( core == null ) {
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
coreName == null? "No core": "No such core: " + coreName );
"No such core: " + coreName );
}
SolrParams params = request.getParams();
@ -126,14 +130,13 @@ public class EmbeddedSolrServer extends SolrServer
}
// Perhaps the path is to manage the cores
if( handler == null &&
multicore != null &&
path.equals( multicore.getAdminPath() ) ) {
handler = multicore.getMultiCoreHandler();
coreContainer != null &&
path.equals( coreContainer.getAdminPath() ) ) {
handler = coreContainer.getMultiCoreHandler();
}
}
if( handler == null ) {
// solr-647
// core.close();
core.close();
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "unknown handler: "+path );
}
@ -158,8 +161,7 @@ public class EmbeddedSolrServer extends SolrServer
throw new SolrServerException( ex );
}
finally {
// solr-647
// core.close();
core.close();
}
}

View File

@ -45,6 +45,6 @@ public class LargeVolumeEmbeddedTest extends LargeVolumeTestBase {
@Override
protected SolrServer createNewSolrServer()
{
return new EmbeddedSolrServer( h.getCore() );
return new EmbeddedSolrServer( h.getCoreContainer(), "" );
}
}

View File

@ -47,6 +47,6 @@ public class SolrExampleEmbeddedTest extends SolrExampleTests {
@Override
protected SolrServer createNewSolrServer()
{
return new EmbeddedSolrServer( h.getCore() );
return new EmbeddedSolrServer( h.getCoreContainer(), "" );
}
}

View File

@ -25,9 +25,7 @@ import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.channels.FileChannel;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.*;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
@ -36,6 +34,7 @@ import javax.xml.xpath.XPathConstants;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.DOMUtil;
import org.apache.solr.common.util.XML;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.schema.IndexSchema;
import org.w3c.dom.Node;
@ -51,7 +50,7 @@ public class CoreContainer
{
protected static Logger log = Logger.getLogger(CoreContainer.class.getName());
protected final Map<String, CoreDescriptor> cores = new LinkedHashMap<String, CoreDescriptor>();
protected final Map<String, SolrCore> cores = new LinkedHashMap<String, SolrCore>();
protected boolean persistent = false;
protected String adminPath = null;
protected String managementPath = null;
@ -69,7 +68,6 @@ public class CoreContainer
public static class Initializer {
protected String solrConfigFilename = null;
protected boolean abortOnConfigurationError = true;
protected String managementPath = null;
public boolean isAbortOnConfigurationError() {
return abortOnConfigurationError;
@ -87,14 +85,6 @@ public class CoreContainer
this.solrConfigFilename = solrConfigFilename;
}
public String getManagementPath() {
return managementPath;
}
public void setManagementPath(String managementPath) {
this.managementPath = managementPath;
}
// core container instantiation
public CoreContainer initialize() throws IOException, ParserConfigurationException, SAXException {
CoreContainer cores = null;
@ -116,16 +106,13 @@ public class CoreContainer
solrConfigFilename = cores.getConfigFile().getName();
} else {
// perform compatibility init
cores = new CoreContainer();
cores.loader = new SolrResourceLoader(instanceDir);
cores = new CoreContainer(new SolrResourceLoader(instanceDir));
SolrConfig cfg = solrConfigFilename == null ? new SolrConfig() : new SolrConfig(solrConfigFilename);
CoreDescriptor dcore = new CoreDescriptor(cores);
dcore.init("", cfg.getResourceLoader().getInstanceDir());
CoreDescriptor dcore = new CoreDescriptor(cores, "", cfg.getResourceLoader().getInstanceDir());
SolrCore singlecore = new SolrCore(null, null, cfg, null, dcore);
dcore.setCore(singlecore);
abortOnConfigurationError = cfg.getBool(
"abortOnConfigurationError", abortOnConfigurationError);
cores.register(dcore);
cores.register("", singlecore, false);
cores.setPersistent(false);
solrConfigFilename = cfg.getName();
}
@ -147,6 +134,14 @@ public class CoreContainer
this.load(dir, configFile);
}
/**
* Minimal CoreContainer constructor.
* @param loader the CoreContainer resource loader
*/
public CoreContainer(SolrResourceLoader loader) {
this.loader = loader;
}
//-------------------------------------------------------------------
// Initialization / Cleanup
//-------------------------------------------------------------------
@ -184,12 +179,15 @@ public class CoreContainer
}
NodeList nodes = (NodeList)cfg.evaluate("solr/cores/core", XPathConstants.NODESET);
synchronized (cores) {
for (int i=0; i<nodes.getLength(); i++) {
Node node = nodes.item(i);
try {
CoreDescriptor p = new CoreDescriptor(this);
p.init(DOMUtil.getAttr(node, "name", null), DOMUtil.getAttr(node, "instanceDir", null));
String names = DOMUtil.getAttr(node, "name", null);
List<String> aliases = StrUtils.splitSmart(names,',');
String name = aliases.get(0);
CoreDescriptor p = new CoreDescriptor(this, name, DOMUtil.getAttr(node, "instanceDir", null));
// deal with optional settings
String opt = DOMUtil.getAttr(node, "config", null);
if (opt != null) {
@ -199,12 +197,15 @@ public class CoreContainer
if (opt != null) {
p.setSchemaName(opt);
}
CoreDescriptor old = cores.get(p.getName());
if (old != null && old.getName() != null && old.getName().equals(p.getName())) {
throw new RuntimeException( cfg.getName() +
" registers multiple cores to the same name: " + p.name);
SolrCore core = create(p);
for (int a=1; a<aliases.size(); a++) {
core.open();
register(aliases.get(i), core, false);
}
p.setCore(create(p));
register(name, core, false);
}
catch (Throwable ex) {
SolrConfig.severeErrors.add( ex );
@ -212,7 +213,7 @@ public class CoreContainer
}
}
}
}
finally {
if (cfgis != null) {
try { cfgis.close(); } catch (Exception xany) {}
@ -225,12 +226,9 @@ public class CoreContainer
*/
public void shutdown() {
synchronized(cores) {
for(CoreDescriptor descriptor : cores.values()) {
SolrCore core = descriptor.getCore();
if( core != null ) {
for(SolrCore core : cores.values()) {
core.close();
}
}
cores.clear();
}
}
@ -240,39 +238,43 @@ public class CoreContainer
shutdown();
}
// ---------------- CoreDescriptor related methods ---------------
/**
* Registers a SolrCore descriptor in the registry.
* @param descr the Solr core descriptor
* @return a previous descriptor having the same name if it existed, null otherwise
* @return a previous core having the same name if it existed and returnPrev==true
*/
public CoreDescriptor register( CoreDescriptor descr ) {
if( descr == null ) {
public SolrCore register(String name, SolrCore core, boolean returnPrev) {
if( core == null ) {
throw new RuntimeException( "Can not register a null core." );
}
String name = descr.getName();
if( name == null ||
name.indexOf( '/' ) >= 0 ||
name.indexOf( '\\' ) >= 0 ){
throw new RuntimeException( "Invalid core name: "+name );
}
CoreDescriptor old = null;
SolrCore old = null;
synchronized (cores) {
old = cores.put(name, descr);
old = cores.put(name, core);
core.setName(name);
}
if( old == null ) {
if( old == null || old == core) {
log.info( "registering core: "+name );
return null;
}
else {
log.info( "replacing core: "+name );
if (!returnPrev) {
old.close();
}
return old;
}
}
/**
* Creates a new core based on a descriptor.
* Creates a new core based on a descriptor but does not register it.
*
* @param dcore a core descriptor
* @return the newly created core
@ -293,10 +295,6 @@ public class CoreContainer
SolrConfig config = new SolrConfig(solrLoader, dcore.getConfigName(), null);
IndexSchema schema = new IndexSchema(config, dcore.getSchemaName(), null);
SolrCore core = new SolrCore(dcore.getName(), null, config, schema, dcore);
dcore.setCore(core);
// Register the new core
CoreDescriptor old = this.register(dcore);
return core;
}
@ -304,36 +302,38 @@ public class CoreContainer
* @return a Collection of registered SolrCores
*/
public Collection<SolrCore> getCores() {
java.util.List<SolrCore> l = new java.util.ArrayList<SolrCore>();
List<SolrCore> lst = new ArrayList<SolrCore>();
synchronized (cores) {
for(CoreDescriptor descr : this.cores.values()) {
if (descr.getCore() != null)
l.add(descr.getCore());
lst.addAll(this.cores.values());
}
}
return l;
return lst;
}
/**
* @return a Collection of registered CoreDescriptors
* @return a Collection of the names that cores are mapped to
*/
public Collection<CoreDescriptor> getDescriptors() {
java.util.List<CoreDescriptor> l = new java.util.ArrayList<CoreDescriptor>();
public Collection<String> getCoreNames() {
List<String> lst = new ArrayList<String>();
synchronized (cores) {
l.addAll(cores.values());
lst.addAll(this.cores.keySet());
}
return l;
return lst;
}
/**
* @return the CoreDescriptor registered under that name
* @return a Collection of the names that a specific core is mapped to.
*/
public CoreDescriptor getDescriptor(String name) {
public Collection<String> getCoreNames(SolrCore core) {
List<String> lst = new ArrayList<String>();
synchronized (cores) {
return cores.get( name );
for (Map.Entry<String,SolrCore> entry : cores.entrySet()) {
if (core == entry.getValue()) {
lst.add(entry.getKey());
}
}
}
return lst;
}
// ---------------- Core name related methods ---------------
/**
@ -348,12 +348,14 @@ public class CoreContainer
*/
public void reload(String name) throws ParserConfigurationException, IOException, SAXException {
SolrCore core;
synchronized(cores) {
CoreDescriptor dcore = cores.get(name);
if (dcore == null)
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No such core: " + name );
create(new CoreDescriptor(dcore));
core = cores.get(name);
}
if (core == null)
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No such core: " + name );
register(name, create(core.getCoreDescriptor()), false);
}
@ -367,36 +369,27 @@ public class CoreContainer
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Can not swap unnamed cores." );
}
synchronized( cores ) {
CoreDescriptor c0 = cores.get(n0);
SolrCore c0 = cores.get(n0);
SolrCore c1 = cores.get(n1);
if (c0 == null)
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No such core: " + n0 );
CoreDescriptor c1 = cores.get(n1);
if (c1 == null)
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No such core: " + n1 );
cores.put(n0, c1);
cores.put(n1, c0);
c0.setName(n1);
if (c0.getCore() != null)
c0.getCore().setName(n1);
c1.setName(n0);
if (c1.getCore() != null)
c1.getCore().setName(n0);
log.info( "swaped: "+c0.getName() + " with " + c1.getName() );
}
}
/** Removes & closes a registered core. */
public void remove( String name ) {
log.info("swaped: "+n0 + " with " + n1);
}
/** Removes and returns registered core w/o decrementing it's reference count */
public SolrCore remove( String name ) {
synchronized(cores) {
CoreDescriptor dcore = cores.remove( name );
if (dcore == null) {
return;
}
SolrCore core = dcore.getCore();
if (core != null) {
core.close();
}
return cores.remove( name );
}
}
@ -408,15 +401,10 @@ public class CoreContainer
*/
public SolrCore getCore(String name) {
synchronized(cores) {
CoreDescriptor dcore = cores.get(name);
SolrCore core = null;
if (dcore != null)
core = dcore.getCore();
SolrCore core = cores.get(name);
if (core != null)
core.open(); // increment the ref count while still synchronized
return core;
// solr-647
// if (core != null)
// return core.open();
// return null;
}
}
@ -437,16 +425,13 @@ public class CoreContainer
public SolrCore getAdminCore() {
synchronized (cores) {
SolrCore core = adminCore != null ? adminCore.get() : null;
// solr-647
// if (core != null)
// core = core.open();
if (core == null) {
for (CoreDescriptor descr : this.cores.values()) {
core = descr.getCore();
// solr-647
// if (core != null)
// core = core.open();
if (core != null) {
core.open();
} else {
for (SolrCore c : cores.values()) {
if (c != null) {
core = c;
core.open();
break;
}
}
@ -527,7 +512,6 @@ public class CoreContainer
file = tmpFile = File.createTempFile("solr", ".xml", configFile.getParentFile());
}
java.io.FileOutputStream out = new java.io.FileOutputStream(file);
synchronized(cores) {
Writer writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
persist(writer);
writer.flush();
@ -541,7 +525,6 @@ public class CoreContainer
fileCopy(tmpFile, configFile);
}
}
}
catch(java.io.FileNotFoundException xnf) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, xnf);
}
@ -578,20 +561,35 @@ public class CoreContainer
writer.write('\'');
writer.write(">\n");
Map<SolrCore, LinkedList<String>> aliases = new HashMap<SolrCore,LinkedList<String>>();
synchronized(cores) {
for (Map.Entry<String, CoreDescriptor> entry : cores.entrySet()) {
persist(writer, entry.getValue());
for (Map.Entry<String, SolrCore> entry : cores.entrySet()) {
String name = entry.getKey();
LinkedList<String> a = aliases.get(entry.getValue());
if (a==null) a = new LinkedList<String>();
if (name.equals(entry.getValue().getName())) {
a.addFirst(name);
} else {
a.addLast(name);
}
aliases.put(entry.getValue(), a);
}
}
for (Map.Entry<SolrCore, LinkedList<String>> entry : aliases.entrySet()) {
persist(writer, entry.getValue(), entry.getKey().getCoreDescriptor());
}
writer.write("</cores>\n");
writer.write("</solr>\n");
}
/** Writes the cores configuration node for a given core. */
void persist(Writer writer, CoreDescriptor dcore) throws IOException {
void persist(Writer writer, List<String> aliases, CoreDescriptor dcore) throws IOException {
writer.write(" <core");
writer.write (" name='");
XML.escapeAttributeValue(dcore.getName(), writer);
XML.escapeAttributeValue(StrUtils.join(aliases,','), writer);
writer.write("' instanceDir='");
XML.escapeAttributeValue(dcore.getInstanceDir(), writer);
writer.write('\'');

View File

@ -28,22 +28,17 @@ public class CoreDescriptor implements Cloneable {
protected String instanceDir;
protected String configName;
protected String schemaName;
protected SolrCore core = null;
private final CoreContainer coreContainer;
public CoreDescriptor(CoreContainer coreContainer) {
public CoreDescriptor(CoreContainer coreContainer, String name, String instanceDir) {
this.coreContainer = coreContainer;
}
/** Initialize defaults from instance directory. */
public void init(String name, String instanceDir) {
this.name = name;
if (name == null) {
throw new RuntimeException("Core needs a name");
}
if (instanceDir == null) {
throw new NullPointerException("Missing required \'instanceDir\'");
}
this.name = name;
if (!instanceDir.endsWith("/")) instanceDir = instanceDir + "/";
this.instanceDir = instanceDir;
this.configName = getDefaultConfigName();
@ -51,10 +46,10 @@ public class CoreDescriptor implements Cloneable {
}
public CoreDescriptor(CoreDescriptor descr) {
this.name = descr.name;
this.instanceDir = descr.instanceDir;
this.configName = descr.configName;
this.schemaName = descr.schemaName;
this.name = descr.name;
coreContainer = descr.coreContainer;
}
@ -73,16 +68,6 @@ public class CoreDescriptor implements Cloneable {
return this.instanceDir + "data/";
}
/**@return the core name. */
public String getName() {
return name;
}
/** Sets the core name. */
public void setName(String name) {
this.name = name;
}
/**@return the core instance directory. */
public String getInstanceDir() {
return instanceDir;
@ -112,15 +97,12 @@ public class CoreDescriptor implements Cloneable {
return this.schemaName;
}
public SolrCore getCore() {
return core;
/**@return the initial core name */
public String getName() {
return this.name;
}
public void setCore(SolrCore core) {
this.core = core;
}
public CoreContainer getMultiCore() {
public CoreContainer getCoreContainer() {
return coreContainer;
}
}

View File

@ -62,13 +62,15 @@ import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import java.net.URL;
/**
* @version $Id$
*/
public final class SolrCore {
public final class SolrCore implements SolrInfoMBean {
public static final String version="1.0";
public static Logger log = Logger.getLogger(SolrCore.class.getName());
@ -479,8 +481,9 @@ public final class SolrCore {
latch.countDown();
}
} // end synchronized
}
infoRegistry.put("core", this);
}
/**
@ -570,15 +573,38 @@ public final class SolrCore {
return chain;
}
// this core current usage count
private final AtomicInteger refCount = new AtomicInteger(1);
final void open() {
refCount.incrementAndGet();
}
/**
* Close all resources allocated by the core.
* 1. searcher
* 2. updateHandler
* 3. all CloseHooks will be notified
* 4. All MBeans will be unregistered from MBeanServer if JMX was enabled
* <p>
* This should always be called when the core is obtained through:
* @see CoreContainer.getCore
* @see CoreContainer.getAdminCore
* </p>
* The actual close is performed if the core usage count is 1.
* (A core is created with a usage count of 1).
* If usage count is > 1, the usage count is decreased by 1.
* If usage count is &lt; 0, this is an error and a runtime exception is thrown.
*/
public void close() {
log.info(logid+" CLOSING SolrCore!");
int count = refCount.decrementAndGet();
if (count > 0) return;
if (count < 0) {
//throw new RuntimeException("Too many closes on " + this);
log.severe("Too many close {count:"+count+"} on " + this + ". Please report this exception to solr-user@lucene.apache.org");
return;
}
log.info(logid+" CLOSING SolrCore " + this);
try {
infoRegistry.clear();
} catch (Exception e) {
@ -606,12 +632,18 @@ public final class SolrCore {
}
}
public boolean isClosed() {
return _searcher == null;
/** Current core usage count. */
public int getOpenCount() {
return refCount.get();
}
@Override
protected void finalize() { close(); }
/** Whether this core is closed. */
public boolean isClosed() {
return refCount.get() <= 0;
}
// this can cause an extra close
// protected void finalize() { close(); }
private List<CloseHook> closeHooks = null;
@ -1380,6 +1412,45 @@ public final class SolrCore {
public CoreDescriptor getCoreDescriptor() {
return coreDescriptor;
}
/////////////////////////////////////////////////////////////////////
// SolrInfoMBean stuff: Statistics and Module Info
/////////////////////////////////////////////////////////////////////
public String getVersion() {
return SolrCore.version;
}
public String getDescription() {
return "SolrCore";
}
public Category getCategory() {
return Category.CORE;
}
public String getSourceId() {
return "$Id:$";
}
public String getSource() {
return "$URL:$";
}
public URL[] getDocs() {
return null;
}
public NamedList getStatistics() {
NamedList lst = new SimpleOrderedMap();
lst.add("coreName", name==null ? "(null)" : name);
lst.add("startTime", new Date(startTime));
lst.add("refCount", getOpenCount());
lst.add("aliases", getCoreDescriptor().getCoreContainer().getCoreNames(this));
return lst;
}
}

View File

@ -90,9 +90,8 @@ public abstract class CoreAdminHandler extends RequestHandlerBase
switch(action) {
case CREATE: {
CoreDescriptor dcore = new CoreDescriptor(cores);
dcore.init(params.get(CoreAdminParams.NAME),
params.get(CoreAdminParams.INSTANCE_DIR));
String name = params.get(CoreAdminParams.NAME);
CoreDescriptor dcore = new CoreDescriptor(cores, name, params.get(CoreAdminParams.INSTANCE_DIR));
// fillup optional parameters
String opts = params.get(CoreAdminParams.CONFIG);
@ -104,6 +103,7 @@ public abstract class CoreAdminHandler extends RequestHandlerBase
dcore.setSchemaName(opts);
SolrCore core = cores.create(dcore);
cores.register(name, core,false);
rsp.add("core", core.getName());
do_persist = cores.isPersistent();
break;
@ -112,9 +112,8 @@ public abstract class CoreAdminHandler extends RequestHandlerBase
case STATUS: {
NamedList<Object> status = new SimpleOrderedMap<Object>();
if( cname == null ) {
for (CoreDescriptor d : cores.getDescriptors()) {
cname = d.getName();
status.add(d.getName(), getCoreStatus( cores, cname ) );
for (SolrCore core : cores.getCores()) {
status.add(core.getName(), getCoreStatus( cores, cname ) );
}
}
else {
@ -172,8 +171,7 @@ public abstract class CoreAdminHandler extends RequestHandlerBase
info.add("index", LukeRequestHandler.getIndexInfo(searcher.get().getReader(), false));
searcher.decref();
} finally {
// solr-647
// core.close();
core.close();
}
}
return info;

View File

@ -21,6 +21,9 @@ import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.XML;
import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrCore;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.handler.XmlUpdateRequestHandler;
import org.apache.solr.request.LocalSolrQueryRequest;
import org.apache.solr.request.QueryResponseWriter;
@ -32,6 +35,7 @@ import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
@ -60,7 +64,7 @@ import java.util.Map;
* @version $Id:$
*/
public class TestHarness {
protected CoreContainer container;
private SolrCore core;
private XPath xpath = XPathFactory.newInstance().newXPath();
private DocumentBuilder builder;
@ -123,8 +127,19 @@ public class TestHarness {
public TestHarness( String dataDirectory,
SolrConfig solrConfig,
IndexSchema indexSchema) {
this("", new Initializer("", dataDirectory, solrConfig, indexSchema));
}
public TestHarness(String coreName, CoreContainer.Initializer init) {
try {
core = new SolrCore( null, dataDirectory, solrConfig, indexSchema, null);
container = init.initialize();
if (coreName == null)
coreName = "";
// get the core & decrease its refcount:
// the container holds the core for the harness lifetime
core = container.getCore(coreName);
if (core != null)
core.close();
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
updater = new XmlUpdateRequestHandler();
@ -134,6 +149,42 @@ public class TestHarness {
}
}
// Creates a container based on infos needed to create one core
static class Initializer extends CoreContainer.Initializer {
String coreName;
String dataDirectory;
SolrConfig solrConfig;
IndexSchema indexSchema;
public Initializer(String coreName,
String dataDirectory,
SolrConfig solrConfig,
IndexSchema indexSchema) {
if (coreName == null)
coreName = "";
this.coreName = coreName;
this.dataDirectory = dataDirectory;
this.solrConfig = solrConfig;
this.indexSchema = indexSchema;
}
public String getCoreName() {
return coreName;
}
@Override
public CoreContainer initialize() {
CoreContainer container = new CoreContainer(new SolrResourceLoader(SolrResourceLoader.locateInstanceDir()));
CoreDescriptor dcore = new CoreDescriptor(container, coreName, solrConfig.getResourceLoader().getInstanceDir());
dcore.setConfigName(solrConfig.getResourceName());
dcore.setSchemaName(indexSchema.getResourceName());
SolrCore core = new SolrCore( null, dataDirectory, solrConfig, indexSchema, dcore);
container.register(coreName, core, false);
return container;
}
}
public CoreContainer getCoreContainer() {
return container;
}
public SolrCore getCore() {
return core;
}
@ -153,8 +204,6 @@ public class TestHarness {
StringReader req = new StringReader(xml);
StringWriter writer = new StringWriter(32000);
// This relies on the fact that SolrCore.getSolrCore() uses the
// last instantiated SolrCore.
updater.doLegacyUpdate(req, writer);
return writer.toString();
}
@ -321,7 +370,17 @@ public class TestHarness {
* Shuts down and frees any resources
*/
public void close() {
core.close();
if (container != null) {
for (SolrCore c : container.getCores()) {
if (c.getOpenCount() > 1)
throw new RuntimeException("SolrCore.getOpenCount()=="+core.getOpenCount());
}
}
if (container != null) {
container.shutdown();
container = null;
}
}
/**

View File

@ -24,13 +24,14 @@ import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.util.AbstractSolrTestCase;
import org.apache.solr.util.plugin.SolrCoreAware;
import java.util.concurrent.*;
import java.util.*;
public class SolrCoreTest extends AbstractSolrTestCase {
public String getSchemaFile() { return "schema.xml"; }
public String getSolrConfigFile() { return "solrconfig.xml"; }
public void testRequestHandlerRegistry() {
// property values defined in build.xml
SolrCore core = h.getCore();
EmptyRequestHandler handler1 = new EmptyRequestHandler();
@ -46,7 +47,8 @@ public class SolrCoreTest extends AbstractSolrTestCase {
}
public void testClose() throws Exception {
SolrCore core = h.getCore();
final CoreContainer cores = h.getCoreContainer();
SolrCore core = cores.getCore("");
ClosingRequestHandler handler1 = new ClosingRequestHandler();
handler1.inform( core );
@ -56,10 +58,116 @@ public class SolrCoreTest extends AbstractSolrTestCase {
assertNull( old ); // should not be anything...
assertEquals( core.getRequestHandlers().get( path ), handler1 );
core.close();
cores.shutdown();
assertTrue("Handler not closed", handler1.closed == true);
}
public void testRefCount() throws Exception {
SolrCore core = h.getCore();
assertTrue("Refcount != 1", core.getOpenCount() == 1);
final CoreContainer cores = h.getCoreContainer();
SolrCore c1 = cores.getCore("");
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("");
c1.close();
assertTrue("Refcount < 1", core.getOpenCount() >= 1);
assertTrue("Handler is closed", handler1.closed == false);
c1 = cores.getCore("");
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);
}
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 = Executors.newFixedThreadPool(MT);
List<Callable<Integer>> callees = new ArrayList<Callable<Integer>>(MT);
final CoreContainer cores = h.getCoreContainer();
for (int i = 0; i < MT; ++i) {
Callable<Integer> call = new Callable<Integer>() {
void yield(int n) {
try {
Thread.sleep(0, (n % 13 + 1) * 10);
} catch (InterruptedException xint) {
}
}
public Integer call() {
SolrCore core = null;
int r = 0;
try {
for (int l = 0; l < LOOP; ++l) {
r += 1;
core = cores.getCore("");
// sprinkle concurrency hinting...
yield(l);
assertTrue("Refcount < 1", core.getOpenCount() >= 1);
yield(l);
assertTrue("Refcount > 17", core.getOpenCount() <= 17);
yield(l);
assertTrue("Handler is closed", handler1.closed == false);
yield(l);
core.close();
core = null;
yield(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));
}
}
class ClosingRequestHandler extends EmptyRequestHandler implements SolrCoreAware {
boolean closed = false;

View File

@ -285,6 +285,9 @@ public class SolrDispatchFilter implements Filter
if( solrReq != null ) {
solrReq.close();
}
if (core != null) {
core.close();
}
}
}

View File

@ -21,6 +21,8 @@
<%-- $Name: $ --%>
<%@ page import="java.util.Date" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.Collection" %>
<%-- jsp:include page="header.jsp"/ --%>
<%-- do a verbatim include so we can use the local vars --%>
@ -52,15 +54,12 @@
</tr>
<%-- List the cores (that arent this one) so we can switch --%>
<% org.apache.solr.core.CoreContainer multicore = (org.apache.solr.core.CoreContainer)request.getAttribute("org.apache.solr.CoreContainer");
if (multicore!=null) {
java.util.Collection<SolrCore> cores = multicore.getCores();
if (cores.size() > 1) {%><tr><td><strong>Cores:</strong><br></td><td><%
java.util.Iterator<SolrCore> icore = cores.iterator();
while (icore.hasNext()) {
SolrCore acore = icore.next();
if (acore == core) continue;
%>[<a href="../../<%=acore.getName()%>/admin/"><%=acore.getName()%></a>]<%
<% org.apache.solr.core.CoreContainer cores = (org.apache.solr.core.CoreContainer)request.getAttribute("org.apache.solr.CoreContainer");
if (cores!=null) {
Collection<String> names = cores.getCoreNames();
if (names.size() > 1) {%><tr><td><strong>Cores:</strong><br></td><td><%
for (String name : names) {
%>[<a href="../../<%=name%>/admin/"><%=name%></a>]<%
}%></td></tr><%
}}%>