From f01b3e97d19c4b96ca9eb7d4e2a8647f68fbb969 Mon Sep 17 00:00:00 2001 From: Mikhail Khludnev Date: Sat, 9 Nov 2019 17:49:31 +0100 Subject: [PATCH] SOLR-13904: Make Analytics component sensitive to timeAllowed. --- solr/CHANGES.txt | 10 ++++-- .../analytics/AnalyticsRequestManager.java | 11 ++++++ .../analytics/TimeExceededStubException.java | 36 +++++++++++++++++++ .../stream/AnalyticsShardRequestManager.java | 10 ++++-- .../apache/solr/handler/AnalyticsHandler.java | 13 +++++++ .../handler/component/AnalyticsComponent.java | 4 +++ .../LegacyAbstractAnalyticsCloudTest.java | 32 +++++++++++++---- .../legacy/LegacyAbstractAnalyticsTest.java | 4 +++ .../legacy/LegacyNoFacetCloudTest.java | 8 ++--- .../facet/LegacyFieldFacetCloudTest.java | 7 ++-- .../LegacyFieldFacetExtrasCloudTest.java | 6 ++-- .../legacy/facet/LegacyFieldFacetTest.java | 17 +++++++++ .../solr/handler/component/SearchHandler.java | 2 +- .../src/common-query-parameters.adoc | 3 +- 14 files changed, 139 insertions(+), 24 deletions(-) create mode 100644 solr/contrib/analytics/src/java/org/apache/solr/analytics/TimeExceededStubException.java diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index f8400da0900..cf28fe1147e 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -87,7 +87,7 @@ Other Changes * SOLR-13655:Upgrade Collections.unModifiableSet to Set.of and Set.copyOf (Atri Sharma via Tomás Fernández Löbbe) -* SOLR-13797: SolrResourceLoader no longer caches bad results when asked for wrong type (Mike Drob) +* SOLR-13797: SolrResourceLoader no longer caches bad results when asked for wrong type (Mike Drob) ================== 8.4.0 ================== @@ -122,6 +122,8 @@ Upgrade Notes * SOLR-14025: VelocityResponseWriter has been hardened - only trusted configsets can render configset provided templates and rendering templates from request parameters has been removed. +* SOLR-13904: timeAllowed parameter is allowed to have 0 value (Houston Putman, Mikhail Khludnev) + New Features --------------------- * SOLR-13821: A Package store to store and load package artifacts (noble, Ishan Chattopadhyaya) @@ -142,7 +144,8 @@ New Features * SOLR-13912: Add 'countvals' aggregation in JSON FacetModule (hossman, Munendra S N) -* SOLR-12217: Support shards.preference in SolrJ for single shard collections. The parameter is now used by the CloudSolrClient and Streaming Expressions. (Houston Putman, Tomas Fernandez-Lobbe) +* SOLR-12217: Support shards.preference in SolrJ for single shard collections. The parameter + is now used by the CloudSolrClient and Streaming Expressions. (Houston Putman, Tomas Fernandez-Lobbe) * SOLR-14043: Allow the precision Stream Evaluator to operate on matrices (Joel bernstein) @@ -182,6 +185,9 @@ Improvements * SOLR-13957: Add sensible defaults for the facet, random, facet2D, timeseries, stats and update Streaming Expressions (Joel Bernstein) +* SOLR-13904: Analytic component abandon requests exceedig limit passed via timeAllowed parameter + (Houston Putman, Mikhail Khludnev). + Optimizations --------------------- (No changes) diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestManager.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestManager.java index f58bb6e0aa5..68ae155ce57 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestManager.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestManager.java @@ -54,6 +54,7 @@ public class AnalyticsRequestManager { public String analyticsRequest; public AnalyticsShardRequestManager shardStream; public boolean sendShards; + private boolean partialResults = false; /** * Create an manager with the given ungrouped expressions. This is straightforward in the new @@ -276,4 +277,14 @@ public class AnalyticsRequestManager { } return analyticsResponse; } + + public void setPartialResults(boolean b) { + this.partialResults=b; + } + + public boolean isPartialResults() { + return partialResults; + } + + } \ No newline at end of file diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/TimeExceededStubException.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/TimeExceededStubException.java new file mode 100644 index 00000000000..4b0eec85b09 --- /dev/null +++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/TimeExceededStubException.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.analytics; + +import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.response.SolrQueryResponse; + +@SuppressWarnings("serial") +public final class TimeExceededStubException extends SolrException { + private static final int HTTP_CODE = 524; + + public TimeExceededStubException(Throwable th) { + super(HTTP_CODE, + SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY, th); + setMetadata("cause", CommonParams.TIME_ALLOWED+" is exceeded"); + } + + public static boolean isIt(SolrException e) { + return e.code() == HTTP_CODE && SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY.equals(e.getMessage()); + } +} \ No newline at end of file diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/AnalyticsShardRequestManager.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/AnalyticsShardRequestManager.java index a8c50ef733f..6e2f2866e95 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/AnalyticsShardRequestManager.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/AnalyticsShardRequestManager.java @@ -31,10 +31,11 @@ import java.util.concurrent.Future; import org.apache.solr.analytics.AnalyticsRequestManager; import org.apache.solr.analytics.AnalyticsRequestParser; +import org.apache.solr.analytics.TimeExceededStubException; import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.impl.CloudSolrClient; -import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.impl.CloudSolrClient.Builder; +import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.common.SolrException; import org.apache.solr.common.cloud.ClusterState; @@ -154,7 +155,11 @@ public class AnalyticsShardRequestManager { for (Future f : futures) { SolrException e = f.get(); if (e != null) { - throw e; + if (TimeExceededStubException.isIt(e)) { + manager.setPartialResults(true); + } else { + throw e; + } } } } catch (InterruptedException e1) { @@ -188,6 +193,7 @@ public class AnalyticsShardRequestManager { solrParams.add(CommonParams.WT, AnalyticsShardResponseWriter.NAME); solrParams.add(CommonParams.Q, paramsIn.get(CommonParams.Q)); solrParams.add(CommonParams.FQ, paramsIn.getParams(CommonParams.FQ)); + solrParams.add(CommonParams.TIME_ALLOWED, paramsIn.get(CommonParams.TIME_ALLOWED,"-1")); solrParams.add(AnalyticsRequestParser.analyticsParamName, analyticsRequest); return solrParams; diff --git a/solr/contrib/analytics/src/java/org/apache/solr/handler/AnalyticsHandler.java b/solr/contrib/analytics/src/java/org/apache/solr/handler/AnalyticsHandler.java index 827f77eb53c..b289d1c9638 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/handler/AnalyticsHandler.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/handler/AnalyticsHandler.java @@ -18,12 +18,15 @@ package org.apache.solr.handler; import java.io.IOException; import java.util.ArrayList; + +import org.apache.lucene.index.ExitableDirectoryReader; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.solr.analytics.AnalyticsDriver; import org.apache.solr.analytics.AnalyticsRequestManager; import org.apache.solr.analytics.AnalyticsRequestParser; import org.apache.solr.analytics.ExpressionFactory; +import org.apache.solr.analytics.TimeExceededStubException; import org.apache.solr.analytics.stream.AnalyticsShardResponseParser; import org.apache.solr.client.solrj.io.ModelCache; import org.apache.solr.client.solrj.io.SolrClientCache; @@ -43,6 +46,7 @@ import org.apache.solr.search.QParser; import org.apache.solr.search.QParserPlugin; import org.apache.solr.search.QueryParsing; import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrQueryTimeoutImpl; import org.apache.solr.search.SyntaxError; import org.apache.solr.security.AuthorizationContext; import org.apache.solr.security.PermissionNameProvider; @@ -74,6 +78,11 @@ public class AnalyticsHandler extends RequestHandlerBase implements SolrCoreAwar } public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + + long timeAllowed = req.getParams().getLong(CommonParams.TIME_ALLOWED, -1L); + if (timeAllowed >= 0L) { + SolrQueryTimeoutImpl.set(timeAllowed); + } try { DocSet docs; try { @@ -95,6 +104,10 @@ public class AnalyticsHandler extends RequestHandlerBase implements SolrCoreAwar rsp.addResponse(new AnalyticsResponse(manager)); } catch (SolrException e) { rsp.addResponse(new AnalyticsResponse(e)); + } catch (ExitableDirectoryReader.ExitingReaderException e) { + rsp.addResponse(new AnalyticsResponse(new TimeExceededStubException(e))); + } finally { + SolrQueryTimeoutImpl.reset(); } } diff --git a/solr/contrib/analytics/src/java/org/apache/solr/handler/component/AnalyticsComponent.java b/solr/contrib/analytics/src/java/org/apache/solr/handler/component/AnalyticsComponent.java index 6c69623157c..5e676042495 100644 --- a/solr/contrib/analytics/src/java/org/apache/solr/handler/component/AnalyticsComponent.java +++ b/solr/contrib/analytics/src/java/org/apache/solr/handler/component/AnalyticsComponent.java @@ -27,6 +27,7 @@ import org.apache.solr.analytics.util.AnalyticsResponseHeadings; import org.apache.solr.analytics.util.OldAnalyticsParams; import org.apache.solr.analytics.util.OldAnalyticsRequestConverter; import org.apache.solr.common.util.NamedList; +import org.apache.solr.response.SolrQueryResponse; /** * Computes analytics requests. @@ -143,6 +144,9 @@ public class AnalyticsComponent extends SearchComponent { } else { rb.rsp.add(AnalyticsResponseHeadings.COMPLETED_HEADER, reqManager.createResponse()); } + if (reqManager.isPartialResults()) { + rb.rsp.getResponseHeader().asShallowMap().put(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY,true); + } } super.finishStage(rb); diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/LegacyAbstractAnalyticsCloudTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/LegacyAbstractAnalyticsCloudTest.java index 32f26ba95a9..c9c40fce46c 100644 --- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/LegacyAbstractAnalyticsCloudTest.java +++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/LegacyAbstractAnalyticsCloudTest.java @@ -33,8 +33,9 @@ import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.cloud.SolrCloudTestCase; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.NamedList; -import org.junit.After; -import org.junit.Before; +import org.apache.solr.response.SolrQueryResponse; +import org.junit.AfterClass; +import org.junit.BeforeClass; public class LegacyAbstractAnalyticsCloudTest extends SolrCloudTestCase { @@ -42,8 +43,8 @@ public class LegacyAbstractAnalyticsCloudTest extends SolrCloudTestCase { protected static final int TIMEOUT = DEFAULT_TIMEOUT; protected static final String id = "id"; - @Before - public void setupCollection() throws Exception { + @BeforeClass + public static void setupCollection() throws Exception { configureCluster(4) .addConfig("conf", configset("cloud-analytics")) .configure(); @@ -52,8 +53,8 @@ public class LegacyAbstractAnalyticsCloudTest extends SolrCloudTestCase { cluster.waitForActiveCollection(COLLECTIONORALIAS, 2, 2); } - @After - public void teardownCollection() throws Exception { + @AfterClass + public static void teardownCollection() throws Exception { shutdownCluster(); } @@ -85,6 +86,7 @@ public class LegacyAbstractAnalyticsCloudTest extends SolrCloudTestCase { } } + protected NamedList queryLegacyCloudAnalytics(String[] testParams) throws SolrServerException, IOException, InterruptedException, TimeoutException { ModifiableSolrParams params = new ModifiableSolrParams(); params.set("q", "*:*"); @@ -97,7 +99,23 @@ public class LegacyAbstractAnalyticsCloudTest extends SolrCloudTestCase { cluster.waitForAllNodes(10000); QueryRequest qreq = new QueryRequest(params); QueryResponse resp = qreq.process(cluster.getSolrClient(), COLLECTIONORALIAS); - return resp.getResponse(); + final NamedList response = resp.getResponse(); + assertRequestTimeout(params); + return response; + } + + /** caveat: the given params are modified */ + protected void assertRequestTimeout(ModifiableSolrParams params) + throws IOException, InterruptedException, TimeoutException, SolrServerException { + params.set("timeAllowed", 0); + cluster.waitForAllNodes(10000); + final QueryResponse maybeTimeout = new QueryRequest(params).process(cluster.getSolrClient(), COLLECTIONORALIAS); + assertEquals(maybeTimeout.getHeader() + "", 0, maybeTimeout.getStatus()); + final Boolean partial = maybeTimeout.getHeader() + .getBooleanArg(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY); + assertNotNull("No partial results header returned", partial); + assertTrue("The request " + params + + "was not stopped halfway through, the partial results header was false", partial); } @SuppressWarnings("unchecked") diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/LegacyAbstractAnalyticsTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/LegacyAbstractAnalyticsTest.java index 2ac3ed9f1d3..2f78203e11d 100644 --- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/LegacyAbstractAnalyticsTest.java +++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/LegacyAbstractAnalyticsTest.java @@ -209,6 +209,10 @@ public class LegacyAbstractAnalyticsTest extends SolrTestCaseJ4 { return SolrTestCaseJ4.req( ObjectArrays.concat(BASEPARMS, args,String.class) ); } + public static SolrQueryRequest request(String[] args, String... additional){ + return SolrTestCaseJ4.req( ObjectArrays.concat(BASEPARMS, args,String.class), additional ); + } + public static String[] fileToStringArr(Class clazz, String fileName) throws FileNotFoundException { InputStream in = clazz.getResourceAsStream("/solr/analytics/legacy/" + fileName); if (in == null) throw new FileNotFoundException("Resource not found: " + fileName); diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/LegacyNoFacetCloudTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/LegacyNoFacetCloudTest.java index 56a7a72d655..dbc9522aace 100644 --- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/LegacyNoFacetCloudTest.java +++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/LegacyNoFacetCloudTest.java @@ -21,7 +21,7 @@ import java.util.List; import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.common.util.NamedList; -import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; public class LegacyNoFacetCloudTest extends LegacyAbstractAnalyticsCloudTest { @@ -57,8 +57,8 @@ public class LegacyNoFacetCloudTest extends LegacyAbstractAnalyticsCloudTest { static ArrayList stringTestStart; static long stringMissing = 0; - @Before - public void populate() throws Exception { + @BeforeClass + public static void populate() throws Exception { intTestStart = new ArrayList<>(); longTestStart = new ArrayList<>(); floatTestStart = new ArrayList<>(); @@ -146,7 +146,7 @@ public class LegacyNoFacetCloudTest extends LegacyAbstractAnalyticsCloudTest { //Double Double doubleResult = getValue(response, "sr", "double_dd"); - Double doubleTest = (Double) calculateNumberStat(doubleTestStart, "sum"); + Double doubleTest = (Double) calculateNumberStat(doubleTestStart, "sum"); assertEquals(responseStr, doubleResult,doubleTest); } diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/facet/LegacyFieldFacetCloudTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/facet/LegacyFieldFacetCloudTest.java index ec205768f45..78c0a0a770f 100644 --- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/facet/LegacyFieldFacetCloudTest.java +++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/facet/LegacyFieldFacetCloudTest.java @@ -24,10 +24,9 @@ import java.util.List; import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.common.util.NamedList; import org.junit.Assert; -import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; - public class LegacyFieldFacetCloudTest extends LegacyAbstractAnalyticsFacetCloudTest { public static final int INT = 71; public static final int LONG = 36; @@ -85,8 +84,8 @@ public class LegacyFieldFacetCloudTest extends LegacyAbstractAnalyticsFacetCloud private static ArrayList> multiDateTestStart; private static ArrayList multiDateTestMissing; - @Before - public void beforeTest() throws Exception { + @BeforeClass + public static void beforeTest() throws Exception { //INT intDateTestStart = new ArrayList<>(); diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/facet/LegacyFieldFacetExtrasCloudTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/facet/LegacyFieldFacetExtrasCloudTest.java index 69f10fe4492..cf28f64c898 100644 --- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/facet/LegacyFieldFacetExtrasCloudTest.java +++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/facet/LegacyFieldFacetExtrasCloudTest.java @@ -24,7 +24,7 @@ import java.util.List; import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.common.util.NamedList; -import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; public class LegacyFieldFacetExtrasCloudTest extends LegacyAbstractAnalyticsFacetCloudTest { @@ -42,8 +42,8 @@ public class LegacyFieldFacetExtrasCloudTest extends LegacyAbstractAnalyticsFace static ArrayList> intDoubleTestStart; static ArrayList> intStringTestStart; - @Before - public void beforeTest() throws Exception { + @BeforeClass + public static void beforeTest() throws Exception { //INT intLongTestStart = new ArrayList<>(); diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/facet/LegacyFieldFacetTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/facet/LegacyFieldFacetTest.java index d3943808b56..843c605d70a 100644 --- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/facet/LegacyFieldFacetTest.java +++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/legacy/facet/LegacyFieldFacetTest.java @@ -21,6 +21,9 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -417,6 +420,20 @@ public class LegacyFieldFacetTest extends LegacyAbstractAnalyticsFacetTest{ setResponse(h.query(request(reqFacetParamas))); } + @Test + public void timeAllowedTest() throws Exception { + String query = "int_id: [0 TO " + random().nextInt(INT) + "] AND long_ld: [0 TO " + random().nextInt(LONG) + "]"; + try (SolrQueryRequest req = req(fileToStringArr(LegacyFieldFacetTest.class, fileName), "q", query, "timeAllowed", "0", "cache", "false")) { + SolrQueryResponse resp = h.queryAndResponse(req.getParams().get(CommonParams.QT), req); + + Assert.assertEquals(resp.getResponseHeader().toString(), 0, resp.getResponseHeader().get("status")); + Boolean partialResults = resp.getResponseHeader().getBooleanArg(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY); + assertNotNull("No partial results header returned: " + resp.getResponseHeader().toString(), partialResults); + assertTrue("The request was not stopped halfway through, the partial results header was false", partialResults); + } + } + + @SuppressWarnings("unchecked") @Test public void sumTest() throws Exception { diff --git a/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java b/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java index f617fcbb0a7..2d4d63dcaba 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java @@ -316,7 +316,7 @@ public class SearchHandler extends RequestHandlerBase implements SolrCoreAware, // a normal non-distributed request long timeAllowed = req.getParams().getLong(CommonParams.TIME_ALLOWED, -1L); - if (timeAllowed > 0L) { + if (timeAllowed >= 0L) { SolrQueryTimeoutImpl.set(timeAllowed); } try { diff --git a/solr/solr-ref-guide/src/common-query-parameters.adoc b/solr/solr-ref-guide/src/common-query-parameters.adoc index 671000699cf..82d69d653d7 100644 --- a/solr/solr-ref-guide/src/common-query-parameters.adoc +++ b/solr/solr-ref-guide/src/common-query-parameters.adoc @@ -229,8 +229,9 @@ This value is only checked at the time of: . Query Expansion, and . Document collection +. Doc Values reading -As this check is periodically performed, the actual time for which a request can be processed before it is aborted would be marginally greater than or equal to the value of `timeAllowed`. If the request consumes more time in other stages, custom components, etc., this parameter is not expected to abort the request. +As this check is periodically performed, the actual time for which a request can be processed before it is aborted would be marginally greater than or equal to the value of `timeAllowed`. If the request consumes more time in other stages, custom components, etc., this parameter is not expected to abort the request. Regular search, JSON Facet and Analytics handler abandon requests in according to this parameter. == segmentTerminateEarly Parameter