SOLR-1797: fix ResourceLoader thread safety

git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/branches/newtrunk@925896 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yonik Seeley 2010-03-21 21:05:57 +00:00
parent 4abb609eb8
commit f011f9535b
3 changed files with 88 additions and 39 deletions

View File

@ -223,6 +223,9 @@ Bug Fixes
a ClassCastException when a Map containing a non-String key is used. a ClassCastException when a Map containing a non-String key is used.
(Frank Wesemann, hossman) (Frank Wesemann, hossman)
* SOLR-1797: fix ConcurrentModificationException and potential memory
leaks in ResourceLoader. (yonik)
Other Changes Other Changes
---------------------- ----------------------

View File

@ -586,7 +586,7 @@ public final class SolrCore implements SolrInfoMBean {
// Finally tell anyone who wants to know // Finally tell anyone who wants to know
resourceLoader.inform( resourceLoader ); resourceLoader.inform( resourceLoader );
resourceLoader.inform( this ); resourceLoader.inform( this ); // last call before the latch is released.
instance = this; // set singleton for backwards compatibility instance = this; // set singleton for backwards compatibility
} catch (IOException e) { } catch (IOException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e); throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);

View File

@ -69,13 +69,15 @@ public class SolrResourceLoader implements ResourceLoader
private final String instanceDir; private final String instanceDir;
private String dataDir; private String dataDir;
private final List<SolrCoreAware> waitingForCore = new ArrayList<SolrCoreAware>(); private final List<SolrCoreAware> waitingForCore = Collections.synchronizedList(new ArrayList<SolrCoreAware>());
private final List<SolrInfoMBean> infoMBeans = new ArrayList<SolrInfoMBean>(); private final List<SolrInfoMBean> infoMBeans = Collections.synchronizedList(new ArrayList<SolrInfoMBean>());
private final List<ResourceLoaderAware> waitingForResources = new ArrayList<ResourceLoaderAware>(); private final List<ResourceLoaderAware> waitingForResources = Collections.synchronizedList(new ArrayList<ResourceLoaderAware>());
private static final Charset UTF_8 = Charset.forName("UTF-8"); private static final Charset UTF_8 = Charset.forName("UTF-8");
private final Properties coreProperties; private final Properties coreProperties;
private volatile boolean live;
/** /**
* <p> * <p>
* This loader will delegate to the context classloader when possible, * This loader will delegate to the context classloader when possible,
@ -399,18 +401,20 @@ public class SolrResourceLoader implements ResourceLoader
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
"Error instantiating class: '" + clazz.getName()+"'", e, false ); "Error instantiating class: '" + clazz.getName()+"'", e, false );
} }
if( obj instanceof SolrCoreAware ) { if (!live) {
assertAwareCompatibility( SolrCoreAware.class, obj ); if( obj instanceof SolrCoreAware ) {
waitingForCore.add( (SolrCoreAware)obj ); assertAwareCompatibility( SolrCoreAware.class, obj );
} waitingForCore.add( (SolrCoreAware)obj );
if( obj instanceof ResourceLoaderAware ) { }
assertAwareCompatibility( ResourceLoaderAware.class, obj ); if( obj instanceof ResourceLoaderAware ) {
waitingForResources.add( (ResourceLoaderAware)obj ); assertAwareCompatibility( ResourceLoaderAware.class, obj );
} waitingForResources.add( (ResourceLoaderAware)obj );
if (obj instanceof SolrInfoMBean){ }
//TODO: Assert here? if (obj instanceof SolrInfoMBean){
infoMBeans.add((SolrInfoMBean) obj); //TODO: Assert here?
infoMBeans.add((SolrInfoMBean) obj);
}
} }
return obj; return obj;
} }
@ -431,12 +435,16 @@ public class SolrResourceLoader implements ResourceLoader
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
"Error instantiating class: '" + clazz.getName()+"'", e, false ); "Error instantiating class: '" + clazz.getName()+"'", e, false );
} }
//TODO: Does SolrCoreAware make sense here since in a multi-core context
// which core are we talking about ? if (!live) {
if( obj instanceof ResourceLoaderAware ) { //TODO: Does SolrCoreAware make sense here since in a multi-core context
assertAwareCompatibility( ResourceLoaderAware.class, obj ); // which core are we talking about ?
waitingForResources.add( (ResourceLoaderAware)obj ); if( obj instanceof ResourceLoaderAware ) {
assertAwareCompatibility( ResourceLoaderAware.class, obj );
waitingForResources.add( (ResourceLoaderAware)obj );
}
} }
return obj; return obj;
} }
@ -460,18 +468,21 @@ public class SolrResourceLoader implements ResourceLoader
"Error instantiating class: '" + clazz.getName()+"'", e, false ); "Error instantiating class: '" + clazz.getName()+"'", e, false );
} }
if( obj instanceof SolrCoreAware ) { if (!live) {
assertAwareCompatibility( SolrCoreAware.class, obj ); if( obj instanceof SolrCoreAware ) {
waitingForCore.add( (SolrCoreAware)obj ); assertAwareCompatibility( SolrCoreAware.class, obj );
} waitingForCore.add( (SolrCoreAware)obj );
if( obj instanceof ResourceLoaderAware ) { }
assertAwareCompatibility( ResourceLoaderAware.class, obj ); if( obj instanceof ResourceLoaderAware ) {
waitingForResources.add( (ResourceLoaderAware)obj ); assertAwareCompatibility( ResourceLoaderAware.class, obj );
} waitingForResources.add( (ResourceLoaderAware)obj );
if (obj instanceof SolrInfoMBean){ }
//TODO: Assert here? if (obj instanceof SolrInfoMBean){
infoMBeans.add((SolrInfoMBean) obj); //TODO: Assert here?
infoMBeans.add((SolrInfoMBean) obj);
}
} }
return obj; return obj;
} }
@ -482,10 +493,24 @@ public class SolrResourceLoader implements ResourceLoader
public void inform(SolrCore core) public void inform(SolrCore core)
{ {
this.dataDir = core.getDataDir(); this.dataDir = core.getDataDir();
for( SolrCoreAware aware : waitingForCore ) {
aware.inform( core ); // make a copy to avoid potential deadlock of a callback calling newInstance and trying to
// add something to waitingForCore.
SolrCoreAware[] arr;
while (waitingForCore.size() > 0) {
synchronized (waitingForCore) {
arr = waitingForCore.toArray(new SolrCoreAware[waitingForCore.size()]);
waitingForCore.clear();
}
for( SolrCoreAware aware : arr) {
aware.inform( core );
}
} }
waitingForCore.clear();
// this is the last method to be called in SolrCore before the latch is released.
live = true;
} }
/** /**
@ -493,10 +518,20 @@ public class SolrResourceLoader implements ResourceLoader
*/ */
public void inform( ResourceLoader loader ) public void inform( ResourceLoader loader )
{ {
for( ResourceLoaderAware aware : waitingForResources ) {
aware.inform( loader ); // make a copy to avoid potential deadlock of a callback adding to the list
ResourceLoaderAware[] arr;
while (waitingForResources.size() > 0) {
synchronized (waitingForResources) {
arr = waitingForResources.toArray(new ResourceLoaderAware[waitingForResources.size()]);
waitingForResources.clear();
}
for( ResourceLoaderAware aware : arr) {
aware.inform(loader);
}
} }
waitingForResources.clear();
} }
/** /**
@ -504,10 +539,21 @@ public class SolrResourceLoader implements ResourceLoader
* @param infoRegistry The Info Registry * @param infoRegistry The Info Registry
*/ */
public void inform(Map<String, SolrInfoMBean> infoRegistry) { public void inform(Map<String, SolrInfoMBean> infoRegistry) {
for (SolrInfoMBean bean : infoMBeans) { // this can currently happen concurrently with requests starting and lazy components
// loading. Make sure infoMBeans doesn't change.
SolrInfoMBean[] arr;
synchronized (infoMBeans) {
arr = infoMBeans.toArray(new SolrInfoMBean[infoMBeans.size()]);
waitingForResources.clear();
}
for (SolrInfoMBean bean : arr) {
infoRegistry.put(bean.getName(), bean); infoRegistry.put(bean.getName(), bean);
} }
} }
/** /**
* Determines the solrhome from the environment. * Determines the solrhome from the environment.
* Tries JNDI (java:comp/env/solr/home) then system property (solr.solr.home); * Tries JNDI (java:comp/env/solr/home) then system property (solr.solr.home);