apache/iceberg · error · UncheckedSQLException
Failed to check the state of the lock
Error message
Failed to check the state of the lock %s
What it means
JdbcLockFactory's JdbcLock.isHeld() queries the JDBC lock table to determine whether this trigger holds the maintenance lock. Any SQLException from the underlying JDBC connection/query is wrapped into UncheckedSQLException with this message. The error means the lock state could not be read, not that the lock is (or is not) held.
Solutions
- Verify the database is reachable and the lock table exists (run the JdbcCatalog table-create SQL against it).
- Test credentials in the JDBC URI by connecting with a plain SQL client.
- Check network/firewall/Security Group rules between the Flink cluster and the DB host.
- Inspect the cause (UncheckedSQLException.getCause()) for the exact SQLState to distinguish connectivity from schema/permission problems.
- If transient, retry triggerLock/table maintenance after the DB recovers; the exception is unchecked so callers must catch UncheckedSQLException.
Example fix
// before
String jdbcUrl = "jdbc:mysql://db-host:3306/iceberg"; // db down -> UncheckedSQLException
TableMaintenance.forTable(table).lockFactory(JdbcLockFactory.builder().setJdbcUrl(jdbcUrl).build());
// after
// pre-check connectivity before wiring the lock factory
try (Connection c = DriverManager.getConnection(jdbcUrl, user, pass)) { /* ok */ }
TableMaintenance.forTable(table).lockFactory(JdbcLockFactory.builder().setJdbcUrl(jdbcUrl).build()); Defensive patterns
Strategy: try-catch
Validate before calling
try (Connection c = DriverManager.getConnection(jdbcUrl, user, pass)) {
try (Statement s = c.createStatement()) { s.execute("SELECT 1 FROM trigger_lock LIMIT 1"); }
} Try / catch
try {
factory.tryLock(...);
} catch (UncheckedSQLException e) {
LOG.error("JDBC lock check failed", e.getCause());
// fall back: skip maintenance this cycle
} Prevention
- Create/verify the JDBC lock table before enabling trigger locking
- Test JDBC URL and credentials with a SQL client from the Flink host
- Grant SELECT/INSERT/UPDATE/DELETE on the lock table to the JDBC user
- Monitor DB availability and set sane connection/socket timeouts
When it happens
Trigger: tryLock() -> isHeld() executing SELECT against the JDBC lock table fails with a SQLException: bad JDBC URI, database down, table missing (schema not initialized), credentials revoked, or connection dropped mid-query.
Common situations: JDBC catalog database restarted or unreachable during maintenance trigger creation; lock table dropped/recreated; network partition between Flink task manager and the RDS/Postgres host; wrong username/password in the JDBC connection config.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- Failed to remove lock
- Failed to check the state of the lock
- Failed to get lock information for
- Failed to get lock information for
- Failed to remove lock
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/ff6c30827255e4de.
Report an issue: GitHub.
Appendix: source
Thrown at flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/maintenance/api/JdbcLockFactory.java:230
@Override
public boolean isHeld() {
try {
return pool.run(
conn -> {
try (PreparedStatement sql = conn.prepareStatement(GET_LOCK_SQL)) {
sql.setString(1, type.key);
sql.setString(2, lockId);
try (ResultSet rs = sql.executeQuery()) {
return rs.next();
}
}
});
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new UncheckedInterruptedException(e, "Interrupted during isHeld");
} catch (SQLException e) {
// SQL exception happened when getting lock information
throw new UncheckedSQLException(e, "Failed to check the state of the lock %s", this);
}
}
@SuppressWarnings("checkstyle:NestedTryDepth")
@Override
public void unlock() {
try {
// Possible concurrency issue:
// - `unlock` and `tryLock` happens at the same time when there is an existing lock
//
// Steps:
// 1. `unlock` removes the lock in the database, but there is a temporary connection failure
// 2. `lock` finds that there is no lock, so creates a new lock
// 3. `unlock` retries the lock removal and removes the new lock
//
// To prevent the situation above we fetch the current lockId, and remove the lock
// only with the given id.
String instanceId = instanceId();View on GitHub (pinned to 86d9c8fc54)