{"record":{"id":"1bd8cdb69643b9f4","repo":"apache/druid","slug":"interrupted-while-waiting-for-mount-1bd8cd","errorCode":null,"errorMessage":"Interrupted while waiting for mount","messagePattern":"Interrupted while waiting for mount","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"warning","filePath":"server/src/main/java/org/apache/druid/segment/loading/PartialSegmentMetadataCacheEntry.java","lineNumber":1062,"sourceCode":"          allPresent = false;\n          break;\n        }\n      }\n      if (allPresent) {\n        restorable.add(bundleName);\n      }\n    }\n    return restorable;\n  }\n\n  private static void awaitMount(SettableFuture<Void> future) throws IOException\n  {\n    try {\n      future.get();\n    }\n    catch (InterruptedException e) {\n      Thread.currentThread().interrupt();\n      throw new IOException(\"Interrupted while waiting for mount\", e);\n    }\n    catch (ExecutionException e) {\n      final Throwable cause = e.getCause() == null ? e : e.getCause();\n      switch (cause) {\n        case IOException ioException -> throw ioException;\n        case RuntimeException runtimeException -> throw runtimeException;\n        case Error error -> throw error;\n        default -> throw DruidException.defensive(e, \"mount failed\");\n      }\n    }\n  }\n\n  /**\n   * Triggers cleanup of this entry. If any references acquired via {@link #acquireMetadataReference()} are still\n   * outstanding, the actual unmap-and-delete work is deferred until the last reference releases; in that case this\n   * method returns immediately and {@link #doActualUnmount} will fire later on the thread that closes the last\n   * reference. With no outstanding references, cleanup runs synchronously on the caller's thread.\n   * <p>","sourceCodeStart":1044,"sourceCodeEnd":1080,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/segment/loading/PartialSegmentMetadataCacheEntry.java#L1044-L1080","documentation":"PartialSegmentMetadataCacheEntry.awaitMount waits on the Future completing the metadata mount. An interrupt while blocked in future.get() is converted to an IOException after re-asserting the thread's interrupt flag, since the mount outcome is unknowable at that point.","triggerScenarios":"A thread calling mount() on a PartialSegmentMetadataCacheEntry receives Thread.interrupt() while blocked in future.get() awaiting the async metadata mount.","commonSituations":"Node shutdown or task cancellation while a partial-load rule mount is in progress; coordinator issuing concurrent drops and loads; thread pool termination during reload.","solutions":["Respect the restored interrupt flag and abort the current load; rely on the coordinator's load queue to retry the segment.","Make shutdown paths drain in-flight mounts before interrupting worker threads.","Investigate sources of spurious interrupts if this occurs outside shutdown."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (Thread.currentThread().isInterrupted()) {\n  throw new InterruptedException();\n}","typeGuard":null,"tryCatchPattern":"try {\n  metadata.mount(location);\n} catch (IOException e) {\n  if (e.getCause() instanceof InterruptedException || Thread.currentThread().isInterrupted()) {\n    Thread.currentThread().interrupt();\n    return; // abort; load queue will retry\n  }\n  throw e;\n}","preventionTips":["Preserve the interrupt flag when handling mount IOExceptions.","Gracefully stop schedulers/executors before interrupting mount threads.","Check logs for unexpected interrupt sources if this occurs outside shutdown.","Rely on coordinator load-queue retries rather than tight retry loops."],"tags":["interruption","threading","segment-mount","cache"],"backgroundTag":"operation-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"}