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

@ -358,6 +358,9 @@ Changes in runtime behavior
1.3 release. Speciffically, solr.xml has replaced multicore.xml, and uses a slightly 1.3 release. Speciffically, solr.xml has replaced multicore.xml, and uses a slightly
different syntax. The solrj classes: MultiCore{Request/Response/Params} have been different syntax. The solrj classes: MultiCore{Request/Response/Params} have been
renamed: CoreAdmin{Request/Response/Params} (hossman, ryan, Henri Biestro) 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 Optimizations
1. SOLR-276: improve JSON writer speed. (yonik) 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.common.util.NamedList;
import org.apache.solr.core.CoreContainer; import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.SolrCore; import org.apache.solr.core.SolrCore;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.request.BinaryResponseWriter; import org.apache.solr.request.BinaryResponseWriter;
import org.apache.solr.request.QueryResponseWriter; import org.apache.solr.request.QueryResponseWriter;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
@ -54,38 +55,46 @@ import org.apache.solr.servlet.SolrRequestParsers;
*/ */
public class EmbeddedSolrServer extends SolrServer public class EmbeddedSolrServer extends SolrServer
{ {
protected final CoreContainer coreContainer;
protected final CoreContainer multicore; // either cores protected final String coreName;
protected final SolrCore core; // or single core
protected final String coreName; // use CoreContainer registry
private final SolrRequestParsers _parser; private final SolrRequestParsers _parser;
/**
* Use the other constructor using a CoreContainer and a name.
* @param core
* @deprecated
*/
@Deprecated
public EmbeddedSolrServer( SolrCore core ) public EmbeddedSolrServer( SolrCore core )
{ {
if ( core == null ) { if ( core == null ) {
throw new NullPointerException("SolrCore instance required"); throw new NullPointerException("SolrCore instance required");
} }
this.core = core; CoreDescriptor dcore = core.getCoreDescriptor();
if (core.getCoreDescriptor() != null) { if (dcore == null)
this.multicore = core.getCoreDescriptor().getMultiCore(); throw new NullPointerException("CoreDescriptor required");
this.coreName = core.getCoreDescriptor().getName();
} else { CoreContainer cores = dcore.getCoreContainer();
this.multicore = null; if (cores == null)
this.coreName = null; throw new NullPointerException("CoreContainer required");
}
coreName = dcore.getName();
coreContainer = cores;
_parser = new SolrRequestParsers( null ); _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"); throw new NullPointerException("CoreContainer instance required");
} }
this.core = null; this.coreContainer = coreContainer;
this.multicore = multicore;
this.coreName = coreName == null? "" : coreName; this.coreName = coreName == null? "" : coreName;
_parser = new SolrRequestParsers( null ); _parser = new SolrRequestParsers( null );
} }
@ -98,15 +107,10 @@ public class EmbeddedSolrServer extends SolrServer
} }
// Check for cores action // Check for cores action
SolrCore core = this.core; SolrCore core = coreContainer.getCore( coreName );
if( core == null )
core = multicore.getCore( coreName );
// solr-647
//else
// core = core.open();
if( core == null ) { if( core == null ) {
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
coreName == null? "No core": "No such core: " + coreName ); "No such core: " + coreName );
} }
SolrParams params = request.getParams(); SolrParams params = request.getParams();
@ -126,14 +130,13 @@ public class EmbeddedSolrServer extends SolrServer
} }
// Perhaps the path is to manage the cores // Perhaps the path is to manage the cores
if( handler == null && if( handler == null &&
multicore != null && coreContainer != null &&
path.equals( multicore.getAdminPath() ) ) { path.equals( coreContainer.getAdminPath() ) ) {
handler = multicore.getMultiCoreHandler(); handler = coreContainer.getMultiCoreHandler();
} }
} }
if( handler == null ) { if( handler == null ) {
// solr-647 core.close();
// core.close();
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "unknown handler: "+path ); throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "unknown handler: "+path );
} }
@ -158,8 +161,7 @@ public class EmbeddedSolrServer extends SolrServer
throw new SolrServerException( ex ); throw new SolrServerException( ex );
} }
finally { finally {
// solr-647 core.close();
// core.close();
} }
} }

