{"record":{"id":"b81bf65c3ca1a1a5","repo":"apache/iceberg","slug":"lock-for-s-currently-held-by-s-expiration-s","errorCode":null,"errorMessage":"Lock for %s currently held by %s, expiration: %s","messagePattern":"Lock for (.+?) currently held by (.+?), expiration: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"warning","filePath":"core/src/main/java/org/apache/iceberg/util/LockManagers.java","lineNumber":192,"sourceCode":"   * implementation should only be used for testing, or if the caller only needs locking within the\n   * same JVM during table commits.\n   */\n  static class InMemoryLockManager extends BaseLockManager {\n\n    private static final Logger LOG = LoggerFactory.getLogger(InMemoryLockManager.class);\n\n    private static final Map<String, InMemoryLockContent> LOCKS = Maps.newConcurrentMap();\n    private static final Map<String, ScheduledFuture<?>> HEARTBEATS = Maps.newHashMap();\n\n    InMemoryLockManager(Map<String, String> properties) {\n      initialize(properties);\n    }\n\n    @VisibleForTesting\n    void acquireOnce(String entityId, String ownerId) {\n      InMemoryLockContent content = LOCKS.get(entityId);\n      if (content != null && content.expireMs() > System.currentTimeMillis()) {\n        throw new IllegalStateException(\n            String.format(\n                \"Lock for %s currently held by %s, expiration: %s\",\n                entityId, content.ownerId(), content.expireMs()));\n      }\n\n      long expiration = System.currentTimeMillis() + heartbeatTimeoutMs();\n      boolean succeed;\n      if (content == null) {\n        InMemoryLockContent previous =\n            LOCKS.putIfAbsent(entityId, new InMemoryLockContent(ownerId, expiration));\n        succeed = previous == null;\n      } else {\n        succeed = LOCKS.replace(entityId, content, new InMemoryLockContent(ownerId, expiration));\n      }\n\n      if (succeed) {\n        // cleanup old heartbeat\n        if (HEARTBEATS.containsKey(entityId)) {","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/util/LockManagers.java#L174-L210","documentation":"InMemoryLockManager.acquireOnce checks whether the entity's lock is currently held by another owner and unexpired; if so, it throws IllegalStateException with the entity ID, current owner, and expiration timestamp. This is the in-memory lock contention signal for single-process commit coordination.","triggerScenarios":"Two threads in the same JVM acquire the same entity lock concurrently and the first heartbeat has not yet expired when the second attempts acquisition.","commonSituations":"Long-running commits holding in-memory locks while another operation (e.g., expireSnapshots vs. commit) touches the same table; leaked locks from a crashed thread within the heartbeat timeout window.","solutions":["Retry acquire() after the reported expiration time passes (acquire already blocks/retries on heartbeat timeouts)","Investigate the owner operation holding the lock and whether it is stuck or excessively long","Ensure the heartbeat interval/timeout properties fit your operation duration; use a persistent LockManager across JVMs"],"exampleFix":"// before\nlockManager.acquire(entityId, ownerId); // may throw if held\n// after\ntry {\n  lockManager.acquire(entityId, ownerId);\n} catch (IllegalStateException e) {\n  Thread.sleep(1000);\n  lockManager.acquire(entityId, ownerId);\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { lockManager.acquire(entityId, ownerId); } catch (IllegalStateException e) { retryAfter(expirationFrom(e)); }","preventionTips":["Keep commit operations shorter than the heartbeat timeout","Increase heartbeat interval/timeout for long-running operations","Log and monitor lock owners to identify stuck operations","Use a persistent LockManager for multi-JVM deployments"],"tags":["lock","concurrency","in-memory","contention"],"backgroundTag":"lock-contention","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}