From 8899affe48971f7a1e50683081132dbe887b7b7f Mon Sep 17 00:00:00 2001 From: Gian Merlino Date: Tue, 9 Aug 2016 10:50:56 -0700 Subject: [PATCH] Introduce standardized "Resource limit exceeded" error. (#3338) Fixes #3336. --- docs/content/querying/querying.md | 1 + .../query/QueryInterruptedException.java | 3 ++ .../query/ResourceLimitExceededException.java | 34 +++++++++++++++++++ .../query/groupby/GroupByQueryHelper.java | 3 +- .../GroupByMergingQueryRunnerV2.java | 3 +- .../query/QueryInterruptedExceptionTest.java | 12 +++++++ .../query/groupby/GroupByQueryRunnerTest.java | 5 +-- 7 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 processing/src/main/java/io/druid/query/ResourceLimitExceededException.java diff --git a/docs/content/querying/querying.md b/docs/content/querying/querying.md index a1e9d83fd09..3bb367f6c1a 100644 --- a/docs/content/querying/querying.md +++ b/docs/content/querying/querying.md @@ -97,4 +97,5 @@ Possible codes for the *error* field include: |`Query timeout`|The query timed out.| |`Query interrupted`|The query was interrupted, possibly due to JVM shutdown.| |`Query cancelled`|The query was cancelled through the query cancellation API.| +|`Resource limit exceeded`|The query exceeded a configured resource limit (e.g. groupBy maxResults).| |`Unknown exception`|Some other exception occurred. Check errorMessage and errorClass for details, although keep in mind that the contents of those fields are free-form and may change from release to release.| diff --git a/processing/src/main/java/io/druid/query/QueryInterruptedException.java b/processing/src/main/java/io/druid/query/QueryInterruptedException.java index 7b5326a3764..8f8fe2cc34d 100644 --- a/processing/src/main/java/io/druid/query/QueryInterruptedException.java +++ b/processing/src/main/java/io/druid/query/QueryInterruptedException.java @@ -44,6 +44,7 @@ public class QueryInterruptedException extends RuntimeException public static final String QUERY_INTERRUPTED = "Query interrupted"; public static final String QUERY_TIMEOUT = "Query timeout"; public static final String QUERY_CANCELLED = "Query cancelled"; + public static final String RESOURCE_LIMIT_EXCEEDED = "Resource limit exceeded"; public static final String UNKNOWN_EXCEPTION = "Unknown exception"; private final String errorCode; @@ -118,6 +119,8 @@ public class QueryInterruptedException extends RuntimeException return QUERY_CANCELLED; } else if (e instanceof TimeoutException) { return QUERY_TIMEOUT; + } else if (e instanceof ResourceLimitExceededException) { + return RESOURCE_LIMIT_EXCEEDED; } else { return UNKNOWN_EXCEPTION; } diff --git a/processing/src/main/java/io/druid/query/ResourceLimitExceededException.java b/processing/src/main/java/io/druid/query/ResourceLimitExceededException.java new file mode 100644 index 00000000000..1ed038840ba --- /dev/null +++ b/processing/src/main/java/io/druid/query/ResourceLimitExceededException.java @@ -0,0 +1,34 @@ +/* + * Licensed to Metamarkets Group Inc. (Metamarkets) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Metamarkets 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 io.druid.query; + +/** + * Exception indicating that an operation failed because it exceeded some configured resource limit. + * + * This is used as a marker exception by {@link QueryInterruptedException} to report the "Resource limit exceeded" + * error code. + */ +public class ResourceLimitExceededException extends RuntimeException +{ + public ResourceLimitExceededException(String message) + { + super(message); + } +} diff --git a/processing/src/main/java/io/druid/query/groupby/GroupByQueryHelper.java b/processing/src/main/java/io/druid/query/groupby/GroupByQueryHelper.java index 89eb145f3c5..c71308f5c6f 100644 --- a/processing/src/main/java/io/druid/query/groupby/GroupByQueryHelper.java +++ b/processing/src/main/java/io/druid/query/groupby/GroupByQueryHelper.java @@ -31,6 +31,7 @@ import io.druid.data.input.MapBasedInputRow; import io.druid.data.input.MapBasedRow; import io.druid.data.input.Row; import io.druid.granularity.QueryGranularity; +import io.druid.query.ResourceLimitExceededException; import io.druid.query.aggregation.AggregatorFactory; import io.druid.query.dimension.DimensionSpec; import io.druid.segment.incremental.IncrementalIndex; @@ -132,7 +133,7 @@ public class GroupByQueryHelper ); } catch (IndexSizeExceededException e) { - throw new ISE(e.getMessage()); + throw new ResourceLimitExceededException(e.getMessage()); } } else { throw new ISE("Unable to accumulate something of type [%s]", in.getClass()); diff --git a/processing/src/main/java/io/druid/query/groupby/epinephelinae/GroupByMergingQueryRunnerV2.java b/processing/src/main/java/io/druid/query/groupby/epinephelinae/GroupByMergingQueryRunnerV2.java index cffa996af41..1be7b4ce048 100644 --- a/processing/src/main/java/io/druid/query/groupby/epinephelinae/GroupByMergingQueryRunnerV2.java +++ b/processing/src/main/java/io/druid/query/groupby/epinephelinae/GroupByMergingQueryRunnerV2.java @@ -50,6 +50,7 @@ import io.druid.query.QueryContextKeys; import io.druid.query.QueryInterruptedException; import io.druid.query.QueryRunner; import io.druid.query.QueryWatcher; +import io.druid.query.ResourceLimitExceededException; import io.druid.query.aggregation.AggregatorFactory; import io.druid.query.groupby.GroupByQuery; import io.druid.query.groupby.GroupByQueryConfig; @@ -308,7 +309,7 @@ public class GroupByMergingQueryRunnerV2 implements QueryRunner for (Boolean result : results) { if (!result) { future.cancel(true); - throw new ISE("Grouping resources exhausted"); + throw new ResourceLimitExceededException("Grouping resources exhausted"); } } } diff --git a/processing/src/test/java/io/druid/query/QueryInterruptedExceptionTest.java b/processing/src/test/java/io/druid/query/QueryInterruptedExceptionTest.java index e13dcea94bc..62c36e45231 100644 --- a/processing/src/test/java/io/druid/query/QueryInterruptedExceptionTest.java +++ b/processing/src/test/java/io/druid/query/QueryInterruptedExceptionTest.java @@ -45,6 +45,10 @@ public class QueryInterruptedExceptionTest Assert.assertEquals("Query timeout", new QueryInterruptedException(new TimeoutException()).getErrorCode()); Assert.assertEquals("Unknown exception", new QueryInterruptedException(null).getErrorCode()); Assert.assertEquals("Unknown exception", new QueryInterruptedException(new ISE("Something bad!")).getErrorCode()); + Assert.assertEquals( + "Resource limit exceeded", + new QueryInterruptedException(new ResourceLimitExceededException("too many!")).getErrorCode() + ); Assert.assertEquals( "Unknown exception", new QueryInterruptedException(new QueryInterruptedException(new ISE("Something bad!"))).getErrorCode() @@ -74,6 +78,10 @@ public class QueryInterruptedExceptionTest null, new QueryInterruptedException(null).getMessage() ); + Assert.assertEquals( + "too many!", + new QueryInterruptedException(new ResourceLimitExceededException("too many!")).getMessage() + ); Assert.assertEquals( "Something bad!", new QueryInterruptedException(new ISE("Something bad!")).getMessage() @@ -103,6 +111,10 @@ public class QueryInterruptedExceptionTest "java.util.concurrent.TimeoutException", new QueryInterruptedException(new TimeoutException()).getErrorClass() ); + Assert.assertEquals( + "io.druid.query.ResourceLimitExceededException", + new QueryInterruptedException(new ResourceLimitExceededException("too many!")).getErrorClass() + ); Assert.assertEquals( null, new QueryInterruptedException(null).getErrorClass() diff --git a/processing/src/test/java/io/druid/query/groupby/GroupByQueryRunnerTest.java b/processing/src/test/java/io/druid/query/groupby/GroupByQueryRunnerTest.java index 33498176bbc..f147a1f8323 100644 --- a/processing/src/test/java/io/druid/query/groupby/GroupByQueryRunnerTest.java +++ b/processing/src/test/java/io/druid/query/groupby/GroupByQueryRunnerTest.java @@ -50,6 +50,7 @@ import io.druid.query.Query; import io.druid.query.QueryRunner; import io.druid.query.QueryRunnerTestHelper; import io.druid.query.QueryToolChest; +import io.druid.query.ResourceLimitExceededException; import io.druid.query.Result; import io.druid.query.aggregation.AggregatorFactory; import io.druid.query.aggregation.CountAggregatorFactory; @@ -875,7 +876,7 @@ public class GroupByQueryRunnerTest List expectedResults = null; if (config.getDefaultStrategy().equals(GroupByStrategySelector.STRATEGY_V1)) { - expectedException.expect(ISE.class); + expectedException.expect(ResourceLimitExceededException.class); } else { expectedResults = Arrays.asList( GroupByQueryRunnerTestHelper.createExpectedRow("2011-04-01", "alias", "automotive", "rows", 1L, "idx", 135L), @@ -968,7 +969,7 @@ public class GroupByQueryRunnerTest List expectedResults = null; if (config.getDefaultStrategy().equals(GroupByStrategySelector.STRATEGY_V2)) { - expectedException.expect(ISE.class); + expectedException.expect(ResourceLimitExceededException.class); expectedException.expectMessage("Grouping resources exhausted"); } else { expectedResults = Arrays.asList(