{"record":{"id":"ad8c5e8eec5bb872","repo":"hyperledger/fabric","slug":"lock-is-already-acquired-on-file-s","errorCode":null,"errorMessage":"lock is already acquired on file %s","messagePattern":"lock is already acquired on file (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/ledger/util/leveldbhelper/leveldb_helper.go","lineNumber":216,"sourceCode":"// Lock acquire a file lock. We achieve this by opening\n// a db for the given filePath. Internally, leveldb acquires a\n// file lock while opening a db. If the db is opened again by the same or\n// another process, error would be returned. When the db is closed\n// or the owner process dies, the lock would be released and hence\n// the other process can open the db. We exploit this leveldb\n// functionality to acquire and release file lock as the leveldb\n// supports this for Windows, Solaris, and Unix.\nfunc (f *FileLock) Lock() error {\n\tdbOpts := &opt.Options{}\n\tvar err error\n\tvar dirEmpty bool\n\tif dirEmpty, err = fileutil.CreateDirIfMissing(f.filePath); err != nil {\n\t\tpanic(fmt.Sprintf(\"Error creating dir if missing: %s\", err))\n\t}\n\tdbOpts.ErrorIfMissing = !dirEmpty\n\tdb, err := leveldb.OpenFile(f.filePath, dbOpts)\n\tif err != nil && err == syscall.EAGAIN {\n\t\treturn errors.Errorf(\"lock is already acquired on file %s\", f.filePath)\n\t}\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"Error acquiring lock on file %s: %s\", f.filePath, err))\n\t}\n\n\t// only mutate the lock db reference AFTER validating that the lock was held.\n\tf.db = db\n\n\treturn nil\n}\n\n// Determine if the lock is currently held open.\nfunc (f *FileLock) IsLocked() bool {\n\treturn f.db != nil\n}\n\n// Unlock releases a previously acquired lock. We achieve this by closing\n// the previously opened db. FileUnlock can be called multiple times.","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/common/ledger/util/leveldbhelper/leveldb_helper.go#L198-L234","documentation":"FileLock.Lock returns errors.Errorf when leveldb.OpenFile on the lock file fails with syscall.EAGAIN, meaning another process already holds the lock. This is the only error Lock returns normally; all other open failures panic. It is the expected 'lock is taken' signal for cross-process mutual exclusion.","triggerScenarios":"A second process (or a leftover process) attempts Lock on the same file path while the first holder is alive; a stale lock file is held by a hung process that never released it.","commonSituations":"Starting two peer/leader-election processes against the same data directory; a crashed-but-not-exited process still holding the OS lock; container restarts where the old PID lingers.","solutions":["Ensure only one process uses this file lock path (check running processes / container replicas).","Wait and retry until the current holder releases the lock.","Kill or wait for the stale holder process; the OS lock frees when the holder exits.","Compare with errors.Is(err, ...) on the returned error to distinguish 'lock held' from other failures (which panic instead)."],"exampleFix":"// before\nfileLock.Lock()\n// after\nif err := fileLock.Lock(); err != nil {\n  if strings.Contains(err.Error(), \"lock is already acquired\") {\n    return errors.New(\"another process holds the file lock; retry later\")\n  }\n  return err\n}","handlingStrategy":"retry","validationCode":"// check no other holder before locking (best-effort)\nif procHolding(lockFilePath) != nil {\n  return errors.New(\"another process holds the file lock\")\n}","typeGuard":"func isLockHeldErr(err error) bool {\n  return err != nil && strings.Contains(err.Error(), \"lock is already acquired on file\")\n}","tryCatchPattern":"for i := 0; i < maxAttempts; i++ {\n  err := fileLock.Lock()\n  if err == nil { break }\n  if isLockHeldErr(err) { time.Sleep(retryInterval); continue }\n  return err\n}","preventionTips":["Run exactly one process per lock path (check replicas/PIDs)","Clean up hung processes that hold the lock before restarting","Use bounded retry-with-backoff around Lock","Remember other open failures panic — wrap Lock with recover"],"tags":["leveldb","filelock","locking","concurrency","golang"],"backgroundTag":"leveldb-lock-held","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}