mirror of https://github.com/apache/lucene.git
SOLR-13509: add omitHeader=false for shards requests to avoid NPE on partialResuls check
This commit is contained in:
parent
9ddc94045d
commit
5f6df28e11
|
@ -98,6 +98,8 @@ Bug Fixes
|
|||
|
||||
* SOLR-12013: collections API CUSTERSTATUS command fails when configset missing (Erick Erickson)
|
||||
|
||||
* SOLR-13509: NPE on omitHeader=true is fixed by sending omitHeader=false to shard searches (Munendra S N, Mikhail Khludnev)
|
||||
|
||||
Other Changes
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -849,7 +849,7 @@ public class QueryComponent extends SearchComponent
|
|||
}
|
||||
else {
|
||||
responseHeader = (NamedList<?>)srsp.getSolrResponse().getResponse().get("responseHeader");
|
||||
final Object rhste = (responseHeader == null ? null : responseHeader.get(SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY));
|
||||
final Object rhste = responseHeader.get(SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY);
|
||||
if (rhste != null) {
|
||||
nl.add(SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY, rhste);
|
||||
}
|
||||
|
@ -879,20 +879,16 @@ public class QueryComponent extends SearchComponent
|
|||
}
|
||||
|
||||
final boolean thisResponseIsPartial;
|
||||
if (responseHeader != null) {
|
||||
thisResponseIsPartial = Boolean.TRUE.equals(responseHeader.getBooleanArg(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY));
|
||||
thereArePartialResults |= thisResponseIsPartial;
|
||||
|
||||
if (!Boolean.TRUE.equals(segmentTerminatedEarly)) {
|
||||
final Object ste = responseHeader.get(SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY);
|
||||
if (Boolean.TRUE.equals(ste)) {
|
||||
segmentTerminatedEarly = Boolean.TRUE;
|
||||
} else if (Boolean.FALSE.equals(ste)) {
|
||||
segmentTerminatedEarly = Boolean.FALSE;
|
||||
}
|
||||
thisResponseIsPartial = Boolean.TRUE.equals(responseHeader.getBooleanArg(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY));
|
||||
thereArePartialResults |= thisResponseIsPartial;
|
||||
|
||||
if (!Boolean.TRUE.equals(segmentTerminatedEarly)) {
|
||||
final Object ste = responseHeader.get(SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY);
|
||||
if (Boolean.TRUE.equals(ste)) {
|
||||
segmentTerminatedEarly = Boolean.TRUE;
|
||||
} else if (Boolean.FALSE.equals(ste)) {
|
||||
segmentTerminatedEarly = Boolean.FALSE;
|
||||
}
|
||||
} else {
|
||||
thisResponseIsPartial = false;
|
||||
}
|
||||
|
||||
// calculate global maxScore and numDocsFound
|
||||
|
@ -1185,7 +1181,7 @@ public class QueryComponent extends SearchComponent
|
|||
}
|
||||
{
|
||||
NamedList<?> responseHeader = (NamedList<?>)srsp.getSolrResponse().getResponse().get("responseHeader");
|
||||
if (responseHeader!=null && Boolean.TRUE.equals(responseHeader.getBooleanArg(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY))) {
|
||||
if (Boolean.TRUE.equals(responseHeader.getBooleanArg(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY))) {
|
||||
rb.rsp.getResponseHeader().asShallowMap()
|
||||
.put(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY, Boolean.TRUE);
|
||||
}
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
*/
|
||||
package org.apache.solr.handler.component;
|
||||
|
||||
import static org.apache.solr.common.params.CommonParams.DISTRIB;
|
||||
import static org.apache.solr.common.params.CommonParams.PATH;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
@ -56,6 +53,9 @@ import org.apache.solr.util.plugin.SolrCoreAware;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.apache.solr.common.params.CommonParams.DISTRIB;
|
||||
import static org.apache.solr.common.params.CommonParams.PATH;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -370,6 +370,7 @@ public class SearchHandler extends RequestHandlerBase implements SolrCoreAware ,
|
|||
params.set(ShardParams.IS_SHARD, true); // a sub (shard) request
|
||||
params.set(ShardParams.SHARDS_PURPOSE, sreq.purpose);
|
||||
params.set(ShardParams.SHARD_URL, shard); // so the shard knows what was asked
|
||||
params.set(CommonParams.OMIT_HEADER, false);
|
||||
if (rb.requestInfo != null) {
|
||||
// we could try and detect when this is needed, but it could be tricky
|
||||
params.set("NOW", Long.toString(rb.requestInfo.getNOW().getTime()));
|
||||
|
|
|
@ -21,13 +21,15 @@ import java.util.LinkedHashMap;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import com.codahale.metrics.Metered;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
|
||||
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
|
||||
import org.apache.solr.client.solrj.request.UpdateRequest;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.cloud.MiniSolrCloudCluster.JettySolrRunnerWithMetrics;
|
||||
import static org.apache.solr.cloud.TrollingIndexReaderFactory.*;
|
||||
import org.apache.solr.common.cloud.DocCollection;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.common.params.SolrParams;
|
||||
|
@ -40,9 +42,11 @@ import org.junit.Test;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import com.codahale.metrics.Metered;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import static org.apache.solr.cloud.TrollingIndexReaderFactory.CheckMethodName;
|
||||
import static org.apache.solr.cloud.TrollingIndexReaderFactory.Trap;
|
||||
import static org.apache.solr.cloud.TrollingIndexReaderFactory.catchClass;
|
||||
import static org.apache.solr.cloud.TrollingIndexReaderFactory.catchCount;
|
||||
import static org.apache.solr.cloud.TrollingIndexReaderFactory.catchTrace;
|
||||
|
||||
/**
|
||||
* Distributed test for {@link org.apache.lucene.index.ExitableDirectoryReader}
|
||||
|
@ -193,7 +197,7 @@ public class CloudExitableDirectoryReaderTest extends SolrCloudTestCase {
|
|||
params( "sort","query($q,1) asc"),
|
||||
params("rows","0", "facet","true", "facet.method", "enum", "facet.field", "name"),
|
||||
params("rows","0", "json.facet","{ ids: { type: range, field : num, start : 1, end : 99, gap : 9 }}")
|
||||
}; //add more cases here
|
||||
}; // add more cases here
|
||||
|
||||
params.add(cases[random().nextInt(cases.length)]);
|
||||
for (; ; creep*=1.5) {
|
||||
|
@ -218,13 +222,20 @@ public class CloudExitableDirectoryReaderTest extends SolrCloudTestCase {
|
|||
int numBites = atLeast(100);
|
||||
for(int bite=0; bite<numBites; bite++) {
|
||||
int boundary = random().nextInt(creep);
|
||||
boolean omitHeader = random().nextBoolean();
|
||||
try(Trap catchCount = catchCount(boundary)){
|
||||
params.set("omitHeader", "" + omitHeader);
|
||||
params.set("boundary", boundary);
|
||||
QueryResponse rsp = cluster.getSolrClient().query(COLLECTION,
|
||||
params);
|
||||
assertEquals(""+rsp, rsp.getStatus(), 0);
|
||||
assertNo500s(""+rsp);
|
||||
assertEquals(""+creep+" ticks were sucessful; trying "+boundary+" yields "+rsp,
|
||||
// without responseHeader, whether the response is partial or not can't be known
|
||||
// omitHeader=true used in request to ensure that no NPE exceptions are thrown
|
||||
if (omitHeader) {
|
||||
continue;
|
||||
}
|
||||
assertEquals("" + creep + " ticks were successful; trying " + boundary + " yields " + rsp,
|
||||
catchCount.hasCaught(), isPartial(rsp));
|
||||
}catch(AssertionError ae) {
|
||||
Trap.dumpLastStackTraces(log);
|
||||
|
|
|
@ -117,6 +117,10 @@ public class DistributedDebugComponentTest extends SolrJettyTestBase {
|
|||
query.set("distrib", "true");
|
||||
query.setFields("id", "text");
|
||||
query.set("shards", shard1 + "," + shard2);
|
||||
|
||||
if (random().nextBoolean()) {
|
||||
query.add("omitHeader", Boolean.toString(random().nextBoolean()));
|
||||
}
|
||||
QueryResponse response = collection1.query(query);
|
||||
NamedList<Object> track = (NamedList<Object>) response.getDebugMap().get("track");
|
||||
assertNotNull(track);
|
||||
|
@ -138,13 +142,6 @@ public class DistributedDebugComponentTest extends SolrJettyTestBase {
|
|||
assertElementsPresent((NamedList<String>)((NamedList<Object>)track.get("GET_FIELDS")).get(shard2),
|
||||
"QTime", "ElapsedTime", "RequestPurpose", "NumFound", "Response");
|
||||
|
||||
query.add("omitHeader", "true");
|
||||
response = collection1.query(query);
|
||||
assertNull("QTime is not included in the response when omitHeader is set to true",
|
||||
((NamedList<Object>)response.getDebugMap().get("track")).findRecursive("EXECUTE_QUERY", shard1, "QTime"));
|
||||
assertNull("QTime is not included in the response when omitHeader is set to true",
|
||||
((NamedList<Object>)response.getDebugMap().get("track")).findRecursive("GET_FIELDS", shard2, "QTime"));
|
||||
|
||||
query.setQuery("id:1");
|
||||
response = collection1.query(query);
|
||||
track = (NamedList<Object>) response.getDebugMap().get("track");
|
||||
|
|
|
@ -219,6 +219,15 @@ public class RangeFacetCloudTest extends SolrCloudTestCase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testStatsWithOmitHeader() throws Exception {
|
||||
// SOLR-13509: no NPE should be thrown when only stats are specified with omitHeader=true
|
||||
SolrQuery solrQuery = new SolrQuery("q", "*:*", "omitHeader", "true",
|
||||
"json.facet", "{unique_foo:\"unique(" + STR_FIELD+ ")\"}");
|
||||
final QueryResponse rsp = cluster.getSolrClient().query(solrQuery);
|
||||
// response shouldn't contain header as omitHeader is set to true
|
||||
assertNull(rsp.getResponseHeader());
|
||||
}
|
||||
|
||||
public void testInclude_Upper() throws Exception {
|
||||
for (boolean doSubFacet : Arrays.asList(false, true)) {
|
||||
|
|
|
@ -203,7 +203,27 @@ The default value of this parameter is blank, which causes no extra "explain inf
|
|||
|
||||
== timeAllowed Parameter
|
||||
|
||||
This parameter specifies the amount of time, in milliseconds, allowed for a search to complete. If this time expires before the search is complete, any partial results will be returned, but values such as `numFound`, <<faceting.adoc#faceting,facet>> counts, and result <<the-stats-component.adoc#the-stats-component,stats>> may not be accurate for the entire result set.
|
||||
This parameter specifies the amount of time, in milliseconds, allowed for a search to complete. If this time expires before the search is complete, any partial results will be returned, but values such as `numFound`, <<faceting.adoc#faceting,facet>> counts, and result <<the-stats-component.adoc#the-stats-component,stats>> may not be accurate for the entire result set. In case of expiration, if `omitHeader` isn't set to `true` the response header contains a special flag called `partialResults`.
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"responseHeader": {
|
||||
"status": 0,
|
||||
"zkConnected": true,
|
||||
"partialResults": true,
|
||||
"QTime": 20,
|
||||
"params": {
|
||||
"q": "*:*"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"numFound": 77,
|
||||
"start": 0,
|
||||
"docs": [ "..." ]
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
This value is only checked at the time of:
|
||||
|
||||
|
|
Loading…
Reference in New Issue