proper-lockfile vs lockfile vs async-lock
ロックファイル管理
proper-lockfilelockfileasync-lock類似パッケージ:

ロックファイル管理

ロックファイル管理ライブラリは、複数のプロセスやスレッドが同時にリソースにアクセスするのを防ぐためのツールです。これらのライブラリは、特定のリソース(ファイル、データベース、メモリなど)へのアクセスを制御し、データの整合性を保つためにロック機構を提供します。async-lockは、非同期関数の実行をロックするためのシンプルなライブラリで、特定のキーに基づいてロックを管理します。lockfileは、ファイルシステムを使用してロックを管理するライブラリで、プロセス間でのロックを実現します。proper-lockfileは、ファイルベースのロックを提供し、ロックの取得、解放、タイムアウトなどの機能を備えています。これらのライブラリは、競合状態を防ぎ、データの整合性を保つために役立ちます。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
proper-lockfile11,085,414271-215年前MIT
lockfile3,098,371259-128年前ISC
async-lock042618.3 kB52年前MIT

機能比較: proper-lockfile vs lockfile vs async-lock

ロックの方法

  • proper-lockfile:

    proper-lockfileは、ファイルベースのロックを提供し、ロックファイルを作成して管理します。ロックの取得、解放、タイムアウトを制御できるため、より柔軟で安全なロック機構を提供します。

  • lockfile:

    lockfileは、ファイルシステムを使用してロックを管理します。指定したファイルにロックをかけることで、同じファイルにアクセスする他のプロセスをブロックします。

  • async-lock:

    async-lockは、非同期関数の実行をロックするために、メモリ内でロックを管理します。特定のキーに基づいてロックを取得し、同じキーに対する複数の非同期操作を順番に実行します。

非同期処理のサポート

  • proper-lockfile:

    proper-lockfileも非同期処理を直接サポートしているわけではありませんが、ファイルベースのロックを提供するため、プロセス間の競合を防ぐことができます。

  • lockfile:

    lockfileは、非同期処理を直接サポートしているわけではありませんが、ファイルベースのロックを使用するため、プロセス間の競合を防ぐことができます。

  • async-lock:

    async-lockは、非同期処理をサポートするために設計されています。非同期関数の実行をロックすることで、競合状態を防ぎます。

ロックのタイムアウト

  • proper-lockfile:

    proper-lockfileは、ロックのタイムアウト機能を提供します。指定した時間内にロックを取得できない場合、タイムアウトエラーを返します。これにより、デッドロックのリスクを軽減できます。

  • lockfile:

    lockfileは、ロックのタイムアウト機能を提供していません。ロックは、ロックを取得したプロセスが終了するまで保持されます。

  • async-lock:

    async-lockは、ロックのタイムアウト機能を提供していません。ロックは、ロックを取得した関数が完了するまで保持されます。

ロックの解放

  • proper-lockfile:

    proper-lockfileは、ロックを手動で解放する必要がありますが、ロックファイルを明示的に削除することで解放できます。

  • lockfile:

    lockfileは、ロックを手動で解放する必要があります。ロックを取得したプロセスが終了するまで、ロックは保持されます。

  • async-lock:

    async-lockは、ロックを自動的に解放します。ロックを取得した関数が完了すると、ロックは自動的に解放されます。

Ease of Use: Code Examples

  • proper-lockfile:

    proper-lockfileの使用例

    const { lock, unlock } = require('proper-lockfile');
    const lockFilePath = './resource.lock';
    
    const lockResource = async () => {
      await lock(lockFilePath);
      console.log('Resource is locked');
      // ここでリソースにアクセス
      await new Promise(resolve => setTimeout(resolve, 2000));
      await unlock(lockFilePath);
      console.log('Resource is unlocked');
    };
    
    lockResource();
    
  • lockfile:

    lockfileの使用例

    const { lock, unlock } = require('lockfile');
    const lockFilePath = './resource.lock';
    
    const lockResource = async () => {
      await lock(lockFilePath);
      console.log('Resource is locked');
      // ここでリソースにアクセス
      await new Promise(resolve => setTimeout(resolve, 2000));
      await unlock(lockFilePath);
      console.log('Resource is unlocked');
    };
    
    lockResource();
    
  • async-lock:

    async-lockの使用例

    const AsyncLock = require('async-lock');
    const lock = new AsyncLock();
    
    const task = async (key) => {
      await lock.acquire(key, async () => {
        // ここで非同期処理を実行
        console.log(`Task for ${key} is running`);
        await new Promise(resolve => setTimeout(resolve, 1000));
      });
    };
    
    Promise.all([task('key1'), task('key1'), task('key2')]);
    

選び方: proper-lockfile vs lockfile vs async-lock

  • proper-lockfile:

    ファイルベースのロックをより厳密に管理したい場合は、proper-lockfileを選択してください。ロックのタイムアウトや解放に関する詳細な制御が可能で、より安全なロック機構を提供します。

  • lockfile:

    プロセス間でのロックをファイルシステムを介して管理したい場合は、lockfileを選択してください。特に、複数のプロセスが同じリソースにアクセスする可能性がある環境で効果的です。

  • async-lock:

    非同期処理の競合を防ぎたい場合は、async-lockを選択してください。特定のキーに基づいてロックを管理できるため、同じリソースに対する複数の非同期操作を制御するのに適しています。

proper-lockfile のREADME

