SOLR-9867: fixing TestSolrCLIRunExample.testTechproductsExample

- SolrDispatchFilter.doFilter rejects invocation until init() is completed.
- introducing isLoaded=false, isLoading=true core status
- blocking shutdown until core loading stops
- looping run example tool while core is loading 1 min max.
This commit is contained in:
Mikhail Khludnev 2017-04-29 00:25:28 +03:00
parent e912b7cb5c
commit 32b7791fa8
6 changed files with 136 additions and 91 deletions

View File

@ -332,6 +332,9 @@ Other Changes
* SOLR-10519: SolrCLI.atPath cannot handle children that begin with a slash. (Erick Erickson)
* SOLR-9867: Adding isLoading=true as core status. Fixing start after stop scenario in bin/solr
(Mikhail Khludnev)
================== 6.5.1 ==================
Bug Fixes

View File

@ -95,6 +95,7 @@ class SolrCores implements Observer {
// We are shutting down. You can't hold the lock on the various lists of cores while they shut down, so we need to
// make a temporary copy of the names and shut them down outside the lock.
protected void close() {
waitForLoadingCoresToFinish(30*1000);
Collection<SolrCore> coreList = new ArrayList<>();

View File

@ -303,50 +303,56 @@ enum CoreAdminOperation implements CoreAdminOp {
static NamedList<Object> getCoreStatus(CoreContainer cores, String cname, boolean isIndexInfoNeeded) throws IOException {
NamedList<Object> info = new SimpleOrderedMap<>();
if (!cores.isLoaded(cname)) { // Lazily-loaded core, fill in what we can.
// It would be a real mistake to load the cores just to get the status
CoreDescriptor desc = cores.getUnloadedCoreDescriptor(cname);
if (desc != null) {
info.add(NAME, desc.getName());
info.add("instanceDir", desc.getInstanceDir());
// None of the following are guaranteed to be present in a not-yet-loaded core.
String tmp = desc.getDataDir();
if (StringUtils.isNotBlank(tmp)) info.add("dataDir", tmp);
tmp = desc.getConfigName();
if (StringUtils.isNotBlank(tmp)) info.add("config", tmp);
tmp = desc.getSchemaName();
if (StringUtils.isNotBlank(tmp)) info.add("schema", tmp);
info.add("isLoaded", "false");
}
if (cores.isCoreLoading(cname)) {
info.add(NAME, cname);
info.add("isLoaded", "false");
info.add("isLoading", "true");
} else {
try (SolrCore core = cores.getCore(cname)) {
if (core != null) {
info.add(NAME, core.getName());
info.add("instanceDir", core.getResourceLoader().getInstancePath().toString());
info.add("dataDir", normalizePath(core.getDataDir()));
info.add("config", core.getConfigResource());
info.add("schema", core.getSchemaResource());
info.add("startTime", core.getStartTimeStamp());
info.add("uptime", core.getUptimeMs());
if (cores.isZooKeeperAware()) {
info.add("lastPublished", core.getCoreDescriptor().getCloudDescriptor().getLastPublished().toString().toLowerCase(Locale.ROOT));
info.add("configVersion", core.getSolrConfig().getZnodeVersion());
SimpleOrderedMap cloudInfo = new SimpleOrderedMap<>();
cloudInfo.add(COLLECTION, core.getCoreDescriptor().getCloudDescriptor().getCollectionName());
cloudInfo.add(SHARD, core.getCoreDescriptor().getCloudDescriptor().getShardId());
cloudInfo.add(REPLICA, core.getCoreDescriptor().getCloudDescriptor().getCoreNodeName());
info.add("cloud", cloudInfo);
}
if (isIndexInfoNeeded) {
RefCounted<SolrIndexSearcher> searcher = core.getSearcher();
try {
SimpleOrderedMap<Object> indexInfo = LukeRequestHandler.getIndexInfo(searcher.get().getIndexReader());
long size = getIndexSize(core);
indexInfo.add("sizeInBytes", size);
indexInfo.add("size", NumberUtils.readableSize(size));
info.add("index", indexInfo);
} finally {
searcher.decref();
if (!cores.isLoaded(cname)) { // Lazily-loaded core, fill in what we can.
// It would be a real mistake to load the cores just to get the status
CoreDescriptor desc = cores.getUnloadedCoreDescriptor(cname);
if (desc != null) {
info.add(NAME, desc.getName());
info.add("instanceDir", desc.getInstanceDir());
// None of the following are guaranteed to be present in a not-yet-loaded core.
String tmp = desc.getDataDir();
if (StringUtils.isNotBlank(tmp)) info.add("dataDir", tmp);
tmp = desc.getConfigName();
if (StringUtils.isNotBlank(tmp)) info.add("config", tmp);
tmp = desc.getSchemaName();
if (StringUtils.isNotBlank(tmp)) info.add("schema", tmp);
info.add("isLoaded", "false");
}
} else {
try (SolrCore core = cores.getCore(cname)) {
if (core != null) {
info.add(NAME, core.getName());
info.add("instanceDir", core.getResourceLoader().getInstancePath().toString());
info.add("dataDir", normalizePath(core.getDataDir()));
info.add("config", core.getConfigResource());
info.add("schema", core.getSchemaResource());
info.add("startTime", core.getStartTimeStamp());
info.add("uptime", core.getUptimeMs());
if (cores.isZooKeeperAware()) {
info.add("lastPublished", core.getCoreDescriptor().getCloudDescriptor().getLastPublished().toString().toLowerCase(Locale.ROOT));
info.add("configVersion", core.getSolrConfig().getZnodeVersion());
SimpleOrderedMap cloudInfo = new SimpleOrderedMap<>();
cloudInfo.add(COLLECTION, core.getCoreDescriptor().getCloudDescriptor().getCollectionName());
cloudInfo.add(SHARD, core.getCoreDescriptor().getCloudDescriptor().getShardId());
cloudInfo.add(REPLICA, core.getCoreDescriptor().getCloudDescriptor().getCoreNodeName());
info.add("cloud", cloudInfo);
}
if (isIndexInfoNeeded) {
RefCounted<SolrIndexSearcher> searcher = core.getSearcher();
try {
SimpleOrderedMap<Object> indexInfo = LukeRequestHandler.getIndexInfo(searcher.get().getIndexReader());
long size = getIndexSize(core);
indexInfo.add("sizeInBytes", size);
indexInfo.add("size", NumberUtils.readableSize(size));
info.add("index", indexInfo);
} finally {
searcher.decref();
}
}
}
}

View File

@ -16,18 +16,6 @@
*/
package org.apache.solr.servlet;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@ -46,10 +34,18 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.codahale.metrics.jvm.ClassLoadingGaugeSet;
import com.codahale.metrics.jvm.GarbageCollectorMetricSet;
import com.codahale.metrics.jvm.MemoryUsageGaugeSet;
import com.codahale.metrics.jvm.ThreadStatesGaugeSet;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.apache.commons.io.FileCleaningTracker;
import org.apache.commons.io.input.CloseShieldInputStream;
import org.apache.commons.io.output.CloseShieldOutputStream;
@ -78,6 +74,11 @@ import org.apache.solr.util.SolrFileCleaningTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.jvm.ClassLoadingGaugeSet;
import com.codahale.metrics.jvm.GarbageCollectorMetricSet;
import com.codahale.metrics.jvm.MemoryUsageGaugeSet;
import com.codahale.metrics.jvm.ThreadStatesGaugeSet;
/**
* This filter looks at the incoming URL maps them to handlers defined in solrconfig.xml
*
@ -136,6 +137,8 @@ public class SolrDispatchFilter extends BaseSolrFilter {
public void init(FilterConfig config) throws ServletException
{
log.trace("SolrDispatchFilter.init(): {}", this.getClass().getClassLoader());
CoreContainer coresInit = null;
try{
SolrRequestParsers.fileCleaningTracker = new SolrFileCleaningTracker();
@ -166,9 +169,9 @@ public class SolrDispatchFilter extends BaseSolrFilter {
String solrHome = (String) config.getServletContext().getAttribute(SOLRHOME_ATTRIBUTE);
ExecutorUtil.addThreadLocalProvider(SolrRequestInfo.getInheritableThreadLocalProvider());
this.cores = createCoreContainer(solrHome == null ? SolrResourceLoader.locateSolrHome() : Paths.get(solrHome),
coresInit = createCoreContainer(solrHome == null ? SolrResourceLoader.locateSolrHome() : Paths.get(solrHome),
extraProperties);
this.httpClient = cores.getUpdateShardHandler().getHttpClient();
this.httpClient = coresInit.getUpdateShardHandler().getHttpClient();
setupJvmMetrics();
log.debug("user.dir=" + System.getProperty("user.dir"));
}
@ -181,7 +184,10 @@ public class SolrDispatchFilter extends BaseSolrFilter {
}
}
log.trace("SolrDispatchFilter.init() done");
}finally{
log.trace("SolrDispatchFilter.init() done");
this.cores = coresInit; // crucially final assignment
}
}
private void setupJvmMetrics() {
@ -308,6 +314,7 @@ public class SolrDispatchFilter extends BaseSolrFilter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain, boolean retry) throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) return;
try {
if (cores == null || cores.isShutDown()) {

View File

@ -16,6 +16,12 @@
*/
package org.apache.solr.util;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.solr.common.SolrException.ErrorCode.FORBIDDEN;
import static org.apache.solr.common.SolrException.ErrorCode.UNAUTHORIZED;
import static org.apache.solr.common.params.CommonParams.DISTRIB;
import static org.apache.solr.common.params.CommonParams.NAME;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -111,16 +117,12 @@ import org.noggit.ObjectBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.solr.common.SolrException.ErrorCode.FORBIDDEN;
import static org.apache.solr.common.SolrException.ErrorCode.UNAUTHORIZED;
import static org.apache.solr.common.params.CommonParams.DISTRIB;
import static org.apache.solr.common.params.CommonParams.NAME;
/**
* Command-line utility for working with Solr.
*/
public class SolrCLI {
private static final long MAX_WAIT_FOR_CORE_LOAD_NANOS = TimeUnit.NANOSECONDS.convert(1, TimeUnit.MINUTES);
/**
* Defines the interface to a Solr tool that can be run from this command-line app.
*/
@ -1399,12 +1401,23 @@ public class SolrCLI {
public static boolean safeCheckCoreExists(String coreStatusUrl, String coreName) {
boolean exists = false;
try {
Map<String,Object> existsCheckResult = getJson(coreStatusUrl);
Map<String,Object> status = (Map<String, Object>)existsCheckResult.get("status");
Map<String,Object> coreStatus = (Map<String, Object>)status.get(coreName);
Map<String,Object> failureStatus = (Map<String, Object>)existsCheckResult.get("initFailures");
String errorMsg = (String) failureStatus.get(coreName);
exists = coreStatus != null && coreStatus.containsKey(NAME) || errorMsg != null;
boolean wait = false;
final long startWaitAt = System.nanoTime();
do{
if (wait) {
final int clamPeriodForStatusPollMs = 1000;
Thread.sleep(clamPeriodForStatusPollMs);
}
Map<String,Object> existsCheckResult = getJson(coreStatusUrl);
Map<String,Object> status = (Map<String, Object>)existsCheckResult.get("status");
Map<String,Object> coreStatus = (Map<String, Object>)status.get(coreName);
Map<String,Object> failureStatus = (Map<String, Object>)existsCheckResult.get("initFailures");
String errorMsg = (String) failureStatus.get(coreName);
final boolean hasName = coreStatus != null && coreStatus.containsKey(NAME);
exists = hasName || errorMsg != null;
wait = hasName && errorMsg==null && "true".equals(coreStatus.get("isLoading"));
}while (wait &&
System.nanoTime() - startWaitAt < MAX_WAIT_FOR_CORE_LOAD_NANOS);
} catch (Exception exc) {
// just ignore it since we're only interested in a positive result here
}

View File

@ -31,6 +31,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@ -47,8 +48,8 @@ import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.cloud.MiniSolrCloudCluster;
import org.apache.solr.common.SolrInputDocument;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -66,6 +67,13 @@ public class TestSolrCLIRunExample extends SolrTestCaseJ4 {
public static void beforeClass() throws IOException {
assumeFalse("FIXME: This test does not work with whitespace in CWD (https://issues.apache.org/jira/browse/SOLR-8877)",
Paths.get(".").toAbsolutePath().toString().contains(" "));
// to be true
System.setProperty("solr.directoryFactory", "solr.NRTCachingDirectoryFactory");
}
@AfterClass
public static void cleanupDirectoryFactory() throws IOException {
System.clearProperty("solr.directoryFactory");
}
/**
@ -289,7 +297,6 @@ public class TestSolrCLIRunExample extends SolrTestCaseJ4 {
}
}
@Ignore
@Test
public void testTechproductsExample() throws Exception {
testExample("techproducts");
@ -309,6 +316,7 @@ public class TestSolrCLIRunExample extends SolrTestCaseJ4 {
File solrExampleDir = tmpDir.toFile();
File solrServerDir = solrHomeDir.getParentFile();
for (int i =0; i<2; i++){
// need a port to start the example server on
int bindPort = -1;
try (ServerSocket socket = new ServerSocket(0)) {
@ -333,7 +341,8 @@ public class TestSolrCLIRunExample extends SolrTestCaseJ4 {
SolrCLI.RunExampleTool tool = new SolrCLI.RunExampleTool(executor, System.in, stdoutSim);
try {
tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), toolArgs));
final int status = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), toolArgs));
assertEquals("it should be ok "+tool+" "+Arrays.toString(toolArgs),0, status);
} catch (Exception e) {
log.error("RunExampleTool failed due to: " + e +
"; stdout from tool prior to failure: " + baos.toString(StandardCharsets.UTF_8.name()));
@ -352,25 +361,31 @@ public class TestSolrCLIRunExample extends SolrTestCaseJ4 {
if ("techproducts".equals(exampleName)) {
HttpSolrClient solrClient = getHttpSolrClient("http://localhost:" + bindPort + "/solr/" + exampleName);
SolrQuery query = new SolrQuery("*:*");
QueryResponse qr = solrClient.query(query);
long numFound = qr.getResults().getNumFound();
if (numFound == 0) {
// brief wait in case of timing issue in getting the new docs committed
log.warn("Going to wait for 1 second before re-trying query for techproduct example docs ...");
try {
Thread.sleep(1000);
} catch (InterruptedException ignore) {
Thread.interrupted();
try{
SolrQuery query = new SolrQuery("*:*");
QueryResponse qr = solrClient.query(query);
long numFound = qr.getResults().getNumFound();
if (numFound == 0) {
// brief wait in case of timing issue in getting the new docs committed
log.warn("Going to wait for 1 second before re-trying query for techproduct example docs ...");
try {
Thread.sleep(1000);
} catch (InterruptedException ignore) {
Thread.interrupted();
}
numFound = solrClient.query(query).getResults().getNumFound();
}
numFound = solrClient.query(query).getResults().getNumFound();
assertTrue("expected 32 docs in the " + exampleName + " example but found " + numFound + ", output: " + toolOutput,
numFound == 32);
}finally{
solrClient.close();
}
assertTrue("expected 32 docs in the " + exampleName + " example but found " + numFound + ", output: " + toolOutput,
numFound == 32);
}
// stop the test instance
executor.execute(org.apache.commons.exec.CommandLine.parse("bin/solr stop -p " + bindPort));
}
}
/**