{"record":{"id":"8f87dddeecad1d33","repo":"apache/iceberg","slug":"interrupted-unlock-we-try-one-more-time","errorCode":null,"errorMessage":"Interrupted unlock we try one more time {}.{}","messagePattern":"Interrupted unlock we try one more time (.+?)\\.(.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"hive-metastore/src/main/java/org/apache/iceberg/hive/MetastoreLock.java","lineNumber":414,"sourceCode":"            return;\n          }\n\n          id = lockInfo.lockId;\n        } else {\n          LOG.warn(\"Could not find lock with HMSClient {}\", HiveVersion.current());\n          return;\n        }\n      } else {\n        id = lockId.get();\n      }\n\n      doUnlock(id);\n    } catch (InterruptedException ie) {\n      if (id != null) {\n        // Interrupted unlock. We try to unlock one more time if we have a lockId\n        try {\n          Thread.interrupted(); // Clear the interrupt status flag for now, so we can retry unlock\n          LOG.warn(\"Interrupted unlock we try one more time {}.{}\", databaseName, tableName, ie);\n          doUnlock(id);\n        } catch (Exception e) {\n          LOG.warn(\"Failed to unlock even on 2nd attempt {}.{}\", databaseName, tableName, e);\n        } finally {\n          Thread.currentThread().interrupt(); // Set back the interrupt status\n        }\n      } else {\n        Thread.currentThread().interrupt(); // Set back the interrupt status\n        LOG.warn(\"Interrupted finding locks to unlock {}.{}\", databaseName, tableName, ie);\n      }\n    } catch (Exception e) {\n      LOG.warn(\"Failed to unlock {}.{}\", databaseName, tableName, e);\n    }\n  }\n\n  private void doUnlock(long lockId) throws TException, InterruptedException {\n    metaClients.run(\n        client -> {","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/hive-metastore/src/main/java/org/apache/iceberg/hive/MetastoreLock.java#L396-L432","documentation":"MetastoreLock.unlock() releases the Hive lock via doUnlock(id). If that call is interrupted but a lockId exists, this warning is logged and unlock is retried once after clearing the interrupt flag; the interrupt status is restored afterwards. This guards against leaking Hive locks when a shutdown interrupt lands exactly during unlock.","triggerScenarios":"unlock() (directly or recursively from acquireLock's failure path) is interrupted during the first doUnlock(id) call while id != null, triggering the one-retry path.","commonSituations":"Flink job cancellation or JVM shutdown racing with the release of a commit lock; long metastore RPC during unlock being interrupted by an aggressive shutdown timeout.","solutions":["Check Hive (SHOW LOCKS) for leaked locks if the second unlock also failed, and unlock manually.","Allow enough shutdown grace time (awaitTermination) so unlock RPCs finish before threads are interrupted.","Retry the operation; Hive locks also expire, but manual cleanup avoids blocking other writers.","Investigate why interrupt happened during unlock — usually premature shutdownNow() calls."],"exampleFix":"// before: shutdownNow interrupts unlock RPC\npool.shutdownNow();\n// after: let commits finish and release locks\npool.shutdown();\npool.awaitTermination(5, TimeUnit.MINUTES);\npool.shutdownNow();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  lock.close(); // unlock\n} catch (RuntimeException e) {\n  // verify lock released via SHOW LOCKS; unlock manually if leaked\n}","preventionTips":["Provide shutdown grace periods so unlock RPCs complete","Audit Hive locks after abnormal terminations","Ensure unlock happens in finally blocks"],"tags":["hive","lock-contention","interrupted","unlock"],"backgroundTag":"lock-wait-interrupted","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"}