{"record":{"id":"2c471174781f8e9c","repo":"apache/iceberg","slug":"failed-to-remove-lock-s-2c4711","errorCode":null,"errorMessage":"Failed to remove lock %s","messagePattern":"Failed to remove lock (.+?)","errorType":"exception","errorClass":"UncheckedSQLException","httpStatus":null,"severity":"error","filePath":"flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/maintenance/api/JdbcLockFactory.java","lineNumber":273,"sourceCode":"                  sql.setString(2, lockId);\n                  sql.setString(3, instanceId);\n                  long count = sql.executeUpdate();\n                  LOG.info(\n                      \"Deleted {} lock with instanceId {} with row count {}\",\n                      this,\n                      instanceId,\n                      count);\n                }\n\n                return null;\n              });\n        }\n      } catch (InterruptedException e) {\n        Thread.currentThread().interrupt();\n        throw new UncheckedInterruptedException(e, \"Interrupted during unlock\");\n      } catch (SQLException e) {\n        // SQL exception happened when getting/updating lock information\n        throw new UncheckedSQLException(e, \"Failed to remove lock %s\", this);\n      }\n    }\n\n    @Override\n    public String toString() {\n      return MoreObjects.toStringHelper(this).add(\"type\", type).add(\"lockId\", lockId).toString();\n    }\n\n    @SuppressWarnings(\"checkstyle:NestedTryDepth\")\n    private String instanceId() {\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                  if (rs.next()) {","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/maintenance/api/JdbcLockFactory.java#L255-L291","documentation":"JdbcLock.unlock() throws UncheckedSQLException when the SQL used to read/update the lock table fails, so the lock could not be removed. The message includes the lock identity. Because the lock row may still exist, later maintenance triggers can be blocked until the issue is fixed.","triggerScenarios":"unlock() executes its get/update lock SQL and the driver throws SQLException: broken connection, missing lock table, deadlocks/lock timeouts on the row, or SQL dialect incompatibility.","commonSituations":"Network drop between Flink and the database mid-unlock; row-level lock contention with another trigger instance; lock table dropped after a catalog re-creation; database failover invalidating pooled connections.","solutions":["Inspect the wrapped SQLException cause (deadlock, timeout, connection reset) and fix that root cause first.","Re-run maintenance or manually delete the stale lock row for this lockId from the lock table.","Add connection retry/validation settings to the JDBC URL and pool so transient drops self-heal.","Ensure only one instance per lockId runs; row contention often means a duplicate trigger is deployed.","If the table is missing, re-initialize the JDBC catalog to recreate the lock table."],"exampleFix":"// before\nString url = \"jdbc:postgresql://db:5432/iceberg\"; // stale connections\n// after\nString url = \"jdbc:postgresql://db:5432/iceberg?connectTimeout=10&socketTimeout=60\";\n// plus: DELETE FROM iceberg_locks WHERE lock_id = 'my-trigger'; // clear orphaned lock","handlingStrategy":"retry","validationCode":"try (Connection c = DriverManager.getConnection(jdbcUrl, user, pass)) { c.createStatement().execute(\"SELECT 1 FROM iceberg_locks LIMIT 1\"); } // table reachable and present","typeGuard":null,"tryCatchPattern":"try { lock.unlock(); } catch (UncheckedSQLException e) { log.error(\"Unlock failed: {}\", e.getCause(), e); /* delete orphaned lock row manually */ }","preventionTips":["Add socketTimeout/connectTimeout to the JDBC URL so failures surface quickly.","Monitor for row-lock contention; one trigger instance per lockId.","Have an operator runbook to delete stale lock rows after failures.","Re-initialize the catalog if the lock table is missing."],"tags":["jdbc","sql","lock","flink","unlock"],"backgroundTag":"database-write-failed","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"}