{"record":{"id":"503e71bab57a5bb1","repo":"apache/iceberg","slug":"interrupted-during-isheld-503e71","errorCode":null,"errorMessage":"Interrupted during isHeld","messagePattern":"Interrupted during isHeld","errorType":"exception","errorClass":"UncheckedInterruptedException","httpStatus":null,"severity":"error","filePath":"flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/maintenance/api/JdbcLockFactory.java","lineNumber":227,"sourceCode":"    }\n\n    @SuppressWarnings(\"checkstyle:NestedTryDepth\")\n    @Override\n    public boolean isHeld() {\n      try {\n        return pool.run(\n            conn -> {\n              try (PreparedStatement sql = conn.prepareStatement(GET_LOCK_SQL)) {\n                sql.setString(1, type.key);\n                sql.setString(2, lockId);\n                try (ResultSet rs = sql.executeQuery()) {\n                  return rs.next();\n                }\n              }\n            });\n      } catch (InterruptedException e) {\n        Thread.currentThread().interrupt();\n        throw new UncheckedInterruptedException(e, \"Interrupted during isHeld\");\n      } catch (SQLException e) {\n        // SQL exception happened when getting lock information\n        throw new UncheckedSQLException(e, \"Failed to check the state of the lock %s\", this);\n      }\n    }\n\n    @SuppressWarnings(\"checkstyle:NestedTryDepth\")\n    @Override\n    public void unlock() {\n      try {\n        // Possible concurrency issue:\n        // - `unlock` and `tryLock` happens at the same time when there is an existing lock\n        //\n        // Steps:\n        // 1. `unlock` removes the lock in the database, but there is a temporary connection failure\n        // 2. `lock` finds that there is no lock, so creates a new lock\n        // 3. `unlock` retries the lock removal and removes the new lock\n        //","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/maintenance/api/JdbcLockFactory.java#L209-L245","documentation":"JdbcLockFactory's JdbcLock.isHeld() queries the JDBC catalog's lock table to determine whether this trigger holds its lock. If the thread waiting on the JDBC query is interrupted, the interruption is re-asserted and an UncheckedInterruptedException wrapping the original InterruptedException is thrown. This converts the checked interruption into a runtime exception so lock-state checks can fail fast in maintenance trigger threads.","triggerScenarios":"Calling tryLock (which calls isHeld) on a JdbcLock when the executing thread is interrupted while blocked on the JDBC query (Tasks.foreach lambda doing rs.next() against the lock table).","commonSituations":"Flink job shutdown/cancel interrupting the maintenance trigger thread mid-query; JDBC driver stuck on a slow or unresponsive database so the thread sits blocked until interrupted; operator restarts cancelling the thread that acquires locks.","solutions":["Check why the thread was interrupted (job cancellation, timeout watchdog) and ensure maintenance tasks run in a thread allowed to complete.","Verify the JDBC database and network are responsive; slow queries increase the window for interruption.","Adjust connection/validation timeouts (e.g., connection pool settings) so queries fail with SQLException instead of hanging until interrupted.","Propagate and honor the interruption: since the flag is re-set via Thread.currentThread().interrupt(), let shutdown logic observe it and stop gracefully."],"exampleFix":"// before\nboolean held = lock.tryLock(); // may throw UncheckedInterruptedException\n// after\ntry {\n  boolean held = lock.tryLock();\n} catch (UncheckedInterruptedException e) {\n  Thread.currentThread().interrupt(); // restore flag, abort maintenance gracefully\n  return;\n}","handlingStrategy":"try-catch","validationCode":"if (Thread.currentThread().isInterrupted()) { /* skip maintenance attempt */ }","typeGuard":null,"tryCatchPattern":"try { boolean held = lock.tryLock(); } catch (UncheckedInterruptedException e) { Thread.currentThread().interrupt(); return; }","preventionTips":["Avoid interrupting threads during maintenance commits; use graceful shutdown with drain time.","Keep JDBC queries fast (timeouts, pool validation) so threads block only briefly.","Check Thread.currentThread().isInterrupted() before starting lock operations.","Log the interruption cause to distinguish cancellation from bugs."],"tags":["jdbc","interrupted","lock","flink","thread"],"backgroundTag":"thread-interrupted","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}