apache/iceberg · error · UncheckedInterruptedException

Interrupted during unlock

Error message

Interrupted during unlock

What it means

JdbcLock.unlock() removes this trigger's lock row/state from the JDBC lock table. If the thread is interrupted while executing the update/delete SQL, the interrupt flag is restored and an UncheckedInterruptedException is thrown. A stuck unlock can leave the lock held and block subsequent maintenance runs.

Source

Thrown at flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/maintenance/api/JdbcLockFactory.java:270

              conn -> {
                try (PreparedStatement sql = conn.prepareStatement(DELETE_LOCK_SQL)) {
                  sql.setString(1, type.key);
                  sql.setString(2, lockId);
                  sql.setString(3, instanceId);
                  long count = sql.executeUpdate();
                  LOG.info(
                      "Deleted {} lock with instanceId {} with row count {}",
                      this,
                      instanceId,
                      count);
                }

                return null;
              });
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new UncheckedInterruptedException(e, "Interrupted during unlock");
      } catch (SQLException e) {
        // SQL exception happened when getting/updating lock information
        throw new UncheckedSQLException(e, "Failed to remove lock %s", this);
      }
    }

    @Override
    public String toString() {
      return MoreObjects.toStringHelper(this).add("type", type).add("lockId", lockId).toString();
    }

    @SuppressWarnings("checkstyle:NestedTryDepth")
    private String instanceId() {
      try {
        return pool.run(
            conn -> {
              try (PreparedStatement sql = conn.prepareStatement(GET_LOCK_SQL)) {
                sql.setString(1, type.key);

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Inspect the wrapped cause and job logs to find who interrupted the thread; avoid cancelling during maintenance commits.
  2. Verify the database is responsive so unlock SQL completes quickly instead of blocking until interrupted.
  3. After restart, confirm the lock was actually released; clear stale lock rows in the lock table if orphaned.
  4. Run unlock in shutdown paths that are not interrupted (or tolerate interruption and re-check lock state afterwards).

Example fix

// before
executor.shutdownNow(); // interrupts thread holding the lock mid-unlock
// after
executor.shutdown();
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
  executor.shutdownNow(); // interrupt only after unlock had a chance to finish
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (Thread.currentThread().isInterrupted()) { /* do not start unlock */ }

Try / catch

try { lock.unlock(); } catch (UncheckedInterruptedException e) { Thread.currentThread().interrupt(); /* schedule lock-state re-check */ }

Prevention

When it happens

Trigger: Calling unlock() on a JdbcLock while the thread executing the get/update lock SQL is interrupted (job cancellation, shutdown hook, interrupt from a scheduler).

Common situations: Flink job restart or cancellation during maintenance commit; database hang causing the unlock SQL to block until an external interrupt; thread pool shutdown while maintenance is still finalizing.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/50e59d63e2736d19. Report an issue: GitHub.