quarkusio/quarkus · error · jakarta.enterprise.context.control.LockException

Read lock not acquired in ${time} ms

Error message

Read lock not acquired in ${time} ms

What it means

Analogous to the write-lock case: @Lock(READ) with time > 0 uses tryLock(time, unit) on the read lock and throws LockException when the timeout expires. The shared resource is held in write mode (or by other readers preventing acquisition) longer than the allowed wait.

Source

Thrown at independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/LockInterceptor.java:93

                if (readHoldCount > 0) {
                    // Re-aqcquire the read locks
                    for (int i = 0; i < readHoldCount; i++) {
                        rwl.readLock().lock();
                    }
                }
                rwl.writeLock().unlock();
            }
        }
    }

    private Object readLock(Lock lock, InvocationContext ctx) throws Exception {
        boolean locked = false;
        long time = lock.time();
        try {
            if (time > 0) {
                locked = rwl.readLock().tryLock(time, lock.unit());
                if (!locked) {
                    throw new LockException("Read lock not acquired in " + lock.unit().toMillis(time) + " ms");
                }
            } else {
                rwl.readLock().lock();
                locked = true;
            }
            return ctx.proceed();
        } finally {
            if (locked) {
                rwl.readLock().unlock();
            }
        }
    }

    Lock getLock(ArcInvocationContext ctx) {
        Lock lock = ctx.findIterceptorBinding(Lock.class);
        if (lock == null) {
            // This should never happen
            throw new LockException("@Lock binding not found on business method " + ctx.getMethod());

View on GitHub (pinned to e1c734241f)

Solutions

  1. Increase lock.time()/unit on the @Lock(READ) annotation.
  2. Reduce the duration/frequency of write-locked operations on the same bean.
  3. Consider splitting data so reads and writes lock less contended state.
  4. Set time = 0 to block indefinitely on the read lock if a hard timeout is not required.

Example fix

// before
@Lock(value = LockType.READ, time = 50, unit = TimeUnit.MILLISECONDS)
public Data get() { ... }
// after
@Lock(value = LockType.READ, time = 2, unit = TimeUnit.SECONDS)
public Data get() { ... }
Defensive patterns

Strategy: retry

Validate before calling

if (unit.toMillis(time) < expectedReadDurationMs) {
    throw new IllegalStateException("@Lock(READ) time " + time + " likely too small");
}

Try / catch

try {
    return bean.readOperation();
} catch (LockException e) {
    // writer starvation: brief backoff then retry
    Thread.sleep(50);
    return bean.readOperation();
}

Prevention

When it happens

Trigger: Method annotated @Lock(value = READ, time = N, unit = X) invoked while a writer holds the lock (or is starving readers) for longer than N units.

Common situations: Readers competing with frequent/long writers; time bound set too small; platform writer preference (ReentrantReadWriteLock is non-fair by default) delaying readers.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/ae527c8326f36485. Report an issue: GitHub.