proper-lockfile
An inter-process and inter-machine lockfile utility for local and network file systems
Repository Health
Technical Analysis
proper-lockfile provides cooperative file locking across processes and even across machines sharing a network file system. Rather than relying on open() with O_EXCL (which is broken on NFS), it uses the mkdir strategy, which is atomic on essentially every filesystem, and creates a .lock suffixed directory next to the file being locked.
To guard against stale locks from crashed processes, it periodically refreshes the lockfile’s mtime while held and lets callers configure a staleness threshold; if the update fails or the lock is found compromised, an onCompromised callback fires. Both promise-based (.lock/.unlock/.check) and synchronous (.lockSync/.unlockSync/.checkSync) APIs are provided, and locks are automatically released on graceful process exit.
What You Get
- Promise-based
.lock()/.unlock()/.check()API plus synchronous.lockSync()/.unlockSync()/.checkSync()equivalents - Atomic
mkdir-based locking that works correctly on local and network file systems, unlikeO_EXCL-based alternatives - Configurable staleness detection via periodic
mtimeupdates, with astalethreshold andupdateinterval - An
onCompromisedhook fired if a held lock’s mtime updates start failing or a lock is found tampered with - Automatic lock release on graceful process exit (not guaranteed on SIGKILL or VM fatal errors)
Common Use Cases
- Preventing concurrent writes to a shared cache, config, or database file from multiple Node.js processes
- Coordinating access to a resource shared across machines on a network file system
- Guarding CLI tools that must not run two instances against the same working directory simultaneously
- Implementing safe, crash-tolerant single-writer semantics for build or deployment scripts
Under The Hood
Architecture — index.js is a thin public-facing wrapper exposing the six lock/unlock/check (sync and async) functions, delegating the actual locking mechanics to lib/lockfile.js, which implements the mkdir-based acquire/release/check logic, staleness detection, and the periodic mtime-refresh timer. lib/adapter.js abstracts the filesystem calls (supporting a pluggable fs, defaulting to graceful-fs), and lib/mtime-precision.js detects the filesystem’s mtime precision to avoid false-positive staleness on filesystems with coarse timestamp resolution.
Tech Stack — Plain Node.js (CommonJS), with graceful-fs as the default filesystem backend and the retry package available for configurable lock-acquisition retry policies. No build step; the library ships as-is from lib/ and index.js.
Code Quality — Tests run via Jest with coverage (jest --env node --coverage --runInBand), and the README explicitly notes ‘the test suite is very extensive’ including a stress test for lock exclusivity guarantees. The library is feature-frozen and has seen no commits since 2023, per the health-score data, but its narrow, well-defined scope means the risk from staleness is low for a locking primitive with this test depth.
API Design — Six clearly-named functions (lock/unlock/check × sync/async) cover the entire public surface, and the async .lock() returns a release function directly rather than requiring a separate unlock call with the original path — reducing the chance of releasing the wrong resource. Sensible option defaults (stale: 10000, update: stale/2) mean most callers need zero configuration to get safe behavior out of the box.