You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by mk...@apache.org on 2019/12/11 20:49:09 UTC

[lucene-solr] branch master updated: SOLR-13904: Make Analytics component sensitive to timeAllowed.

This is an automated email from the ASF dual-hosted git repository.

mkhl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git


The following commit(s) were added to refs/heads/master by this push:
     new f01b3e9  SOLR-13904: Make Analytics component sensitive to timeAllowed.
f01b3e9 is described below

commit f01b3e97d19c4b96ca9eb7d4e2a8647f68fbb969
Author: Mikhail Khludnev <mk...@apache.org>
AuthorDate: Sat Nov 9 17:49:31 2019 +0100

    SOLR-13904: Make Analytics component sensitive to timeAllowed.
---
 solr/CHANGES.txt                                   | 10 ++++--
 .../solr/analytics/AnalyticsRequestManager.java    | 11 +++++++
 .../solr/analytics/TimeExceededStubException.java  | 36 ++++++++++++++++++++++
 .../stream/AnalyticsShardRequestManager.java       | 10 ++++--
 .../org/apache/solr/handler/AnalyticsHandler.java  | 13 ++++++++
 .../solr/handler/component/AnalyticsComponent.java |  4 +++
 .../legacy/LegacyAbstractAnalyticsCloudTest.java   | 32 ++++++++++++++-----
 .../legacy/LegacyAbstractAnalyticsTest.java        |  4 +++
 .../analytics/legacy/LegacyNoFacetCloudTest.java   |  8 ++---
 .../legacy/facet/LegacyFieldFacetCloudTest.java    |  7 ++---
 .../facet/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(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index f8400da..cf28fe1 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 f58bb6e..68ae155 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 0000000..4b0eec8
--- /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 a8c50ef..6e2f286 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<SolrException> 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 827f77e..b289d1c 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 6c69623..5e67604 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 32f26ba..c9c40fc 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<Object> 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<Object> 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 2ac3ed9..2f78203 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 56a7a72..dbc9522 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<String> 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 ec20576..78c0a0a 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<ArrayList<Integer>> multiDateTestStart;
   private static ArrayList<Long> 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 69f10fe..cf28f64 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<ArrayList<Integer>> intDoubleTestStart;
   static ArrayList<ArrayList<Integer>> 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 d394380..843c605 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 f617fcb..2d4d63d 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 6710006..82d69d6 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