{"record":{"id":"4dc084cdeb7aeaac","repo":"iflytek/astron-agent","slug":"distributed-lock-acquisition-timeout-please-try-again-later","errorCode":null,"errorMessage":"Distributed lock acquisition timeout, please try again later","messagePattern":"Distributed lock acquisition timeout, please try again later","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"warning","filePath":"console/backend/commons/src/main/java/com/iflytek/astron/console/commons/service/bot/impl/BotServiceImpl.java","lineNumber":303,"sourceCode":"        userLangChainLogService.insertUserLangChainLog(userLangChainLog);\n        UserLangChainInfo userLangChainInfo = UserLangChainInfo.builder()\n                .id(Long.parseLong(botId.toString()))\n                .botId(Integer.parseInt(botId.toString()))\n                .maasId(data.getLong(\"id\"))\n                .flowId(data.getString(\"flowId\"))\n                .uid(uid)\n                .spaceId(spaceId)\n                .updateTime(LocalDateTime.now())\n                .build();\n        userLangChainDataService.insertUserLangChainInfo(userLangChainInfo);\n    }\n\n    private <T> T executeWithLock(String lockKey, java.util.function.Supplier<T> operation) {\n        RLock lock = redissonClient.getLock(lockKey);\n        try {\n            boolean acquired = lock.tryLock(5, 10, TimeUnit.SECONDS);\n            if (!acquired) {\n                throw new IllegalStateException(\"Distributed lock acquisition timeout, please try again later\");\n            }\n            return operation.get();\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            throw new RuntimeException(\"Thread interrupted while acquiring lock\", e);\n        } catch (Exception e) {\n            log.error(\"Operation failed with lock: {}\", lockKey, e);\n            throw e;\n        } finally {\n            if (lock.isHeldByCurrentThread()) {\n                lock.unlock();\n            }\n        }\n    }\n\n    private void validateBotCreation(String uid, String botName, Long spaceId) {\n        if (chatBotDataService.checkRepeatBotName(uid, null, botName, spaceId)) {\n            throw new BusinessException(ResponseEnum.DUPLICATE_BOT_NAME);","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/service/bot/impl/BotServiceImpl.java#L285-L321","documentation":"BotServiceImpl wraps bot create/update operations in executeWithLock, a Redisson distributed lock (tryLock with 5s wait, 10s lease). If the lock cannot be acquired within 5 seconds, it throws this IllegalStateException so concurrent mutations of the same bot don't race. It signals contention, not corruption.","triggerScenarios":"Calling insertWorkflowBot, insertBotBasicInfo, updateWorkflowBot, or updateBotBasicInfo for a bot whose lockKey is already held by another thread/instance for more than 5 seconds — e.g. double-clicked save buttons, two concurrent requests for the same bot, or a previous holder that is stuck in a slow DB/Redis operation beyond the 10s lease.","commonSituations":"Users double-submitting the create/update form; retry storms after a slow response; long-running updates blocking subsequent requests; Redis latency making lock acquisition slow; deployments where a request thread stalls holding the lock.","solutions":["Retry the request after a short delay — the error message explicitly says to try again later; add client-side backoff on this status.","Disable the save/submit button and deduplicate in-flight requests on the frontend to prevent concurrent mutation of the same bot.","Verify Redisson/Redis connectivity and latency; slow lock acquisition often indicates Redis health issues.","If updates routinely exceed 5s, profile the operation or increase the waitTime in executeWithLock's tryLock call."],"exampleFix":"// before: fire-and-forget submit triggers duplicate updates\nawait updateBot(data);\n// after: serialize per-bot updates and retry on lock timeout\ntry {\n  await updateBot(data);\n} catch (e) {\n  if (isLockTimeout(e)) await retryWithBackoff(() => updateBot(data), { retries: 3, baseMs: 500 });\n  else throw e;\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await updateBot(data); } catch (e) { if (e.code === 'LOCK_TIMEOUT' || /lock acquisition timeout/.test(e.message)) { await sleep(1000); return retry(); } throw e; }","preventionTips":["Debounce/deduplicate save submissions per bot in the UI","Disable the submit button while a request is in flight","Monitor Redis latency; keep the lock holder's work short","Add client-side retry with exponential backoff for lock-timeout responses"],"tags":["redisson","distributed-lock","concurrency","java"],"backgroundTag":"request-timeout","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}