xkcoding/spring-boot-demo · warning · RuntimeException

请勿重复提交

Error message

请勿重复提交

What it means

ZooLockAspect.around acquires a Curator InterProcessMutex; if lock.acquire(timeout, unit) returns false (lock not acquired within the timeout), it throws RuntimeException('请勿重复提交') at ZooLockAspect.java:77. Distributed-lock contention is treated as duplicate-submission prevention.

Source

Thrown at demo-zookeeper/src/main/java/com/xkcoding/zookeeper/aspectj/ZooLockAspect.java:77

     * @throws Throwable 异常信息
     */
    @Around("doLock()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Object[] args = point.getArgs();
        ZooLock zooLock = method.getAnnotation(ZooLock.class);
        if (StrUtil.isBlank(zooLock.key())) {
            throw new RuntimeException("分布式锁键不能为空");
        }
        String lockKey = buildLockKey(zooLock, method, args);
        InterProcessMutex lock = new InterProcessMutex(zkClient, lockKey);
        try {
            // 假设上锁成功,以后拿到的都是 false
            if (lock.acquire(zooLock.timeout(), zooLock.timeUnit())) {
                return point.proceed();
            } else {
                throw new RuntimeException("请勿重复提交");
            }
        } finally {
            lock.release();
        }
    }

    /**
     * 构造分布式锁的键
     *
     * @param lock   注解
     * @param method 注解标记的方法
     * @param args   方法上的参数
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private String buildLockKey(ZooLock lock, Method method, Object[] args) throws NoSuchFieldException, IllegalAccessException {
        StringBuilder key = new StringBuilder(KEY_SEPARATOR + KEY_PREFIX + lock.key());

View on GitHub (pinned to 87a142f960)

Solutions

  1. Increase the @ZooLock timeout to outlast the critical section
  2. Make the client idempotent (disable the button, send an idempotency token)
  3. Verify ZooKeeper quorum health and session-timeout settings
  4. Retry with backoff where the operation is safe to retry

Example fix

// before
if (lock.acquire(zooLock.timeout(), zooLock.timeUnit())) {
    return point.proceed();
} else {
    throw new RuntimeException('请勿重复提交');
}
// after (caller-side)
try {
    lockedService.doWork(req);
} catch (RuntimeException e) {
    if ('请勿重复提交'.equals(e.getMessage())) {
        return ApiResponse.of(429, '请求处理中,请勿重复提交', null);
    }
    throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side: guard against double submit before invoking the locked method
if (submitting) { return; }
submitting = true;
try { lockedService.doWork(req); } finally { submitting = false; }

Try / catch

try {
    lockedService.doWork(req);
} catch (RuntimeException e) {
    if ('请勿重复提交'.equals(e.getMessage())) {
        return ApiResponse.of(429, '请求处理中,请勿重复提交', null);
    }
    throw e;
}

Prevention

When it happens

Trigger: Two concurrent invocations of the same @ZooLock method/lock key; the current holder exceeds the acquire timeout; the ZooKeeper quorum is slow so acquire times out.

Common situations: Double-click submit on the client; high concurrency on an idempotent endpoint; an unhealthy/latent ZooKeeper cluster; @ZooLock timeout set too short.

Related errors


AI-assisted analysis of xkcoding/spring-boot-demo@87a142f960 (2026-08-14). Data as JSON: /api/errors/b997bd6e24acdf3c. Report an issue: GitHub.