{"record":{"id":"68e1011b5a1f6dc1","repo":"apache/dolphinscheduler","slug":"the-client-is-not-the-lock-owner-of-the-lock","errorCode":null,"errorMessage":"The client  is not the lock owner of the lock: ","messagePattern":"The client  is not the lock owner of the lock: ","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-jdbc/src/main/java/org/apache/dolphinscheduler/plugin/registry/jdbc/server/JdbcRegistryLockManager.java","lineNumber":141,"sourceCode":"                // The lock is already exist, wait it release.\n                continue;\n            }\n            log.debug(\"{} acquire the lock {} failed try again\", lockOwner, lockKey);\n            // acquire failed, wait and try again\n            ThreadUtils.sleep(jdbcRegistryProperties.getHeartbeatRefreshInterval().toMillis());\n        }\n        return false;\n    }\n\n    @Override\n    public void releaseJdbcRegistryLock(Long clientId, String lockKey) {\n        String lockOwner = LockUtils.getLockOwner();\n        LockEntry lockEntry = jdbcRegistryLockHolderMap.get(lockKey);\n        if (lockEntry == null || !lockOwner.equals(lockEntry.getLockOwner())) {\n            return;\n        }\n        if (!clientId.equals(lockEntry.getJdbcRegistryLock().getClientId())) {\n            throw new UnsupportedOperationException(\n                    \"The client \" + clientId + \" is not the lock owner of the lock: \" + lockKey);\n        }\n        int newLockCount = lockEntry.lockCount.decrementAndGet();\n        if (newLockCount > 0) {\n            return;\n        }\n        if (newLockCount < 0) {\n            throw new IllegalMonitorStateException(\"Jdbc lock count has gone negative for lock: \" + lockKey);\n        }\n        jdbcRegistryLockRepository.deleteById(lockEntry.getJdbcRegistryLock().getId());\n        jdbcRegistryLockHolderMap.remove(lockKey);\n    }\n\n    @Data\n    @Builder\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class LockEntry {","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/apache/dolphinscheduler/blob/02eac45a1b6676e639fcbfb4be2243de5771b05d/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-jdbc/src/main/java/org/apache/dolphinscheduler/plugin/registry/jdbc/server/JdbcRegistryLockManager.java#L123-L159","documentation":"JdbcRegistryLockManager.releaseJdbcRegistryLock verifies that the clientId releasing the lock matches the clientId recorded in the lock entry's JdbcRegistryLock row. If the local thread holds the lock entry (via LockUtils.getLockOwner) but the given clientId differs from the stored owner, the release is rejected with UnsupportedOperationException. This protects distributed lock state from being released by the wrong client.","triggerScenarios":"Calling releaseJdbcRegistryLock with a clientId different from the one used in acquireJdbcRegistryLock; reusing the same lockKey across services with mismatched client identity; a client reconnecting with a new clientId and trying to release the old lock key.","commonSituations":"Client ID regenerated after server restart while a stale lock row remains, passing a hard-coded or default clientId to release while acquiring with the real one, thread pools crossing client contexts.","solutions":["Always acquire and release the lock with the same clientId value","Reuse one clientId per process lifetime instead of regenerating it","Guard release calls so they only run on the thread/client that successfully acquired"],"exampleFix":"// before\nlong clientId = System.nanoTime(); // new id per call\nregistryClient.acquireJdbcRegistryLock(this.clientId, lockKey);\nregistryClient.releaseJdbcRegistryLock(clientId, lockKey); // wrong client\n// after\nregistryClient.acquireJdbcRegistryLock(this.clientId, lockKey);\nregistryClient.releaseJdbcRegistryLock(this.clientId, lockKey);","handlingStrategy":"validation","validationCode":"if (this.clientId == null) throw new IllegalStateException(\"Client not connected; cannot release lock \" + lockKey);","typeGuard":null,"tryCatchPattern":"try { registryClient.releaseJdbcRegistryLock(clientId, lockKey); } catch (UnsupportedOperationException e) { log.warn(\"Lock {} not owned by client {}\", lockKey, clientId); }","preventionTips":["Keep one stable clientId per process for acquire and release","Wrap acquire/release in the same component so ids cannot diverge","Log clientId with every lock operation for auditing"],"tags":["registry","distributed-lock","ownership"],"backgroundTag":"permission-denied","analyzedSha":"02eac45a1b6676e639fcbfb4be2243de5771b05d","analyzedAt":"2026-09-06T17:43:00.555Z","contentChangedAt":"2026-09-06T17:43:00.555Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}