{"record":{"id":"57551870f657db3c","repo":"apache/druid","slug":"query-interrupted-cancelling-pending-results-for-575518","errorCode":null,"errorMessage":"Query interrupted, cancelling pending results for query [%s]","messagePattern":"Query interrupted, cancelling pending results for query \\[(.+?)\\]","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByMergingQueryRunner.java","lineNumber":392,"sourceCode":"      if (queryWatcher != null) {\n        queryWatcher.registerQueryFuture(query, future);\n      }\n\n      if (hasTimeout && timeout <= 0) {\n        throw new QueryTimeoutException();\n      }\n\n      final List<AggregateResult> results = hasTimeout ? future.get(timeout, TimeUnit.MILLISECONDS) : future.get();\n\n      for (AggregateResult result : results) {\n        if (!result.isOk()) {\n          GuavaUtils.cancelAll(true, future, futures);\n          throw new ResourceLimitExceededException(result.getReason());\n        }\n      }\n    }\n    catch (InterruptedException | CancellationException e) {\n      log.noStackTrace().warn(e, \"Query interrupted, cancelling pending results for query [%s]\", query.getId());\n      GuavaUtils.cancelAll(true, future, futures);\n      throw new QueryInterruptedException(e);\n    }\n    catch (QueryTimeoutException | TimeoutException e) {\n      log.noStackTrace().warn(e, \"Query timeout, cancelling pending results for query [%s]\", query.getId());\n      GuavaUtils.cancelAll(true, future, futures);\n      throw new QueryTimeoutException(StringUtils.nonStrictFormat(\"Query [%s] timed out\", query.getId()));\n    }\n    catch (ExecutionException e) {\n      log.noStackTrace().warn(e, \"Query error, cancelling pending results for query [%s]\", query.getId());\n      GuavaUtils.cancelAll(true, future, futures);\n      Throwable cause = e.getCause();\n      // Nested per-segment future timeout\n      if (cause instanceof TimeoutException) {\n        throw new QueryTimeoutException(StringUtils.nonStrictFormat(\"Query timeout, cancelling pending results for query [%s]. Per-segment timeout exceeded.\", query.getId()));\n      }\n      throw new RuntimeException(e);\n    }","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByMergingQueryRunner.java#L374-L410","documentation":"GroupByMergingQueryRunner.waitForFutureCompletion blocks on per-segment group-by futures and, on InterruptedException or CancellationException, cancels all outstanding futures and throws QueryInterruptedException after logging the query id. Like the chained runner, this is the cancellation/interruption cleanup path for the group-by v2 merging stage.","triggerScenarios":"While waitForFutureCompletion waits on futures: the query is cancelled (cancellation endpoint or client disconnect propagation), or the merging thread is interrupted (broker shutdown, interrupt-based timeout).","commonSituations":"User cancels a heavy group-by query; broker or historical shutdown during merge; client timeout closes the HTTP connection, propagating cancellation to the merge phase.","solutions":["If the query was cancelled intentionally, this log is expected — no action needed","Trace the cancellation source in broker logs (client disconnect vs explicit DELETE on /druid/v2)","Tune group-by performance (groupBy maxOnDiskStorage, buffer sizes, numThreads) so queries finish before client-side timeouts trigger cancellation","Set client read timeouts larger than query context timeout so the server isn't interrupted mid-merge"],"exampleFix":"// before: client timeout shorter than query timeout\n// curl --max-time 30 ... while context timeout is 300s\n// after: align timeouts\n// curl --max-time 300 ... and {\"context\":{\"timeout\":300000}}","handlingStrategy":"try-catch","validationCode":"// Bound query cost before submitting: check the interval and granularity are narrow\nif (interval.toDurationMillis() > maxAllowedScanMillis) {\n  throw new IllegalArgumentException(\"Query interval too large for group-by\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  result = runner.run(queryPlus, responseContext).toList();\n} catch (QueryInterruptedException e) {\n  if (e.getCause() instanceof CancellationException || e.getCause() instanceof InterruptedException) {\n    // treat as cancellation, release partial results\n  } else {\n    throw e;\n  }\n}","preventionTips":["Set client read timeouts >= query context timeout to avoid interrupting the merge phase","Tune group-by buffers (druid.query.groupBy.maxOnDiskStorage, buffer-group size) so merges complete promptly","Cancel heavy queries explicitly rather than letting client disconnects race completion","Ensure brokers are not restarted mid-query (maintenance windows)"],"tags":["query-cancellation","group-by","druid","interrupt"],"backgroundTag":"thread-interrupted","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}