mybatis/mybatis-3 · error · CacheException
Couldn't get a lock in {timeout} for the key {key} at the ca
Error message
Couldn't get a lock in {timeout} for the key {key} at the cache {cacheId} What it means
Thrown by BlockingCache.acquireLock() when a query for a cache key cannot obtain the per-key CountDownLatch within the configured timeout (the 'blocking' cache decorator serializes queries for the same key so a cache miss is not thundering-herded). The message includes the timeout in ms, the key, and the cache id. timeout > 0 only when the decorator was configured with a positive blockingTimeout.
Source
Thrown at src/main/java/org/apache/ibatis/cache/decorators/BlockingCache.java:100
}
@Override
public void clear() {
delegate.clear();
}
private void acquireLock(Object key) {
CountDownLatch newLatch = new CountDownLatch(1);
while (true) {
CountDownLatch latch = locks.putIfAbsent(key, newLatch);
if (latch == null) {
break;
}
try {
if (timeout > 0) {
boolean acquired = latch.await(timeout, TimeUnit.MILLISECONDS);
if (!acquired) {
throw new CacheException(
"Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
}
} else {
latch.await();
}
} catch (InterruptedException e) {
throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
}
}
}
private void releaseLock(Object key) {
CountDownLatch latch = locks.remove(key);
if (latch == null) {
throw new IllegalStateException("Detected an attempt at releasing unacquired lock. This should never happen.");
}
latch.countDown();
}View on GitHub (pinned to 008069adb1)
Solutions
- Increase blockingTimeout (or remove the property to wait indefinitely) so typical query latency fits inside the window
- Fix the underlying slow query (index, fetch size) so first-miss loads finish before other waiters time out
- Pre-warm the cache for hot keys at startup so concurrent waiters never block on a cold miss
- Consider whether BlockingCache is needed at all — a request-coalescing layer or shorter TTL may serve better
Example fix
<!-- before --> <cache type="org.apache.ibatis.cache.decorators.BlockingCache" blockingTimeout="100"/> <!-- after --> <cache type="org.apache.ibatis.cache.decorators.BlockingCache" blockingTimeout="3000"/>
Defensive patterns
Strategy: retry
Validate before calling
// before enabling: measure p99 of the cached query and set blockingTimeout above it
long p99 = measureQueryP99Ms();
if (blockingTimeout > 0 && blockingTimeout < p99) throw new IllegalStateException("blockingTimeout " + blockingTimeout + "ms < query p99 " + p99 + "ms"); Try / catch
catch (CacheException e) { if (e.getMessage().contains("Couldn't get a lock")) { backoffAndRetryOnce(key); } else throw e; } — bounded retry only; a genuinely stuck holder needs cache eviction. Prevention
- Set blockingTimeout comfortably above worst-case query latency
- Pre-warm hot keys to avoid cold-miss stampedes
- Reassess whether BlockingCache is the right tool for your load
When it happens
Trigger: BlockingCache configured via <cache ... ><property name="blockingTimeout" value="250"/></cache> (or CacheDecorator order in a custom cache), and a query for key K takes longer than 250ms while another thread holds K's latch — e.g. a slow first query on a cold cache with many concurrent requests for the same row.
Common situations: Cold-start stampedes on hot keys where the loading query exceeds the blocking timeout; slow DB under load making every first-miss query exceed a small timeout; deadlock-ish long transactions holding the query; timeout set too low (default is 0 = wait forever, so someone set it deliberately).
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Got interrupted while trying to acquire lock for key {key}
- cache-ref element requires a namespace attribute.
- No cache for namespace '{namespace}' could be found.
- Cache-ref not yet resolved
- Should be specified either value() or name() attribute in th
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/dc71d1517678616d.
Report an issue: GitHub.