YunaiV/yudao-cloud · warning · ServiceException
423
423
Error message
请求失败,请稍后重试
What it means
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.
Source
Thrown at yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/lock4j/core/DefaultLockFailureStrategy.java:19
package cn.iocoder.yudao.framework.lock4j.core;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.baomidou.lock.LockFailureStrategy;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
/**
* 自定义获取锁失败策略,抛出 {@link ServiceException} 异常
*/
@Slf4j
public class DefaultLockFailureStrategy implements LockFailureStrategy {
@Override
public void onLockFailure(String key, Method method, Object[] arguments) {
log.debug("[onLockFailure][线程:{} 获取锁失败,key:{} 获取失败:{} ]", Thread.currentThread().getName(), key, arguments);
throw new ServiceException(GlobalErrorCodeConstants.LOCKED);
}
}
View on GitHub (pinned to 477be9dd49)
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.
Example fix
// before
lock4j.tryLockAndExecute(() -> service.rebuildIndex()); // throws on contention
// after
try {
lock4j.tryLockAndExecute(() -> service.rebuildIndex());
} catch (ServiceException e) {
if (GlobalErrorCodeConstants.LOCKED.getCode().equals(e.getCode())) {
return Result.error("任务正在执行,请稍后再试");
}
throw e;
} Defensive patterns
Strategy: retry
Try / catch
try {
service.doLockedOp(id);
} catch (ServiceException e) {
if (GlobalErrorCodeConstants.LOCKED.getCode().equals(e.getCode())) {
scheduleRetryIn(Duration.ofSeconds(5)); // or inform user
return;
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- 900
- LoginUser(%d) Table(%s/%s) 未返回数据权限
- AreaUtils 初始化失败
- IPUtils 初始化失败
- TenantContextHolder 不存在租户编号!可参考文档:https://doc.iocoder.cn
AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14).
Data as JSON: /api/errors/8c82ace88e409250.
Report an issue: GitHub.