{"record":{"id":"8c82ace88e409250","repo":"YunaiV/yudao-cloud","slug":"423","errorCode":"423","errorMessage":"请求失败，请稍后重试","messagePattern":"请求失败，请稍后重试","errorType":"error_code","errorClass":"ServiceException","httpStatus":423,"severity":"warning","filePath":"yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/core/DefaultLockFailureStrategy.java","lineNumber":19,"sourceCode":"package cn.iocoder.yudao.framework.lock4j.core;\n\nimport cn.iocoder.yudao.framework.common.exception.ServiceException;\nimport cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;\nimport com.baomidou.lock.LockFailureStrategy;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.lang.reflect.Method;\n\n/**\n * 自定义获取锁失败策略，抛出 {@link ServiceException} 异常\n */\n@Slf4j\npublic class DefaultLockFailureStrategy implements LockFailureStrategy {\n\n    @Override\n    public void onLockFailure(String key, Method method, Object[] arguments) {\n        log.debug(\"[onLockFailure][线程:{} 获取锁失败，key:{} 获取失败:{} ]\", Thread.currentThread().getName(), key, arguments);\n        throw new ServiceException(GlobalErrorCodeConstants.LOCKED);\n    }\n}\n","sourceCodeStart":1,"sourceCodeEnd":22,"githubUrl":"https://github.com/YunaiV/yudao-cloud/blob/477be9dd49ab7223a972a6abdff0684d6423dec3/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/core/DefaultLockFailureStrategy.java#L1-L22","documentation":"When a @Lock4j/@RateLimiter-protected method cannot acquire its distributed lock, LockFailureStrategy fires. The framework's default implementation throws ServiceException with the global LOCKED error (HTTP 423-ish semantic: resource locked). The strategy bean can be replaced to customize behavior.","triggerScenarios":"Two threads/users invoke a method guarded by lock4j (same key) concurrently; the first holds the lock (e.g. during a long batch operation) and the second's acquire fails immediately, invoking DefaultLockFailureStrategy.onLockFailure.","commonSituations":"Scheduled jobs and manual triggers overlapping on the same lock; concurrent admin operations on the same entity; locks not released after a crash until expire-time passes (lock leak); long transactions exceeding the lock acquire timeout.","solutions":["Retry after a short delay or surface '操作正在执行中，请稍后再试' to the user.","Ensure lock keys are scoped narrowly (include entity id) so unrelated operations don't contend.","Set a reasonable expire/retry configuration on @Lock4j (acquireTimeout, expire) instead of failing fast.","If waiting is acceptable, register a custom LockFailureStrategy that retries or blocks instead of throwing."],"exampleFix":"// before\nlock4j.tryLockAndExecute(() -> service.rebuildIndex()); // throws on contention\n\n// after\ntry {\n    lock4j.tryLockAndExecute(() -> service.rebuildIndex());\n} catch (ServiceException e) {\n    if (GlobalErrorCodeConstants.LOCKED.getCode().equals(e.getCode())) {\n        return Result.error(\"任务正在执行，请稍后再试\");\n    }\n    throw e;\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    service.doLockedOp(id);\n} catch (ServiceException e) {\n    if (GlobalErrorCodeConstants.LOCKED.getCode().equals(e.getCode())) {\n        scheduleRetryIn(Duration.ofSeconds(5)); // or inform user\n        return;\n    }\n    throw e;\n}","preventionTips":["Scope lock keys to entity id to avoid unnecessary contention","Set sensible acquireTimeout/expire on lock annotations; use a custom LockFailureStrategy when blocking is acceptable"],"tags":["yudao","lock4j","distributed-lock","concurrency","aop"],"backgroundTag":null,"analyzedSha":"477be9dd49ab7223a972a6abdff0684d6423dec3","analyzedAt":"2026-08-14T13:35:31.121Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}