apache/druid · warning · IOException

Thread Interrupted while flushing

Error message

Thread Interrupted while flushing

What it means

flush() blocks on emittedBatchCounter.awaitCount, which can throw InterruptedException if the calling thread is interrupted while waiting for the batch to be emitted. The code re-interrupts the thread and wraps the cause in IOException('Thread Interrupted while flushing'). It signals the flush did not complete because the caller was interrupted, not because of a network problem.

Solutions

  1. Avoid interrupting threads that are flushing; complete flushes before shutdownNow().
  2. Catch IOException and check the cause is InterruptedException; restore interrupt status and proceed with shutdown.
  3. Use close() in a non-interruptible shutdown phase, or give it a dedicated thread.
  4. Reduce flush wait time (flushTimeOut) so flushes finish before interruption deadlines.

Example fix

// before
executor.shutdownNow(); // interrupts in-flight flush
// after
emitter.close();
executor.shutdownNow();
Defensive patterns

Strategy: try-catch

Try / catch

try {
  emitter.flush();
} catch (IOException e) {
  if (e.getCause() instanceof InterruptedException) {
    Thread.currentThread().interrupt();
    log.debug("Flush interrupted; deferring to shutdown");
  }
}

Prevention

When it happens

Trigger: A thread calling flush() or close() is interrupted (Thread.interrupt()) mid-wait — e.g. Druid Lifecycle stop, executor shutdownNow(), or task cancellation during shutdown.

Common situations: JVM shutdown hooks or supervisors interrupting worker threads while they flush telemetry; ScheduledExecutorService shutdownNow() cancelling a periodic flush; Druid task kill interrupting peon threads.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/3820ddbb6de22af2. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/java/util/emitter/core/HttpPostEmitter.java:434

  {
    if (batch == null) {
      return;
    }
    batch.seal();
    try {
      // This check doesn't always awaits for this exact batch to be emitted, because another batch could be dropped
      // from the queue ahead of this one, in limitBuffersToEmitSize(). But there is no better way currently to wait for
      // the exact batch, and it's not that important.
      emittedBatchCounter.awaitCount(batch.batchNumber, config.getFlushTimeOut(), TimeUnit.MILLISECONDS);
    }
    catch (TimeoutException e) {
      String message = StringUtils.format("Timed out after [%d] millis during flushing", config.getFlushTimeOut());
      throw new IOException(message, e);
    }
    catch (InterruptedException e) {
      log.debug("Thread Interrupted");
      Thread.currentThread().interrupt();
      throw new IOException("Thread Interrupted while flushing", e);
    }
  }

  @Override
  @LifecycleStop
  public void close() throws IOException
  {
    synchronized (startLock) {
      if (running) {
        running = false;
        Object lastBatch = concurrentBatch.getAndSet(null);
        if (lastBatch instanceof Batch) {
          flush((Batch) lastBatch);
        }
        emittingThread.shuttingDown = true;
        // EmittingThread is interrupted after the last batch is flushed.
        emittingThread.interrupt();
      }

View on GitHub (pinned to 9b90983fd2)