View File

@ -45,6 +45,6 @@ public class LargeVolumeEmbeddedTest extends LargeVolumeTestBase {
@Override @Override
protected SolrServer createNewSolrServer() 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 @Override
protected SolrServer createNewSolrServer() 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.OutputStreamWriter;
import java.io.Writer; import java.io.Writer;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.util.Collection; import java.util.*;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException; 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.SolrException;
import org.apache.solr.common.util.DOMUtil; import org.apache.solr.common.util.DOMUtil;
import org.apache.solr.common.util.XML; 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.handler.admin.CoreAdminHandler;
import org.apache.solr.schema.IndexSchema; import org.apache.solr.schema.IndexSchema;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@ -51,7 +50,7 @@ public class CoreContainer
{ {
protected static Logger log = Logger.getLogger(CoreContainer.class.getName()); 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 boolean persistent = false;
protected String adminPath = null; protected String adminPath = null;
protected String managementPath = null; protected String managementPath = null;
@ -69,8 +68,7 @@ public class CoreContainer
public static class Initializer { public static class Initializer {
protected String solrConfigFilename = null; protected String solrConfigFilename = null;
protected boolean abortOnConfigurationError = true; protected boolean abortOnConfigurationError = true;
protected String managementPath = null;
public boolean isAbortOnConfigurationError() { public boolean isAbortOnConfigurationError() {
return abortOnConfigurationError; return abortOnConfigurationError;
} }
@ -86,14 +84,6 @@ public class CoreContainer
public void setSolrConfigFilename(String solrConfigFilename) { public void setSolrConfigFilename(String solrConfigFilename) {
this.solrConfigFilename = solrConfigFilename; this.solrConfigFilename = solrConfigFilename;
} }
public String getManagementPath() {
return managementPath;
}
public void setManagementPath(String managementPath) {
this.managementPath = managementPath;
}
// core container instantiation // core container instantiation
public CoreContainer initialize() throws IOException, ParserConfigurationException, SAXException { public CoreContainer initialize() throws IOException, ParserConfigurationException, SAXException {
@ -116,16 +106,13 @@ public class CoreContainer
solrConfigFilename = cores.getConfigFile().getName(); solrConfigFilename = cores.getConfigFile().getName();
} else { } else {
// perform compatibility init // perform compatibility init
cores = new CoreContainer(); cores = new CoreContainer(new SolrResourceLoader(instanceDir));
cores.loader = new SolrResourceLoader(instanceDir);
SolrConfig cfg = solrConfigFilename == null ? new SolrConfig() : new SolrConfig(solrConfigFilename); SolrConfig cfg = solrConfigFilename == null ? new SolrConfig() : new SolrConfig(solrConfigFilename);
CoreDescriptor dcore = new CoreDescriptor(cores); CoreDescriptor dcore = new CoreDescriptor(cores, "", cfg.getResourceLoader().getInstanceDir());
dcore.init("", cfg.getResourceLoader().getInstanceDir());
SolrCore singlecore = new SolrCore(null, null, cfg, null, dcore); SolrCore singlecore = new SolrCore(null, null, cfg, null, dcore);
dcore.setCore(singlecore);
abortOnConfigurationError = cfg.getBool( abortOnConfigurationError = cfg.getBool(
"abortOnConfigurationError", abortOnConfigurationError); "abortOnConfigurationError", abortOnConfigurationError);
cores.register(dcore); cores.register("", singlecore, false);
cores.setPersistent(false); cores.setPersistent(false);
solrConfigFilename = cfg.getName(); solrConfigFilename = cfg.getName();
} }
@ -147,6 +134,14 @@ public class CoreContainer
this.load(dir, configFile); this.load(dir, configFile);
} }
/**
* Minimal CoreContainer constructor.
* @param loader the CoreContainer resource loader
*/
public CoreContainer(SolrResourceLoader loader) {
this.loader = loader;
}
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Initialization / Cleanup // Initialization / Cleanup
//------------------------------------------------------------------- //-------------------------------------------------------------------
@ -165,54 +160,60 @@ public class CoreContainer
FileInputStream cfgis = new FileInputStream(configFile); FileInputStream cfgis = new FileInputStream(configFile);
try { try {
Config cfg = new Config(loader, null, cfgis, null); Config cfg = new Config(loader, null, cfgis, null);
persistent = cfg.getBool( "solr/@persistent", false ); persistent = cfg.getBool( "solr/@persistent", false );
libDir = cfg.get( "solr/@sharedLib", null); libDir = cfg.get( "solr/@sharedLib", null);
adminPath = cfg.get( "solr/cores/@adminPath", null ); adminPath = cfg.get( "solr/cores/@adminPath", null );
managementPath = cfg.get("solr/cores/@managementPath", null ); managementPath = cfg.get("solr/cores/@managementPath", null );
if (libDir != null) { if (libDir != null) {
// relative dir to conf // relative dir to conf
File f = new File(dir, libDir); File f = new File(dir, libDir);
libDir = f.getPath(); libDir = f.getPath();
log.info( "loading shared library: "+f.getAbsolutePath() ); log.info( "loading shared library: "+f.getAbsolutePath() );
libLoader = SolrResourceLoader.createClassLoader(f, null); libLoader = SolrResourceLoader.createClassLoader(f, null);
} }
if( adminPath != null ) { if( adminPath != null ) {
coreAdminHandler = this.createMultiCoreHandler(); coreAdminHandler = this.createMultiCoreHandler();
} }
NodeList nodes = (NodeList)cfg.evaluate("solr/cores/core", XPathConstants.NODESET); NodeList nodes = (NodeList)cfg.evaluate("solr/cores/core", XPathConstants.NODESET);
synchronized (cores) {
for (int i=0; i<nodes.getLength(); i++) { for (int i=0; i<nodes.getLength(); i++) {
Node node = nodes.item(i); Node node = nodes.item(i);
try { try {
CoreDescriptor p = new CoreDescriptor(this); String names = DOMUtil.getAttr(node, "name", null);
p.init(DOMUtil.getAttr(node, "name", null), DOMUtil.getAttr(node, "instanceDir", null)); List<String> aliases = StrUtils.splitSmart(names,',');
// deal with optional settings String name = aliases.get(0);
String opt = DOMUtil.getAttr(node, "config", null); CoreDescriptor p = new CoreDescriptor(this, name, DOMUtil.getAttr(node, "instanceDir", null));
if (opt != null) {
p.setConfigName(opt); // deal with optional settings
} String opt = DOMUtil.getAttr(node, "config", null);
opt = DOMUtil.getAttr(node, "schema", null); if (opt != null) {
if (opt != null) { p.setConfigName(opt);
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);
}
p.setCore(create(p));
} }
catch (Throwable ex) { opt = DOMUtil.getAttr(node, "schema", null);
SolrConfig.severeErrors.add( ex ); if (opt != null) {
SolrException.logOnce(log,null,ex); p.setSchemaName(opt);
} }
SolrCore core = create(p);
for (int a=1; a<aliases.size(); a++) {
core.open();
register(aliases.get(i), core, false);
}
register(name, core, false);
}
catch (Throwable ex) {
SolrConfig.severeErrors.add( ex );
SolrException.logOnce(log,null,ex);
} }
} }
} }
finally { finally {
if (cfgis != null) { if (cfgis != null) {
try { cfgis.close(); } catch (Exception xany) {} try { cfgis.close(); } catch (Exception xany) {}
@ -225,11 +226,8 @@ public class CoreContainer
*/ */
public void shutdown() { public void shutdown() {
synchronized(cores) { synchronized(cores) {
for(CoreDescriptor descriptor : cores.values()) { for(SolrCore core : cores.values()) {
SolrCore core = descriptor.getCore(); core.close();
if( core != null ) {
core.close();
}
} }
cores.clear(); cores.clear();
} }
@ -239,40 +237,44 @@ public class CoreContainer
protected void finalize() { protected void finalize() {
shutdown(); shutdown();
} }
// ---------------- CoreDescriptor related methods ---------------
/** /**
* Registers a SolrCore descriptor in the registry. * Registers a SolrCore descriptor in the registry.
* @param descr the Solr core descriptor * @return a previous core having the same name if it existed and returnPrev==true
* @return a previous descriptor having the same name if it existed, null otherwise
*/ */
public CoreDescriptor register( CoreDescriptor descr ) { public SolrCore register(String name, SolrCore core, boolean returnPrev) {
if( descr == null ) { if( core == null ) {
throw new RuntimeException( "Can not register a null core." ); throw new RuntimeException( "Can not register a null core." );
} }
String name = descr.getName(); if( name == null ||
if( name == null ||
name.indexOf( '/' ) >= 0 || name.indexOf( '/' ) >= 0 ||
name.indexOf( '\\' ) >= 0 ){ name.indexOf( '\\' ) >= 0 ){
throw new RuntimeException( "Invalid core name: "+name ); throw new RuntimeException( "Invalid core name: "+name );
} }
CoreDescriptor old = null; SolrCore old = null;
synchronized (cores) { 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 ); log.info( "registering core: "+name );
return null; return null;
} }
else { else {
log.info( "replacing core: "+name ); log.info( "replacing core: "+name );
if (!returnPrev) {
old.close();
}
return old; 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 * @param dcore a core descriptor
* @return the newly created core * @return the newly created core
@ -293,10 +295,6 @@ public class CoreContainer
SolrConfig config = new SolrConfig(solrLoader, dcore.getConfigName(), null); SolrConfig config = new SolrConfig(solrLoader, dcore.getConfigName(), null);
IndexSchema schema = new IndexSchema(config, dcore.getSchemaName(), null); IndexSchema schema = new IndexSchema(config, dcore.getSchemaName(), null);
SolrCore core = new SolrCore(dcore.getName(), null, config, schema, dcore); SolrCore core = new SolrCore(dcore.getName(), null, config, schema, dcore);
dcore.setCore(core);
// Register the new core
CoreDescriptor old = this.register(dcore);
return core; return core;
} }
@ -304,37 +302,39 @@ public class CoreContainer
* @return a Collection of registered SolrCores * @return a Collection of registered SolrCores
*/ */
public Collection<SolrCore> getCores() { public Collection<SolrCore> getCores() {
java.util.List<SolrCore> l = new java.util.ArrayList<SolrCore>(); List<SolrCore> lst = new ArrayList<SolrCore>();
synchronized (cores) { synchronized (cores) {
for(CoreDescriptor descr : this.cores.values()) { lst.addAll(this.cores.values());
if (descr.getCore() != null)
l.add(descr.getCore());
}
} }
return l; return lst;
}
/**
* @return a Collection of registered CoreDescriptors
*/
public Collection<CoreDescriptor> getDescriptors() {
java.util.List<CoreDescriptor> l = new java.util.ArrayList<CoreDescriptor>();
synchronized (cores) {
l.addAll(cores.values());
}
return l;
} }
/** /**
* @return the CoreDescriptor registered under that name * @return a Collection of the names that cores are mapped to
*/ */
public CoreDescriptor getDescriptor(String name) { public Collection<String> getCoreNames() {
synchronized(cores) { List<String> lst = new ArrayList<String>();
return cores.get( name ); synchronized (cores) {
lst.addAll(this.cores.keySet());
} }
return lst;
} }
/**
* @return a Collection of the names that a specific core is mapped to.
*/
public Collection<String> getCoreNames(SolrCore core) {
List<String> lst = new ArrayList<String>();
synchronized (cores) {
for (Map.Entry<String,SolrCore> entry : cores.entrySet()) {
if (core == entry.getValue()) {
lst.add(entry.getKey());
}
}
}
return lst;
}
// ---------------- Core name related methods --------------- // ---------------- Core name related methods ---------------
/** /**
* Recreates a SolrCore. * Recreates a SolrCore.
@ -348,12 +348,14 @@ public class CoreContainer
*/ */
public void reload(String name) throws ParserConfigurationException, IOException, SAXException { public void reload(String name) throws ParserConfigurationException, IOException, SAXException {
SolrCore core;
synchronized(cores) { synchronized(cores) {
CoreDescriptor dcore = cores.get(name); core = cores.get(name);
if (dcore == null)
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No such core: " + name );
create(new CoreDescriptor(dcore));
} }
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." ); throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Can not swap unnamed cores." );
} }
synchronized( cores ) { synchronized( cores ) {
CoreDescriptor c0 = cores.get(n0); SolrCore c0 = cores.get(n0);
SolrCore c1 = cores.get(n1);
if (c0 == null) if (c0 == null)
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No such core: " + n0 ); throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No such core: " + n0 );
CoreDescriptor c1 = cores.get(n1);
if (c1 == null) if (c1 == null)
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No such core: " + n1 ); throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No such core: " + n1 );
cores.put(n0, c1); cores.put(n0, c1);
cores.put(n1, c0); cores.put(n1, c0);
c0.setName( n1 );
if (c0.getCore() != null) c0.setName(n1);
c0.getCore().setName(n1); c1.setName(n0);
c1.setName( n0 );
if (c1.getCore() != null)
c1.getCore().setName(n0);
log.info( "swaped: "+c0.getName() + " with " + c1.getName() );
} }
log.info("swaped: "+n0 + " with " + n1);
} }
/** Removes & closes a registered core. */ /** Removes and returns registered core w/o decrementing it's reference count */
public void remove( String name ) { public SolrCore remove( String name ) {
synchronized(cores) { synchronized(cores) {
CoreDescriptor dcore = cores.remove( name ); return cores.remove( name );
if (dcore == null) {
return;
}
SolrCore core = dcore.getCore();
if (core != null) {
core.close();
}
} }
} }
@ -408,15 +401,10 @@ public class CoreContainer
*/ */
public SolrCore getCore(String name) { public SolrCore getCore(String name) {
synchronized(cores) { synchronized(cores) {
CoreDescriptor dcore = cores.get(name); SolrCore core = cores.get(name);
SolrCore core = null; if (core != null)
if (dcore != null) core.open(); // increment the ref count while still synchronized
core = dcore.getCore(); return core;
return core;
// solr-647
// if (core != null)
// return core.open();
// return null;
} }
} }
@ -433,20 +421,17 @@ public class CoreContainer
* Ensures there is a valid core to handle MultiCore admin taks and * Ensures there is a valid core to handle MultiCore admin taks and
* increase its refcount. * increase its refcount.
* @return the acquired admin core, null if no core is available * @return the acquired admin core, null if no core is available
*/ */
public SolrCore getAdminCore() { public SolrCore getAdminCore() {
synchronized (cores) { synchronized (cores) {
SolrCore core = adminCore != null ? adminCore.get() : null; SolrCore core = adminCore != null ? adminCore.get() : null;
// solr-647 if (core != null) {
// if (core != null) core.open();
// core = core.open(); } else {
if (core == null) { for (SolrCore c : cores.values()) {
for (CoreDescriptor descr : this.cores.values()) { if (c != null) {
core = descr.getCore(); core = c;
// solr-647 core.open();
// if (core != null)
// core = core.open();
if (core != null) {
break; break;
} }
} }
@ -517,7 +502,7 @@ public class CoreContainer
public void persist() { public void persist() {
persistFile(null); persistFile(null);
} }
/** Persists the cores config file in a user provided file. */ /** Persists the cores config file in a user provided file. */
public void persistFile(File file) { public void persistFile(File file) {
File tmpFile = null; File tmpFile = null;
@ -527,7 +512,6 @@ public class CoreContainer
file = tmpFile = File.createTempFile("solr", ".xml", configFile.getParentFile()); file = tmpFile = File.createTempFile("solr", ".xml", configFile.getParentFile());
} }
java.io.FileOutputStream out = new java.io.FileOutputStream(file); java.io.FileOutputStream out = new java.io.FileOutputStream(file);
synchronized(cores) {
Writer writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8")); Writer writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
persist(writer); persist(writer);
writer.flush(); writer.flush();
@ -540,7 +524,6 @@ public class CoreContainer
else else
fileCopy(tmpFile, configFile); fileCopy(tmpFile, configFile);
} }
}
} }
catch(java.io.FileNotFoundException xnf) { catch(java.io.FileNotFoundException xnf) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, xnf); throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, xnf);
@ -578,20 +561,35 @@ public class CoreContainer
writer.write('\''); writer.write('\'');
writer.write(">\n"); writer.write(">\n");
Map<SolrCore, LinkedList<String>> aliases = new HashMap<SolrCore,LinkedList<String>>();
synchronized(cores) { synchronized(cores) {
for (Map.Entry<String, CoreDescriptor> entry : cores.entrySet()) { for (Map.Entry<String, SolrCore> entry : cores.entrySet()) {
persist(writer, entry.getValue()); 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("</cores>\n");
writer.write("</solr>\n"); writer.write("</solr>\n");
} }
/** Writes the cores configuration node for a given core. */ /** 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(" <core");
writer.write (" name='"); writer.write (" name='");
XML.escapeAttributeValue(dcore.getName(), writer); XML.escapeAttributeValue(StrUtils.join(aliases,','), writer);
writer.write("' instanceDir='"); writer.write("' instanceDir='");
XML.escapeAttributeValue(dcore.getInstanceDir(), writer); XML.escapeAttributeValue(dcore.getInstanceDir(), writer);
writer.write('\''); writer.write('\'');

View File

@ -28,22 +28,17 @@ public class CoreDescriptor implements Cloneable {
protected String instanceDir; protected String instanceDir;
protected String configName; protected String configName;
protected String schemaName; protected String schemaName;
protected SolrCore core = null;
private final CoreContainer coreContainer; private final CoreContainer coreContainer;
public CoreDescriptor(CoreContainer coreContainer) { public CoreDescriptor(CoreContainer coreContainer, String name, String instanceDir) {
this.coreContainer = coreContainer; this.coreContainer = coreContainer;
} this.name = name;
/** Initialize defaults from instance directory. */
public void init(String name, String instanceDir) {
if (name == null) { if (name == null) {
throw new RuntimeException("Core needs a name"); throw new RuntimeException("Core needs a name");
} }
if (instanceDir == null) { if (instanceDir == null) {
throw new NullPointerException("Missing required \'instanceDir\'"); throw new NullPointerException("Missing required \'instanceDir\'");
} }
this.name = name;
if (!instanceDir.endsWith("/")) instanceDir = instanceDir + "/"; if (!instanceDir.endsWith("/")) instanceDir = instanceDir + "/";
this.instanceDir = instanceDir; this.instanceDir = instanceDir;
this.configName = getDefaultConfigName(); this.configName = getDefaultConfigName();
@ -51,10 +46,10 @@ public class CoreDescriptor implements Cloneable {
} }
public CoreDescriptor(CoreDescriptor descr) { public CoreDescriptor(CoreDescriptor descr) {
this.name = descr.name;
this.instanceDir = descr.instanceDir; this.instanceDir = descr.instanceDir;
this.configName = descr.configName; this.configName = descr.configName;
this.schemaName = descr.schemaName; this.schemaName = descr.schemaName;
this.name = descr.name;
coreContainer = descr.coreContainer; coreContainer = descr.coreContainer;
} }
@ -72,17 +67,7 @@ public class CoreDescriptor implements Cloneable {
public String getDefaultDataDir() { public String getDefaultDataDir() {
return this.instanceDir + "data/"; 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. */ /**@return the core instance directory. */
public String getInstanceDir() { public String getInstanceDir() {
return instanceDir; return instanceDir;
@ -111,16 +96,13 @@ public class CoreDescriptor implements Cloneable {
public String getSchemaName() { public String getSchemaName() {
return this.schemaName; return this.schemaName;
} }
public SolrCore getCore() { /**@return the initial core name */
return core; public String getName() {
} return this.name;
public void setCore(SolrCore core) {
this.core = core;
} }
public CoreContainer getMultiCore() { public CoreContainer getCoreContainer() {
return coreContainer; return coreContainer;
} }
} }

View File

@ -62,13 +62,15 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.net.URL;
/** /**
* @version $Id$ * @version $Id$
*/ */
public final class SolrCore { public final class SolrCore implements SolrInfoMBean {
public static final String version="1.0"; public static final String version="1.0";
public static Logger log = Logger.getLogger(SolrCore.class.getName()); public static Logger log = Logger.getLogger(SolrCore.class.getName());
@ -374,7 +376,7 @@ public final class SolrCore {
* @since solr 1.0 * @since solr 1.0
*/ */
public SolrCore(String dataDir, IndexSchema schema) throws ParserConfigurationException, IOException, SAXException { public SolrCore(String dataDir, IndexSchema schema) throws ParserConfigurationException, IOException, SAXException {
this( null, dataDir, new SolrConfig(), schema, null ); this(null, dataDir, new SolrConfig(), schema, null );
} }
/** /**
@ -479,8 +481,9 @@ public final class SolrCore {
latch.countDown(); latch.countDown();
} }
} // end synchronized } // end synchronized
infoRegistry.put("core", this);
} }
/** /**
@ -569,16 +572,39 @@ public final class SolrCore {
} }
return chain; 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. * Close all resources allocated by the core.
* 1. searcher * 1. searcher
* 2. updateHandler * 2. updateHandler
* 3. all CloseHooks will be notified * 3. all CloseHooks will be notified
* 4. All MBeans will be unregistered from MBeanServer if JMX was enabled * 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() { 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 { try {
infoRegistry.clear(); infoRegistry.clear();
} catch (Exception e) { } catch (Exception e) {
@ -602,16 +628,22 @@ public final class SolrCore {
if( closeHooks != null ) { if( closeHooks != null ) {
for( CloseHook hook : closeHooks ) { for( CloseHook hook : closeHooks ) {
hook.close( this ); hook.close( this );
} }
} }
} }
public boolean isClosed() { /** Current core usage count. */
return _searcher == null; public int getOpenCount() {
return refCount.get();
} }
@Override /** Whether this core is closed. */
protected void finalize() { close(); } public boolean isClosed() {
return refCount.get() <= 0;
}
// this can cause an extra close
// protected void finalize() { close(); }
private List<CloseHook> closeHooks = null; private List<CloseHook> closeHooks = null;
@ -1380,6 +1412,45 @@ public final class SolrCore {
public CoreDescriptor getCoreDescriptor() { public CoreDescriptor getCoreDescriptor() {
return coreDescriptor; 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) { switch(action) {
case CREATE: { case CREATE: {
CoreDescriptor dcore = new CoreDescriptor(cores); String name = params.get(CoreAdminParams.NAME);
dcore.init(params.get(CoreAdminParams.NAME), CoreDescriptor dcore = new CoreDescriptor(cores, name, params.get(CoreAdminParams.INSTANCE_DIR));
params.get(CoreAdminParams.INSTANCE_DIR));
// fillup optional parameters // fillup optional parameters
String opts = params.get(CoreAdminParams.CONFIG); String opts = params.get(CoreAdminParams.CONFIG);
@ -104,6 +103,7 @@ public abstract class CoreAdminHandler extends RequestHandlerBase
dcore.setSchemaName(opts); dcore.setSchemaName(opts);
SolrCore core = cores.create(dcore); SolrCore core = cores.create(dcore);
cores.register(name, core,false);
rsp.add("core", core.getName()); rsp.add("core", core.getName());
do_persist = cores.isPersistent(); do_persist = cores.isPersistent();
break; break;
@ -112,9 +112,8 @@ public abstract class CoreAdminHandler extends RequestHandlerBase
case STATUS: { case STATUS: {
NamedList<Object> status = new SimpleOrderedMap<Object>(); NamedList<Object> status = new SimpleOrderedMap<Object>();
if( cname == null ) { if( cname == null ) {
for (CoreDescriptor d : cores.getDescriptors()) { for (SolrCore core : cores.getCores()) {
cname = d.getName(); status.add(core.getName(), getCoreStatus( cores, cname ) );
status.add(d.getName(), getCoreStatus( cores, cname ) );
} }
} }
else { else {
@ -172,8 +171,7 @@ public abstract class CoreAdminHandler extends RequestHandlerBase
info.add("index", LukeRequestHandler.getIndexInfo(searcher.get().getReader(), false)); info.add("index", LukeRequestHandler.getIndexInfo(searcher.get().getReader(), false));
searcher.decref(); searcher.decref();
} finally { } finally {
// solr-647 core.close();
// core.close();
} }
} }
return info; 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.common.util.XML;
import org.apache.solr.core.SolrConfig; import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrCore; 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.handler.XmlUpdateRequestHandler;
import org.apache.solr.request.LocalSolrQueryRequest; import org.apache.solr.request.LocalSolrQueryRequest;
import org.apache.solr.request.QueryResponseWriter; import org.apache.solr.request.QueryResponseWriter;
@ -32,6 +35,7 @@ import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath; import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathExpressionException;
@ -60,7 +64,7 @@ import java.util.Map;
* @version $Id:$ * @version $Id:$
*/ */
public class TestHarness { public class TestHarness {
protected CoreContainer container;
private SolrCore core; private SolrCore core;
private XPath xpath = XPathFactory.newInstance().newXPath(); private XPath xpath = XPathFactory.newInstance().newXPath();
private DocumentBuilder builder; private DocumentBuilder builder;
@ -123,8 +127,19 @@ public class TestHarness {
public TestHarness( String dataDirectory, public TestHarness( String dataDirectory,
SolrConfig solrConfig, SolrConfig solrConfig,
IndexSchema indexSchema) { IndexSchema indexSchema) {
this("", new Initializer("", dataDirectory, solrConfig, indexSchema));
}
public TestHarness(String coreName, CoreContainer.Initializer init) {
try { 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(); builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
updater = new XmlUpdateRequestHandler(); updater = new XmlUpdateRequestHandler();
@ -133,6 +148,42 @@ public class TestHarness {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
// 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() { public SolrCore getCore() {
return core; return core;
@ -152,9 +203,7 @@ public class TestHarness {
StringReader req = new StringReader(xml); StringReader req = new StringReader(xml);
StringWriter writer = new StringWriter(32000); StringWriter writer = new StringWriter(32000);
// This relies on the fact that SolrCore.getSolrCore() uses the
// last instantiated SolrCore.
updater.doLegacyUpdate(req, writer); updater.doLegacyUpdate(req, writer);
return writer.toString(); return writer.toString();
} }
@ -321,7 +370,17 @@ public class TestHarness {
* Shuts down and frees any resources * Shuts down and frees any resources
*/ */
public void close() { 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.AbstractSolrTestCase;
import org.apache.solr.util.plugin.SolrCoreAware; import org.apache.solr.util.plugin.SolrCoreAware;
import java.util.concurrent.*;
import java.util.*;
public class SolrCoreTest extends AbstractSolrTestCase { public class SolrCoreTest extends AbstractSolrTestCase {
public String getSchemaFile() { return "schema.xml"; } public String getSchemaFile() { return "schema.xml"; }
public String getSolrConfigFile() { return "solrconfig.xml"; } public String getSolrConfigFile() { return "solrconfig.xml"; }
public void testRequestHandlerRegistry() { public void testRequestHandlerRegistry() {
// property values defined in build.xml
SolrCore core = h.getCore(); SolrCore core = h.getCore();
EmptyRequestHandler handler1 = new EmptyRequestHandler(); EmptyRequestHandler handler1 = new EmptyRequestHandler();
@ -46,7 +47,8 @@ public class SolrCoreTest extends AbstractSolrTestCase {
} }
public void testClose() throws Exception { public void testClose() throws Exception {
SolrCore core = h.getCore(); final CoreContainer cores = h.getCoreContainer();
SolrCore core = cores.getCore("");
ClosingRequestHandler handler1 = new ClosingRequestHandler(); ClosingRequestHandler handler1 = new ClosingRequestHandler();
handler1.inform( core ); handler1.inform( core );
@ -56,10 +58,116 @@ public class SolrCoreTest extends AbstractSolrTestCase {
assertNull( old ); // should not be anything... assertNull( old ); // should not be anything...
assertEquals( core.getRequestHandlers().get( path ), handler1 ); assertEquals( core.getRequestHandlers().get( path ), handler1 );
core.close(); core.close();
cores.shutdown();
assertTrue("Handler not closed", handler1.closed == true); 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 { class ClosingRequestHandler extends EmptyRequestHandler implements SolrCoreAware {
boolean closed = false; boolean closed = false;

View File

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

View File

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