Registry / database / proper-lockfile

proper-lockfile

JSON →
library4.1.2jsnpmunverified

proper-lockfile is a robust JavaScript utility for managing inter-process and inter-machine file locks across local and network file systems. Currently at version 4.1.2, it is actively maintained with updates released as needed. Its core design uses an atomic `mkdir` strategy for lockfile creation, which is more reliable than `open` with `O_EXCL` flags, especially on network file systems (NFS) where `O_EXCL` is prone to race conditions. The library differentiates itself by constantly updating the lockfile's `mtime` (modified time) to accurately check for staleness, a significant improvement over `ctime` (creation time) for long-running processes. Furthermore, it incorporates mechanisms to detect when a lockfile might be compromised due to failed updates or unexpected delays, enhancing overall reliability compared to alternatives.

npm install proper-lockfile
INSTALL
IMPORT
SIG · PROPER-LOCKFILE
P
proper-lockfile
databasejavascriptv4.1.2
Install
Import
Disk
Pass rate
0/ 6
Env Coverage0 / 6
glibc
1822
musl
1822
Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
node 18226 runs
build_error
glibc
node 18226 runs
build_error
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

lock
import { lock } from 'proper-lockfile';
const lock = require('proper-lockfile').lock;
The default export from 'proper-lockfile' is also the `lock` function, so `import lock from 'proper-lockfile';` is also common. Named imports are preferred for clarity.
unlock
import { unlock } from 'proper-lockfile';
const unlock = require('proper-lockfile').unlock;
While `lock` returns a `release` function, `unlock` provides a direct API to remove a lock if the `release` function reference is lost or cannot be used.
check
import { check } from 'proper-lockfile';
const check = require('proper-lockfile').check;
Used to synchronously or asynchronously check if a file is currently locked and not stale.

Demonstrates how to acquire, use, and release an inter-process lock on a file using asynchronous functions, including error handling and retry options. It also shows basic file system interaction.

import { lock, unlock } from 'proper-lockfile'; import { promises as fs } from 'fs'; import path from 'path'; const filePath = path.join(process.cwd(), 'my-resource.txt'); const lockFileOptions = { stale: 15000, // Consider lock stale after 15 seconds update: 5000, // Update mtime every 5 seconds retries: { retries: 5, factor: 2, minTimeout: 1000, maxTimeout: 10000, randomize: true, }, onCompromised: (err) => { console.error('Lock was compromised:', err.message); // Implement critical error handling here, e.g., exit process process.exit(1); }, }; async function accessResourceWithLock() { let release; try { // Ensure the target file exists before trying to lock it await fs.writeFile(filePath, 'Initial content.', { flag: 'a+' }); console.log('Attempting to acquire lock...'); release = await lock(filePath, lockFileOptions); console.log('Lock acquired. Performing sensitive operation...'); // Simulate work await new Promise(resolve => setTimeout(resolve, Math.random() * 3000 + 1000)); await fs.appendFile(filePath, `\nAccessed at ${new Date().toISOString()} by process ${process.pid}`); console.log('Sensitive operation complete. Releasing lock...'); } catch (error) { console.error('Failed to acquire or release lock:', error.message); if (error.code === 'ELOCKED') { console.warn('File is already locked by another process.'); } } finally { if (release) { try { await release(); console.log('Lock released successfully.'); } catch (error) { console.error('Error releasing lock:', error.message); } } else { // If lock was never acquired, or release failed (e.g. compromised), ensure cleanup. // In a real scenario, you might also attempt `unlock(filePath)` here if `release` failed // but only if you are confident it's safe to force-unlock. console.log('No release function available or lock acquisition failed. No explicit release needed/possible.'); } } } accessResourceWithLock();
Debug
Known issues
gotchaUsing different `stale` or `update` option values for the same file across different processes can lead to race conditions and multiple processes acquiring what they believe is an exclusive lock.
fix
Ensure consistent `stale` and `update` options are used for a given lockable file across all processes accessing it. Store these values in a shared configuration.
affects: >=1.0.0
gotchaManual removal of a lockfile by an external process or user can lead to the lock being compromised. `proper-lockfile` detects certain compromises (failed updates) but cannot detect external, arbitrary deletions which would allow another process to acquire a lock immediately after.
fix
Avoid direct manipulation of `.lock` files. Always use the library's `release()` or `unlock()` functions. Implement robust error handling for `onCompromised` callbacks.
affects: >=1.0.0
gotchaThe `release()` function returned by `lock()` can reject its promise if the lock has been compromised (e.g., updates failed, or it became stale). Consumers must handle this rejection to prevent unhandled promise rejections or misinterpretations of lock status.
fix
Always `await` or `.catch()` the promise returned by the `release()` function and implement appropriate error recovery or logging.
affects: >=1.0.0
gotchaThe default `stale` option is 10 seconds (10000ms), with a minimum of 5 seconds (5000ms). Setting `stale` too low for long-running operations or processes with high latency can cause locks to be considered stale prematurely.
fix
Adjust the `stale` option according to the expected maximum duration of the critical section and the network/filesystem characteristics. The `update` interval (default `stale/2`) should also be considered.
affects: >=1.0.0
Errors
Common errors & fixes
Error: Lock file is already being held
Another process currently holds the lock, and the current attempt to acquire it did not succeed within the configured retries/timeout.
fix
Handle the `ELOCKED` error code (if available) or the general error by implementing retry logic with exponential backoff or waiting for the lock to be released. Configure the `retries` option for `lock()`.
Error: Lock was already compromised
The lockfile's integrity check failed during a background update, or it was manually removed, indicating the lock is no longer reliably held by this process. This error can be thrown by the `onCompromised` callback or when `release()` is called on a compromised lock.
fix
Implement robust error handling in the `onCompromised` callback and for the `release()` promise. This usually indicates a critical state requiring process termination or immediate re-evaluation of resource access.
Error: ENOENT: no such file or directory, lstat 'path/to/file.txt.lock'
The base file (e.g., 'path/to/file.txt') did not exist when `lock()` or `check()` was called, and `realpath` option is true (default). `proper-lockfile` expects the target file to exist to resolve its real path and create the `.lock` file alongside it.
fix
Ensure the target file you intend to lock exists before calling `lock()` or `check()`. Use `fs.promises.writeFile(filePath, '', { flag: 'a+' })` to create it if it doesn't exist.
Upgrade
Version history
4.1.2latest on npm
Audit
Dependencies
debugoptionalUsed for internal debugging and logging.
p-retryrequiredHandles retries for acquiring locks, providing configurable backoff strategies.
Agent activity
18 hits · last 30 days
node
16
Resources
proper-lockfile — npm install proper-lockfile · libregistry