proper-lockfile

NPM version Downloads Build Status Coverage Status Dependency status Dev Dependency status

An inter-process and inter-machine lockfile utility that works on a local or network file system.

Installation

$ npm install proper-lockfile

Design

There are various ways to achieve file locking.

This library utilizes the mkdir strategy which works atomically on any kind of file system, even network based ones. The lockfile path is based on the file path you are trying to lock by suffixing it with .lock.

When a lock is successfully acquired, the lockfile's mtime (modified time) is periodically updated to prevent staleness. This allows to effectively check if a lock is stale by checking its mtime against a stale threshold. If the update of the mtime fails several times, the lock might be compromised. The mtime is supported in almost every filesystem.

Comparison

This library is similar to lockfile but the latter has some drawbacks:

  • It relies on open with O_EXCL flag which has problems in network file systems. proper-lockfile uses mkdir which doesn't have this issue.

O_EXCL is broken on NFS file systems; programs which rely on it for performing locking tasks will contain a race condition.

  • The lockfile staleness check is done via ctime (creation time) which is unsuitable for long running processes. proper-lockfile constantly updates lockfiles mtime to do proper staleness check.

  • It does not check if the lockfile was compromised which can lead to undesirable situations. proper-lockfile checks the lockfile when updating the mtime.

  • It has a default value of 0 for the stale option which isn't good because any crash or process kill that the package can't handle gracefully will leave the lock active forever.

Compromised

proper-lockfile does not detect cases in which:

  • A lockfile is manually removed and someone else acquires the lock right after
  • Different stale/update values are being used for the same file, possibly causing two locks to be acquired on the same file

proper-lockfile detects cases in which:

  • Updates to the lockfile fail
  • Updates take longer than expected, possibly causing the lock to become stale for a certain amount of time

As you see, the first two are a consequence of bad usage. Technically, it was possible to detect the first two but it would introduce complexity and eventual race conditions.

Usage

.lock(file, [options])

Tries to acquire a lock on file or rejects the promise on error.

If the lock succeeds, a release function is provided that should be called when you want to release the lock. The release function also rejects the promise on error (e.g. when the lock was already compromised).

Available options:

  • stale: Duration in milliseconds in which the lock is considered stale, defaults to 10000 (minimum value is 5000)
  • update: The interval in milliseconds in which the lockfile's mtime will be updated, defaults to stale/2 (minimum value is 1000, maximum value is stale/2)
  • retries: The number of retries or a retry options object, defaults to 0
  • realpath: Resolve symlinks using realpath, defaults to true (note that if true, the file must exist previously)
  • fs: A custom fs to use, defaults to graceful-fs
  • onCompromised: Called if the lock gets compromised, defaults to a function that simply throws the error which will probably cause the process to die
  • lockfilePath: Custom lockfile path. e.g.: If you want to lock a directory and create the lock file inside it, you can pass file as <dir path> and options.lockfilePath as <dir path>/dir.lock
const lockfile = require('proper-lockfile');

lockfile.lock('some/file')
.then((release) => {
    // Do something while the file is locked

    // Call the provided release function when you're done,
    // which will also return a promise
    return release();
})
.catch((e) => {
    // either lock could not be acquired
    // or releasing it failed
    console.error(e)
});

// Alternatively, you may use lockfile('some/file') directly.

.unlock(file, [options])

Releases a previously acquired lock on file or rejects the promise on error.

Whenever possible you should use the release function instead (as exemplified above). Still there are cases in which it's hard to keep a reference to it around code. In those cases unlock() might be handy.

Available options:

  • realpath: Resolve symlinks using realpath, defaults to true (note that if true, the file must exist previously)
  • fs: A custom fs to use, defaults to graceful-fs
  • lockfilePath: Custom lockfile path. e.g.: If you want to lock a directory and create the lock file inside it, you can pass file as <dir path> and options.lockfilePath as <dir path>/dir.lock
const lockfile = require('proper-lockfile');

lockfile.lock('some/file')
.then(() => {
    // Do something while the file is locked

    // Later..
    return lockfile.unlock('some/file');
});

.check(file, [options])

Check if the file is locked and its lockfile is not stale, rejects the promise on error.

Available options:

  • stale: Duration in milliseconds in which the lock is considered stale, defaults to 10000 (minimum value is 5000)
  • realpath: Resolve symlinks using realpath, defaults to true (note that if true, the file must exist previously)
  • fs: A custom fs to use, defaults to graceful-fs
  • lockfilePath: Custom lockfile path. e.g.: If you want to lock a directory and create the lock file inside it, you can pass file as <dir path> and options.lockfilePath as <dir path>/dir.lock
const lockfile = require('proper-lockfile');

lockfile.check('some/file')
.then((isLocked) => {
    // isLocked will be true if 'some/file' is locked, false otherwise
});

.lockSync(file, [options])

Sync version of .lock().
Returns the release function or throws on error.

.unlockSync(file, [options])

Sync version of .unlock().
Throws on error.

.checkSync(file, [options])

Sync version of .check(). Returns a boolean or throws on error.

Graceful exit

proper-lockfile automatically removes locks if the process exits, except if the process is killed with SIGKILL or it crashes due to a VM fatal error (e.g.: out of memory).

Tests

$ npm test
$ npm test -- --watch during development

The test suite is very extensive. There's even a stress test to guarantee exclusiveness of locks.

License

Released under the MIT License.