{"record":{"id":"0b584ca7bbc830fe","repo":"apache/iceberg","slug":"interrupted-while-shutting-down-pool-some-clients","errorCode":null,"errorMessage":"Interrupted while shutting down pool. Some clients may not be closed.","messagePattern":"Interrupted while shutting down pool\\. Some clients may not be closed\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/src/main/java/org/apache/iceberg/ClientPoolImpl.java","lineNumber":131,"sourceCode":"          synchronized (this) {\n            if (!clients.isEmpty()) {\n              C client = clients.removeFirst();\n              close(client);\n              currentSize -= 1;\n            }\n          }\n        }\n        if (clients.isEmpty() && currentSize > 0) {\n          // wake every second in case this missed the signal\n          synchronized (signal) {\n            signal.wait(1000);\n          }\n        }\n      }\n\n    } catch (InterruptedException e) {\n      Thread.currentThread().interrupt();\n      LOG.warn(\"Interrupted while shutting down pool. Some clients may not be closed.\", e);\n    }\n  }\n\n  private C get() throws InterruptedException {\n    Preconditions.checkState(!closed, \"Cannot get a client from a closed pool\");\n    while (true) {\n      if (!clients.isEmpty() || currentSize < poolSize) {\n        synchronized (this) {\n          if (!clients.isEmpty()) {\n            return clients.removeFirst();\n          } else if (currentSize < poolSize) {\n            C client = newClient();\n            currentSize += 1;\n            return client;\n          }\n        }\n      }\n      synchronized (signal) {","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/ClientPoolImpl.java#L113-L149","documentation":"ClientPoolImpl.close() waits (with a timeout) for queued client-connection tasks to finish while shutting down the pool. If the shutdown thread is interrupted while waiting, the pool logs this warning and gives up waiting, so some pooled clients may remain unclosed.","triggerScenarios":"Closing a ClientPool (directly or via finalize/try-with-resources) from a thread that is interrupted during the join/wait on queued client-close tasks.","commonSituations":"Cancelling a Spark/Flink task that owns the pool, JVM shutdown hooks interrupting threads, mismanaged executor lifecycle, or double-close during finalize racing with explicit close.","solutions":["Avoid interrupting threads that own the pool; let close() finish before cancelling work.","Close the pool explicitly (try-with-resources or lifecycle hook) instead of relying on finalize.","Restore the interrupt status in your own code and re-check pool state (isClosed()) before reuse.","Check for task cancellation storms; guard pool sharing across cancelled tasks."],"exampleFix":"// before\nnew Thread(() -> pool.close()).interrupt();\n// after\nThread t = new Thread(pool::close);\nt.start();\nt.join(); // do not interrupt while shutting down","handlingStrategy":"try-catch","validationCode":"if (pool.isClosed()) throw new IllegalStateException(\"Pool already closed\");","typeGuard":null,"tryCatchPattern":"try { pool.close(); } catch (RuntimeException e) { LOG.warn(\"pool close issue\", e); Thread.currentThread().interrupt(); }","preventionTips":["Close pools explicitly with try-with-resources, not via finalize","Do not interrupt threads that own or shut down pools","Share one pool per task rather than closing from cancellation handlers"],"tags":["concurrency","thread-interrupted","resource-lifecycle"],"backgroundTag":"thread-interrupted","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}