Fix for SOLR-4300, possible race condition when loading lazy cores

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1433778 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Erick Erickson 2013-01-16 00:48:09 +00:00
parent 79f5842532
commit d234d7f650
2 changed files with 95 additions and 18 deletions

View File

@ -128,6 +128,8 @@ public class CoreContainer
protected final Map<String, CoreDescriptor> dynamicDescriptors = new LinkedHashMap<String, CoreDescriptor>(); protected final Map<String, CoreDescriptor> dynamicDescriptors = new LinkedHashMap<String, CoreDescriptor>();
protected final Set<String> pendingDynamicCoreLoads = new HashSet<String>();
protected final Map<String,Exception> coreInitFailures = protected final Map<String,Exception> coreInitFailures =
Collections.synchronizedMap(new LinkedHashMap<String,Exception>()); Collections.synchronizedMap(new LinkedHashMap<String,Exception>());
@ -1245,17 +1247,8 @@ public class CoreContainer
} }
} }
} }
private SolrCore getCoreFromAnyList(String name) {
/** Gets a core by name and increase its refcount.
* @see SolrCore#close()
* @param name the core name
* @return the core if found
*/
public SolrCore getCore(String name) {
name = checkDefault(name);
// Do this in two phases since we don't want to lock access to the cores over a load.
SolrCore core; SolrCore core;
synchronized (cores) { synchronized (cores) {
core = cores.get(name); core = cores.get(name);
if (core != null) { if (core != null) {
@ -1274,20 +1267,64 @@ public class CoreContainer
return core; return core;
} }
} }
return null;
}
/** Gets a core by name and increase its refcount.
* @see SolrCore#close()
* @param name the core name
* @return the core if found
*/
public SolrCore getCore(String name) {
name = checkDefault(name);
// Do this in two phases since we don't want to lock access to the cores over a load.
SolrCore core = getCoreFromAnyList(name);
if (core != null) return core;
// OK, it's not presently in any list, is it in the list of dynamic cores but not loaded yet? If so, load it.
CoreDescriptor desc = dynamicDescriptors.get(name); CoreDescriptor desc = dynamicDescriptors.get(name);
if (desc == null) { //Nope, no transient core with this name if (desc == null) { //Nope, no transient core with this name
return null; return null;
} }
// Keep multiple threads from loading the same core at the same time.
try { try {
core = create(desc); // This should throw an error if it fails. boolean isPending;
core.open(); synchronized (pendingDynamicCoreLoads) {
if (desc.isTransient()) { isPending = pendingDynamicCoreLoads.contains(name);
registerLazyCore(name, core, false); // This is a transient core if (! isPending) {
} else { pendingDynamicCoreLoads.add(name);
register(name, core, false); // This is a "permanent", although deferred-load core }
} }
} catch (Exception ex) {
throw recordAndThrow(name, "Unable to create core" + name, ex); while (isPending) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
return null; // Seems best not to do anything at all if the thread is interrupted
}
synchronized (pendingDynamicCoreLoads) {
if (!pendingDynamicCoreLoads.contains(name)) {
// NOTE: If, for some reason, the load failed, we'll return null here and presumably the log will show
// why. We'll fail all over again next time if the problem isn't corrected.
return getCoreFromAnyList(name);
}
}
}
try {
core = create(desc); // This should throw an error if it fails.
core.open();
if (desc.isTransient()) {
registerLazyCore(name, core, false); // This is a transient core
} else {
register(name, core, false); // This is a "permanent", although deferred-load core
}
} catch (Exception ex) {
throw recordAndThrow(name, "Unable to create core" + name, ex);
}
} finally {
pendingDynamicCoreLoads.remove(name);
} }
return core; return core;
} }

View File

@ -34,8 +34,10 @@ import org.junit.Test;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
public class TestLazyCores extends SolrTestCaseJ4 { public class TestLazyCores extends SolrTestCaseJ4 {
@ -246,6 +248,44 @@ public class TestLazyCores extends SolrTestCaseJ4 {
} }
} }
static List<SolrCore> _theCores = new ArrayList<SolrCore>();
// Test case for SOLR-4300
@Test
public void testRace() throws Exception {
final CoreContainer cc = init();
try {
Thread[] threads = new Thread[15];
for (int idx = 0; idx < threads.length; idx++) {
threads[idx] = new Thread() {
@Override
public void run() {
SolrCore core = cc.getCore("collectionLazy3");
synchronized (_theCores) {
_theCores.add(core);
}
}
};
threads[idx].start();
}
for (Thread thread : threads) {
thread.join();
}
for (int idx = 0; idx < _theCores.size() - 1; ++idx) {
assertEquals("Cores should be the same!", _theCores.get(idx), _theCores.get(idx + 1));
}
for (SolrCore core : _theCores) {
core.close();
}
} finally {
cc.shutdown();
}
}
private void checkNotInCores(CoreContainer cc, String... nameCheck) { private void checkNotInCores(CoreContainer cc, String... nameCheck) {
Collection<String> names = cc.getCoreNames(); Collection<String> names = cc.getCoreNames();
for (String name : nameCheck) { for (String name : nameCheck) {