{"record":{"id":"1d5f5d19192f2cb7","repo":"flowable/flowable-engine","slug":"error-while-waiting-for-global-acquire-lock-for-en","errorCode":null,"errorMessage":"Error while waiting for global acquire lock for engine {}","messagePattern":"Error while waiting for global acquire lock for engine (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/AcquireAsyncJobsDueRunnable.java","lineNumber":123,"sourceCode":"\n        LOGGER.info(\"starting to acquire async jobs due for engine {}\", getEngineName());\n        Thread.currentThread().setName(name);\n\n        final CommandExecutor commandExecutor = asyncExecutor.getJobServiceConfiguration().getCommandExecutor();\n\n        long millisToWait = 0L;\n        while (!isInterrupted) {\n\n            if (configuration.isGlobalAcquireLockEnabled()) {\n\n                try {\n                    millisToWait = lockManager.waitForLockRunAndRelease(configuration.getLockWaitTime(), () -> executeAcquireCycle(commandExecutor));\n                } catch (Exception e) {\n                    // Don't do anything, lock will be tried again next time\n                    millisToWait = asyncExecutor.getDefaultAsyncJobAcquireWaitTimeInMillis();\n\n                    if (!(e instanceof FlowableException)) { // FlowableExeption doesn't need to be logged, could be regular lock logic\n                        LOGGER.warn(\"Error while waiting for global acquire lock for engine {}\", getEngineName(), e);\n                    }\n                }\n\n                if (millisToWait == 0) {\n                    // Always wait when running with global acquire lock, to let other nodes have the ability to fill the queue\n                    // If 0 was returned, it means there is still work to do, but we want to give other nodes a chance.\n                    millisToWait = configuration.getLockPollRate().toMillis();\n                }\n\n            } else {\n                millisToWait = executeAcquireCycle(commandExecutor);\n\n            }\n\n            if (millisToWait > 0) {\n                sleep(millisToWait);\n            }\n","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/AcquireAsyncJobsDueRunnable.java#L105-L141","documentation":"The async executor's AcquireAsyncJobsDueRunnable logs this warning when waitForLockRunAndRelease (global acquire locking) throws while trying to acquire the engine's global acquire lock. FlowableException subclasses are deliberately not logged because they represent regular lock contention logic; any other exception indicates an unexpected problem (DB connection failure, timeout, etc.) during the lock wait. The runnable swallows the error and retries on the next cycle.","triggerScenarios":"In the runnable's run() loop, waitForLockRunAndRelease throws a non-FlowableException while waiting for/releasing the global async job acquire lock — commonly due to database connectivity issues, lock timeouts, or transaction errors against the shared ACT_GE_PROPERTY-based lock row.","commonSituations":"Multi-node async executor setups where a node loses its DB connection mid-acquire; database deadlock/timeout on the lock table; network blip between the executor and the database; misconfigured lockWaitTime causing repeated failed lock attempts.","solutions":["Inspect the attached stack trace for the underlying cause (SQL/connection error) and fix database connectivity or schema issues first.","If caused by lock contention/timeout, tune the async executor's lockWaitTime and acquire wait times so nodes compete less aggressively.","Verify all nodes run compatible Flowable versions sharing the same database, since the global lock is implemented via a shared property row.","If errors recur persistently, check DB health (locks, deadlocks, connection pool exhaustion) and increase pool sizing.","This is a recoverable warning — the executor retries automatically; no code change is needed for transient occurrences."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Before enabling global acquire lock, verify database access\ntry (Connection c = dataSource.getConnection()) {\n    if (!c.isValid(5)) throw new SQLException(\"DB connection invalid\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    millisToWait = lockManager.waitForLockRunAndRelease(lockWaitTime, acquireCycle);\n} catch (Exception e) {\n    if (!(e instanceof FlowableException)) {\n        log.warn(\"Error while waiting for global acquire lock for engine {}\", engineName, e);\n    }\n    millisToWait = defaultAsyncJobAcquireWaitTimeInMillis; // retry on next cycle\n}","preventionTips":["Keep database connection pools sized for all async executor threads","Tune lockWaitTime and wait times to reduce contention in multi-node setups","Monitor DB for deadlocks/lock waits on the shared lock (ACT_GE_PROPERTY) row","Treat this warning as transient; alert only when it fires continuously"],"tags":["async-executor","database","lock","retry"],"backgroundTag":"database-query-failed","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}