{"_id":"mutex","_rev":"14-919f600fef86ccf1988cb8aa9734ea98","name":"mutex","description":"Mutual exclusion made easy","dist-tags":{"latest":"1.0.4"},"versions":{"0.0.1":{"name":"mutex","description":"Distributed mutex for nodejs with redis backend","version":"0.0.1","url":"http://github.com/0ctave/node-mutex","author":{"name":"Yuriy Bogdanov","email":"chinsay@gmail.com"},"main":"lib/mutex","engines":{"node":">=0.4.5"},"dependencies":{"redis":">=0.5.6"},"devDependencies":{},"_id":"mutex@0.0.1","_engineSupported":true,"_npmVersion":"1.0.1rc9","_nodeVersion":"v0.4.5","_defaultsLoaded":true,"dist":{"shasum":"83fe5532d753528390d686031ef2919081b7b5ad","tarball":"https://registry.npmjs.org/mutex/-/mutex-0.0.1.tgz","integrity":"sha512-xFj9RBm4smRsKoFSZm1c11XrNFdzq829IQ4Ahww2wxmnTOb7z/MA8h5fAJdrHPih3bnp0H1lHioCW6fg+TiwFw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC0PjpFNClUi1FI0v0eZdTK4sxXhEcKSoavzhkT+7WtOQIgTmUHcZEN0RNDFVfwkVV7qcfbV4OnkTpM4rhrqWVnMRo="}]},"scripts":{}},"0.0.2":{"name":"mutex","description":"Distributed mutex for nodejs with redis backend","version":"0.0.2","url":"http://github.com/0ctave/node-mutex","author":{"name":"Yuriy Bogdanov","email":"chinsay@gmail.com"},"main":"lib/mutex","engines":{"node":">=0.4.5"},"dependencies":{"redis":">=0.5.6"},"_id":"mutex@0.0.2","dist":{"shasum":"7d721a4dcfa455dd2d5d84efc49d7607b42b76f0","tarball":"https://registry.npmjs.org/mutex/-/mutex-0.0.2.tgz","integrity":"sha512-aM2dWStqh9TeKgLJSifP8d4gBdevUat6hQqYIB7xGtPRL7mWfMNiRixWH4zo81qMlxoRgNqAE2Qo0TWwnoxK1w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCG8usgdmoWPJufVMcK0fTriBZ5a+iWKfAmIHkA+I8U/AIgPz2d+ongOhN5s7Eb42a2wSUqJ9u9F2E5sIR1iov/yzc="}]},"_npmVersion":"1.1.66","_npmUser":{"name":"octave","email":"chinsay@gmail.com"},"maintainers":[{"name":"octave","email":"chinsay@gmail.com"}]},"1.0.1":{"name":"mutex","version":"1.0.1","description":"Mutual exclusion made easy","main":"lib/factory","directories":{"test":"test"},"scripts":{"test":"tap test/**/*.js -t=180 --reporter=spec --statements=100 --coverage-report=text-summary","coverage":"tap test/**/*.js -t=180 --reporter=spec --coverage --coverage-report=html && open -a \"Safari\" coverage/index.html","test-ci":"tap test/**/*.js -t=180 --reporter=spec --statements=100","tdd":"nodemon -x npm test","doctoc":"doctoc README.md","prepublish":"in-publish && npm run doctoc || not-in-publish","benchmark":"node benchmark","benchmarks":"for run in {1..10}\n do\n npm run benchmark || true \n  done","fuzz":"while node fuzz/raft.js; do :; done; afplay /System/Library/Sounds/Glass.aiff"},"repository":{"type":"git","url":"git+https://github.com/ben-ng/mutex-js.git"},"keywords":["distributed","lock","critical","synchronized","lamport","transaction","ricart","agrawala"],"author":{"name":"Ben Ng","email":"me@benng.me"},"license":"MIT","bugs":{"url":"https://github.com/ben-ng/mutex-js/issues"},"homepage":"https://github.com/ben-ng/mutex-js#readme","devDependencies":{"async":"^1.5.2","benchmark":"^2.0.0","doctoc":"^0.15.0","joi":"^6.6.1","jstrace-bars":"^1.2.3","nodemon":"^1.4.1","once":"^1.3.3","tap":"^5.1.1"},"dependencies":{"bluebird":"^2.9.34","conflux":"^1.1.3","defined":"^1.0.0","in-publish":"^2.0.0","lodash":"^4.0.0","redis":"^2.4.2","uuid":"^2.0.1"},"gitHead":"c9b25a89f3469ee1b7e149b23846ffe349cf3112","_id":"mutex@1.0.1","_shasum":"c389310be42a7860c8f3180ebbacefe85dbe45a2","_from":".","_npmVersion":"3.4.0","_nodeVersion":"5.0.0","_npmUser":{"name":"benng","email":"me@benng.me"},"maintainers":[{"name":"benng","email":"me@benng.me"}],"dist":{"shasum":"c389310be42a7860c8f3180ebbacefe85dbe45a2","tarball":"https://registry.npmjs.org/mutex/-/mutex-1.0.1.tgz","integrity":"sha512-rSWcxFSZWAQ08sieVK/abFQ1dRaInoCAYaL26nyLRZSSXwyjfY1ZXg31Q4H7QOdFdkv5VRvNQ4Odg6WtK/tqdg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC1TGGW3Yt4XXjDrOHRH384rEEDc5QtRkWW8Mp0lR62SwIhALU2jqkOJb9gt7LBrBZqYuQBfxY5glx6erScjp4DKsMI"}]}},"1.0.2":{"name":"mutex","version":"1.0.2","description":"Mutual exclusion made easy","main":"lib/factory","directories":{"test":"test"},"scripts":{"test":"tap test/**/*.js -t=180 --reporter=spec --statements=100 --coverage-report=text-summary","coverage":"tap test/**/*.js -t=180 --reporter=spec --coverage --coverage-report=html && open -a \"Safari\" coverage/index.html","test-ci":"tap test/**/*.js -t=180 --reporter=spec --statements=100","tdd":"nodemon -x npm test","doctoc":"doctoc README.md","prepublish":"in-publish && npm run doctoc || not-in-publish","benchmark":"node benchmark","benchmarks":"for run in {1..10}\n do\n npm run benchmark || true \n  done","fuzz":"while node fuzz/raft.js; do :; done; afplay /System/Library/Sounds/Glass.aiff"},"repository":{"type":"git","url":"git+https://github.com/ben-ng/mutex-js.git"},"keywords":["distributed","lock","critical","synchronized","lamport","transaction","ricart","agrawala"],"author":{"name":"Ben Ng","email":"me@benng.me"},"license":"MIT","bugs":{"url":"https://github.com/ben-ng/mutex-js/issues"},"homepage":"https://github.com/ben-ng/mutex-js#readme","devDependencies":{"async":"^1.5.2","benchmark":"^2.0.0","doctoc":"^0.15.0","joi":"^6.6.1","jstrace-bars":"^1.2.3","nodemon":"^1.4.1","once":"^1.3.3","tap":"^5.1.1"},"dependencies":{"bluebird":"^2.9.34","conflux":"^1.1.3","defined":"^1.0.0","in-publish":"^2.0.0","lodash":"^4.0.0","redis":"^2.4.2","uuid":"^2.0.1"},"gitHead":"9634b839ce63a791163addbbf72b13b550221e95","_id":"mutex@1.0.2","_shasum":"56d0dc0dba9f5f7d24bc2d96acb75dcf3ce3e68c","_from":".","_npmVersion":"3.6.0","_nodeVersion":"5.0.0","_npmUser":{"name":"benng","email":"me@benng.me"},"maintainers":[{"name":"benng","email":"me@benng.me"}],"dist":{"shasum":"56d0dc0dba9f5f7d24bc2d96acb75dcf3ce3e68c","tarball":"https://registry.npmjs.org/mutex/-/mutex-1.0.2.tgz","integrity":"sha512-YvL+oJjrSn60brd0XX1vMqeqNGAu8CQzUIp3Z1KkZessES4eh3hFU0kn1nfv2j6JUBq3N5FCBvdtgUpxpkSkZg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH9EC9LhxE3z4vzBHvbkdL1dTmmBXB4SjHs+8xZmmODKAiEApRaNGZwjR92+pHgV4jJCAjM/DskN/YDWIgwH7dBkno0="}]},"_npmOperationalInternal":{"host":"packages-9-west.internal.npmjs.com","tmp":"tmp/mutex-1.0.2.tgz_1454998666984_0.8320899291429669"}},"1.0.3":{"name":"mutex","version":"1.0.3","description":"Mutual exclusion made easy","main":"lib/factory","directories":{"test":"test"},"scripts":{"test":"tap test/**/*.js -t=180 --reporter=spec --statements=100 --coverage-report=text-summary","coverage":"tap test/**/*.js -t=180 --reporter=spec --coverage --coverage-report=html && open -a \"Safari\" coverage/index.html","test-ci":"tap test/**/*.js -t=180 --reporter=spec --statements=100","tdd":"nodemon -x npm test","doctoc":"doctoc README.md","prepublish":"in-publish && npm run doctoc || not-in-publish","benchmark":"node benchmark","benchmarks":"for run in {1..10}\n do\n npm run benchmark || true \n  done","fuzz":"while node fuzz/raft.js; do :; done; afplay /System/Library/Sounds/Glass.aiff"},"repository":{"type":"git","url":"git+https://github.com/ben-ng/mutex-js.git"},"keywords":["distributed","lock","critical","synchronized","lamport","transaction","ricart","agrawala"],"author":{"name":"Ben Ng","email":"me@benng.me"},"license":"MIT","bugs":{"url":"https://github.com/ben-ng/mutex-js/issues"},"homepage":"https://github.com/ben-ng/mutex-js#readme","devDependencies":{"async":"^1.5.2","benchmark":"^2.0.0","doctoc":"^0.15.0","jstrace-bars":"^1.2.3","nodemon":"^1.4.1","once":"^1.3.3","tap":"^5.1.1"},"dependencies":{"bluebird":"^2.9.34","conflux":"^1.1.3","defined":"^1.0.0","in-publish":"^2.0.0","joi":"^6.6.1","lodash":"^4.0.0","redis":"^2.4.2","uuid":"^2.0.1"},"gitHead":"37cdf7f6df96c9623856d2004d4ba040498f9aa8","_id":"mutex@1.0.3","_shasum":"968e8c58d4778c60f77e6313deecf8da70d1e738","_from":".","_npmVersion":"3.7.1","_nodeVersion":"5.6.0","_npmUser":{"name":"benng","email":"me@benng.me"},"maintainers":[{"name":"benng","email":"me@benng.me"}],"dist":{"shasum":"968e8c58d4778c60f77e6313deecf8da70d1e738","tarball":"https://registry.npmjs.org/mutex/-/mutex-1.0.3.tgz","integrity":"sha512-9vxrz7a3bEIVMXU0EhmWqMC8FbjbdkAZY39U9VMGsftAdu0iRAadDqAEwIDUnckbEwtWzWp5TZ4FmPxyafW+MA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCPj+TY0M4PAm3d4c686XfoVjpoXv6AD4bEwwbQEx9l9gIgJDWQ72ksYkF0E7Zr6oJ4qTkTu5mdr1kEP9+/2pkyPyY="}]},"_npmOperationalInternal":{"host":"packages-6-west.internal.npmjs.com","tmp":"tmp/mutex-1.0.3.tgz_1456442423748_0.21974409045651555"}},"1.0.4":{"name":"mutex","version":"1.0.4","description":"Mutual exclusion made easy","main":"lib/factory","directories":{"test":"test"},"scripts":{"test":"tap test/**/*.js -t=180 --reporter=spec --statements=100 --coverage-report=text-summary","coverage":"tap test/**/*.js -t=180 --reporter=spec --statements=100 --coverage --coverage-report=html && open -a \"Safari\" coverage/index.html","test-ci":"tap test/**/*.js -t=180 --reporter=spec --statements=100","tdd":"nodemon -x npm test","doctoc":"doctoc README.md","prepublish":"in-publish && npm run doctoc || not-in-publish","benchmark":"node benchmark","benchmarks":"for run in {1..10}\n do\n npm run benchmark || true \n  done","fuzz":"while node fuzz/raft.js; do :; done; afplay /System/Library/Sounds/Glass.aiff"},"repository":{"type":"git","url":"git+https://github.com/ben-ng/mutex-js.git"},"keywords":["distributed","lock","critical","synchronized","lamport","transaction","ricart","agrawala"],"author":{"name":"Ben Ng","email":"me@benng.me"},"license":"MIT","bugs":{"url":"https://github.com/ben-ng/mutex-js/issues"},"homepage":"https://github.com/ben-ng/mutex-js#readme","devDependencies":{"benchmark":"^2.0.0","doctoc":"^0.15.0","jstrace-bars":"^1.2.3","nodemon":"^1.4.1","once":"^1.3.3","tap":"^5.1.1"},"dependencies":{"async":"^1.5.2","bluebird":"^2.9.34","conflux":"^1.1.3","defined":"^1.0.0","in-publish":"^2.0.0","joi":"^6.6.1","lodash":"^4.0.0","redis":"^2.4.2","uuid":"^2.0.1"},"gitHead":"58bdb25627cfac24df62000028d8e0f330b88b95","_id":"mutex@1.0.4","_shasum":"1c2c55cb52df6a27d87404e03d34f2da3805732b","_from":".","_npmVersion":"4.0.1","_nodeVersion":"7.0.0","_npmUser":{"name":"benng","email":"me@benng.me"},"dist":{"shasum":"1c2c55cb52df6a27d87404e03d34f2da3805732b","tarball":"https://registry.npmjs.org/mutex/-/mutex-1.0.4.tgz","integrity":"sha512-2/C++YmPDHzAPbtsOAs2Xet1A8850/Xgj+Dw2iU3E46mdH6GvoxswPp13alvjQor+XglV7BDFUQI3A0Ib4FeuA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC2W2W87bl7EsmQwLvTM4ro2pzjxGgaNhAb/yFJuSTk/wIgQDM0P8974KIljVYtnGjVrN9xWHkVwdf5el5KPYaYBZU="}]},"maintainers":[{"name":"benng","email":"me@benng.me"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/mutex-1.0.4.tgz_1477620858280_0.13572735036723316"}}},"maintainers":[{"name":"benng","email":"me@benng.me"}],"time":{"modified":"2022-06-20T14:52:34.442Z","created":"2011-06-24T12:38:56.926Z","0.0.1":"2011-06-24T12:38:57.643Z","0.0.2":"2013-03-19T19:57:16.496Z","1.0.1":"2016-01-31T18:56:53.172Z","1.0.2":"2016-02-09T06:17:48.027Z","1.0.3":"2016-02-25T23:20:24.546Z","1.0.4":"2016-10-28T02:14:20.450Z"},"author":{"name":"Ben Ng","email":"me@benng.me"},"readme":"# Mutex [![Build Status](https://img.shields.io/circleci/project/ben-ng/mutex-js.svg)](https://circleci.com/gh/ben-ng/mutex-js/tree/master) [![Coverage Status](https://img.shields.io/coveralls/ben-ng/mutex-js/master.svg)](https://coveralls.io/github/ben-ng/mutex-js?branch=master) [![npm version](https://img.shields.io/npm/v/mutex.svg)](https://www.npmjs.com/package/mutex)\n\nThis is a keyed mutex. It abstracts over different [Strategies](#strategies) for mutual exclusion, so you can choose your own tradeoffs.\n\n**IMPORTANT:** Please read the section on [correctness](#correctness) before using this module.\n\n```sh\nnpm install mutex\n```\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n**Contents**\n\n- [Constructing Instances](#constructing-instances)\n  - [Strategies](#strategies)\n  - [Channels](#channels)\n- [Methods](#methods)\n    - [Mutex.lock(String key, [Object options], [Function callback]) => Lock](#mutexlockstring-key-object-options-function-callback--lock)\n  - [Mutex.unlock(Lock lock)](#mutexunlocklock-lock)\n  - [Lock.isValidForDuration() => Boolean](#lockisvalidforduration--boolean)\n  - [Lock.getKey() => String](#lockgetkey--string)\n  - [Lock.getTTL() => Number](#lockgetttl--number)\n- [Examples](#examples)\n  - [Atomic Increments](#atomic-increments)\n    - [Sample Code: Performing Atomic Increments (Callbacks)](#sample-code-performing-atomic-increments-callbacks)\n    - [Sample Code: Performing Atomic Increments (Promises)](#sample-code-performing-atomic-increments-promises)\n- [Performance](#performance)\n- [Correctness](#correctness)\n  - [Test Suite](#test-suite)\n  - [Fuzzer](#fuzzer)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Constructing Instances\n\nTo use `mutex` you need to pick a [Strategy](#strategies). Distributed strategies require the use of a [Channel](#channels). The easiest way to get started is to use the `redis` strategy.\n\n```js\nvar mutex = require('mutex')\nvar uuid = require('uuid')\n\n// Creating a non-distributed mutex with a redis server\nvar a = mutex({\n  id: uuid.v4()\n, strategy: {\n    name: 'redis'\n\n    // optional, defaults to localhost\n  , connectionString: 'redis://127.0.0.1'\n  })\n})\n\n// Creating a distributed mutex with 5 nodes, using redis for communication\nvar b = mutex({\n  id: uuid.v4()\n, strategy: {\n    name: 'raft'\n\n    // required: how many mutex instances you are creating\n  , clusterSize: 5\n\n    // required: pick any channel that gaggle supports\n  , channel: {\n      name: 'redis'\n\n      // the following options are specific to the channel you picked\n    , channelName: 'mypubsubchannel'\n    }\n  }\n})\n```\n\n### Strategies\n\nStrategy  | Distributed? | Failure Tolerance                                                                                       | Notes\n--------- | ------------ | ------------------------------------------------------------------------------------------------------- | ----------------\n`redis`     | No           | Redis can't fail, but any number of processes can fail as locks automatically expire                    | Uses `SET EX NX`\n`raft`      | Yes          | Less than half of all processes can fail, or be out of contact because of network partitions.           | Based on [Raft](http://raft.github.io)\n\n### Channels\n\nThe distributed mutex is built on [Conflux](https://github.com/ben-ng/conflux), which is in turn built on [Gaggle](https://github.com/ben-ng/gaggle), an implementation of the [Raft](http://raft.github.io) consensus algorithm. This means that you can use [any channel that Gaggle supports](https://github.com/ben-ng/gaggle#channels).\n\n## Methods\n\nOnce you've constructed an instance of `Mutex`, you can use it to acquire and release locks. All `Mutex` methods support callbacks or promises. Omit the `callback` parameter and the method will return a promise.\n\n#### Mutex.lock(String key, [Object options], [Function callback]) => Lock\n\n```js\nm.lock('foobar', {\n  duration: 1000     // how long must the lock be valid for?\n, maxWait: 5000      // how long to wait for another process before giving up?\n}, function (err, lock) {\n  // `lock` is an opaque object with several methods documented below\n  // it is what you should hand as the first argument to `Mutex.unlock`\n})\n```\n\n### Mutex.unlock(Lock lock)\n\n```js\n// `lock` is the opaque object returned from `Mutex.lock`\nm.unlock(lock, function (err) {})\n```\n\n### Lock.isValidForDuration() => Boolean\n\n```js\nif (lock.isValidForDuration(5000)) {\n  // critical section\n}\nelse {\n  // toss out the lock, request a new one\n}\n```\n\n### Lock.getKey() => String\n\n```js\nlock.getKey()\n```\n\nReturns the key that the lock is for.\n\n### Lock.getTTL() => Number\n\n```js\nlock.getTTL()\n```\n\n## Examples\n\n### Atomic Increments\n\nMultiple processes are simultaneously trying to increment the same value in a database that only supports \"GET\" and \"SET\" commands. A situation like this might arise:\n\n```\nProcess A:\n1. GET x => 0\n2. SET x 1\n\nProcess B:\n1. GET x => 0\n2. SET x 1\n\nResult: x = 1\nExpected: x = 2\n```\n\nThis is known as the \"lost update\" problem. You can solve this problem with Mutex, which supports both callbacks and promises.\n\n#### Sample Code: Performing Atomic Increments (Callbacks)\n\n```js\n\nvar mutex = require('mutex')\n  , uuid = require('uuid')\n  , m = mutex({\n      id: uuid.v4()\n    , strategy: {\n        name: 'redis'\n      }\n    })\n  , db = require('your-hypothetical-database')\n\nm.lock('myLock', {    // You can create multiple locks by naming them\n  duration: 1000      // Hold the lock for no longer than 1 second\n, maxWait: 5000       // Wait for no longer than 5s to acquire the lock\n}, function (err, lock) {\n  // Handle any errors. No need to release the lock as it will\n  // automatically expire if the db.get or db.set commands failed.\n\n  // You should check if the lock is valid for the needed duration\n  // just before entering the critical section to protect against\n  // lock drift, garbage collection cycles, network delays...\n  if (lock.isValidForDuration(1000)) {\n    // Begin critical section\n    db.get('x', (err, val) => {\n\n      // Err handling omitted for brevity\n      db.set('x', val + 1, function (err) {\n\n        m.unlock('myLock', function (err) {\n          // End critical section\n        })\n      })\n    })\n  }\n  else {\n    // Error because the lock was not valid for the needed duration\n  }\n})\n\n```\n\n#### Sample Code: Performing Atomic Increments (Promises)\n\n```js\n\nvar mutex = require('mutex')\n  , m = mutex({\n      id: uuid.v4()\n    , strategy: {\n        name: 'redis'\n      }\n    })\n  , db = require('your-hypothetical-database')\n\nm.lock('myLock', {    // You can create multiple locks by naming them\n  duration: 1000      // Request a lock valid for at least 1 second\n, maxWait: 5000       // Wait for no longer than 5s to acquire the lock\n})\n.then(lock => {\n  // You should check if the lock is valid for the needed duration\n  // just before entering the critical section to protect against\n  // lock drift, garbage collection cycles, network delays...\n  if (lock.isValidForDuration(1000)) {\n    // Begin critical section\n    return db.get('x')\n    .then(value => {\n      return db.set('x', value + 1)\n    })\n    // End critical section\n    .then(() => {\n      return m.unlock(lock)\n    })\n  }\n  else {\n    return Promise.reject(new Error('The lock was not valid for the needed duration'))\n  }\n})\n.catch(err => {\n  // Handle any errors. No need to release the lock as it will\n  // automatically expire if the db.get or db.set commands failed.\n})\n\n```\n\nBy enclosing the `GET` and `SET` commands within the critical section, we guarantee that updates are not lost.\n\nReturns the UNIX timestamp that the lock will expire at.\n\n## Performance\n\nEach test performs an asynchronous operation that takes approximately 70ms a hundred times. The \"sequential\" test is a single process performing all one hundred operations itself, in series. The \"Worst Case\" tests are ten processes trying to acquire the same lock before performing the task. The \"Best Case\" tests are ten processes acquiring different locks before performing the task.\n\n```\nSequential - Baseline x 0.15 ops/sec ±0.35% (5 runs sampled)\nRedis - Worst Case x 0.13 ops/sec ±2.55% (5 runs sampled)\nRaft - Worst Case x 0.08 ops/sec ±6.34% (5 runs sampled)\nRedis - Best Case x 0.63 ops/sec ±1.08% (6 runs sampled)\nRaft - Best Case x 0.35 ops/sec ±1.47% (6 runs sampled)\n\n      Raft - Worst Case | ######################################## | 120.77 ms/op\n     Redis - Worst Case | ##########################               | 78.03 ms/op\n  Sequential - Baseline | ######################                   | 67.88 ms/op\n       Raft - Best Case | ##########                               | 28.72 ms/op\n      Redis - Best Case | #####                                    | 15.8 ms/op\n```\n\nNote that ms/operation can be much lower than the ~70ms each task takes when tasks are being performed in parallel.\n\nYou can run the benchmark suite with `npm run benchmark`, or run it ten times with `npm run benchmarks` (the results aren't very stable due to the randomness in Raft leader election).\n\n## Correctness\n\n**This mutex should not be used if correctness is important to you.** While this module is very good at compensating for garbage collection pauses and clock drift, you cannot be 100% certain that the lock you have acquired has expired because processes get paused for bizzare reasons out of your control. Please read Martin Kleppmann's [blog post about distributed locking](http://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html) to better understand this.\n\nIf you can deal with a mutex that is very rarely wrong (<0.000001% of the time), then go ahead and use this.\n\n### Test Suite\n\nMutex has a comprehensive test suite, and releases always have 100% statement, branch, and function coverage.\n\n```sh\n# You need to install Redis to run the tests!\nbrew install redis\n# To have launchd start redis now and restart at login:\n# brew services start redis\n# Or, if you don't want/need a background service you can just run:\n# redis-server /usr/local/etc/redis.confV\nnpm test\n```\n\n### Fuzzer\n\nMutex has a fuzzer can detect very subtle problems (such as those caused by clock drift / garbage collection pauses). You can run it with `npm run fuzz`. It will keep running until a consistency issue is detected. The most subtle issues detected so far required up to fifteen hours of fuzzing.\n\n## License\n\nCopyright (c) 2015 Ben Ng <me@benng.me>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","homepage":"https://github.com/ben-ng/mutex-js#readme","keywords":["distributed","lock","critical","synchronized","lamport","transaction","ricart","agrawala"],"repository":{"type":"git","url":"git+https://github.com/ben-ng/mutex-js.git"},"bugs":{"url":"https://github.com/ben-ng/mutex-js/issues"},"license":"MIT","users":{"timothy_vann":true}}