+1
-1
| { | ||
| "name": "redlock", | ||
| "version": "1.0.1", | ||
| "version": "2.0.0", | ||
| "description": "A node.js redlock implementation for distributed redis locks", | ||
@@ -5,0 +5,0 @@ "main": "redlock.js", |
+43
-12
@@ -93,3 +93,8 @@ [](https://www.npmjs.com/package/redlock) | ||
| // unlock your resource when you are done | ||
| return lock.unlock(); | ||
| return lock.unlock() | ||
| .catch(function(err) { | ||
| // we weren't able to reach redis; your lock will eventually | ||
| // expire, but you probably want to log this error | ||
| console.error(err); | ||
| }); | ||
| }); | ||
@@ -114,3 +119,8 @@ | ||
| // unlock your resource when you are done | ||
| return lock.unlock(); | ||
| return lock.unlock() | ||
| .catch(function(err) { | ||
| // we weren't able to reach redis; your lock will eventually | ||
| // expire, but you probably want to log this error | ||
| console.error(err); | ||
| }); | ||
| }); | ||
@@ -139,4 +149,12 @@ }); | ||
| using(redlock.disposer(resource, ttl), function(lock) { | ||
| // if we weren't able to reach redis, your lock will eventually | ||
| // expire, but you probably want to do something like log that | ||
| // an error occurred; if you don't pass a handler, this error | ||
| // will be ignored | ||
| function unlockErrorHandler(err) { | ||
| console.error(err); | ||
| } | ||
| using(redlock.disposer(resource, ttl, unlockErrorHandler), function(lock) { | ||
| // ...do something here... | ||
@@ -152,3 +170,3 @@ | ||
| ```js | ||
| using(redlock.disposer('locks:account:322456', 1000), function(lock) { | ||
| using(redlock.disposer('locks:account:322456', 1000, unlockErrorHandler), function(lock) { | ||
@@ -204,3 +222,7 @@ // ...do something here... | ||
| // unlock your resource when you are done | ||
| lock.unlock(); | ||
| lock.unlock(function(err) { | ||
| // we weren't able to reach redis; your lock will eventually | ||
| // expire, but you probably want to log this error | ||
| console.error(err); | ||
| }); | ||
| } | ||
@@ -253,3 +275,3 @@ }); | ||
| ###`Redlock.lock(resource, ttl, callback)` | ||
| ###`Redlock.lock(resource, ttl, ?callback)` | ||
| - `resource (string)` resource to be locked | ||
@@ -262,8 +284,9 @@ - `ttl (number)` time in ms until the lock expires | ||
| ###`Redlock.unlock(lock, callback)` | ||
| ###`Redlock.unlock(lock, ?callback)` | ||
| - `lock (Lock)` lock to be released | ||
| - `callback (function)` callback with no returning arguments | ||
| - `callback (function)` callback returning: | ||
| - `err (Error)` | ||
| ###`Redlock.extend(lock, ttl, callback)` | ||
| ###`Redlock.extend(lock, ttl, ?callback)` | ||
| - `lock (Lock)` lock to be extended | ||
@@ -276,7 +299,15 @@ - `ttl (number)` time in ms to extend the lock's expiration | ||
| ###`Lock.unlock(callback)` | ||
| - `callback (function)` callback with no returning arguments | ||
| ###`Redlock.disposer(resource, ttl, ?unlockErrorHandler)` | ||
| - `resource (string)` resource to be locked | ||
| - `ttl (number)` time in ms to extend the lock's expiration | ||
| - `callback (function)` error handler called with: | ||
| - `err (Error)` | ||
| ###`Lock.extend(ttl, callback)` | ||
| ###`Lock.unlock(?callback)` | ||
| - `callback (function)` callback returning: | ||
| - `err (Error)` | ||
| ###`Lock.extend(ttl, ?callback)` | ||
| - `ttl (number)` time in ms to extend the lock's expiration | ||
@@ -283,0 +314,0 @@ - `callback (function)` callback returning: |
+31
-10
@@ -130,4 +130,7 @@ 'use strict'; | ||
| // ``` | ||
| Redlock.prototype.disposer = function disposer(resource, ttl) { | ||
| return this._lock(resource, null, ttl).disposer(function(lock){ return lock.unlock(); }); | ||
| Redlock.prototype.disposer = function disposer(resource, ttl, errorHandler) { | ||
| errorHandler = errorHandler || function(err) {}; | ||
| return this._lock(resource, null, ttl).disposer(function(lock){ | ||
| return lock.unlock().catch(errorHandler); | ||
| }); | ||
| }; | ||
@@ -138,4 +141,6 @@ | ||
| // ------ | ||
| // This method unlocks the provided lock from all servers still persisting it. This is a | ||
| // best-effort attempt and as such fails silently. | ||
| // This method unlocks the provided lock from all servers still persisting it. It will fail | ||
| // with an error if it is unable to release the lock on a quorum of nodes, but will make no | ||
| // attempt to restore the lock on nodes that failed to release. It is safe to re-attempt an | ||
| // unlock or to ignore the error, as the lock will automatically expire after its timeout. | ||
| Redlock.prototype.release = | ||
@@ -146,10 +151,11 @@ Redlock.prototype.unlock = function unlock(lock, callback) { | ||
| // the lock has expired | ||
| if(lock.expiration < Date.now()) { | ||
| return resolve(); | ||
| } | ||
| // invalidate the lock | ||
| lock.expiration = 0; | ||
| // the number of servers which have agreed to release this lock | ||
| var votes = 0; | ||
| // the number of votes needed for consensus | ||
| var quorum = Math.floor(self.servers.length / 2) + 1; | ||
| // the number of async redis calls still waiting to finish | ||
@@ -165,4 +171,19 @@ var waiting = self.servers.length; | ||
| if(err) self.emit('clientError', err); | ||
| // - if the lock was released by this call, it will return 1 | ||
| // - if the lock has already been released, it will return 0 | ||
| // - it may have been re-acquired by another process | ||
| // - it may hava already been manually released | ||
| // - it may have expired | ||
| if(typeof response === 'number' && (response === 0 || response === 1)) | ||
| votes++; | ||
| if(waiting-- > 1) return; | ||
| return resolve(); | ||
| // SUCCESS: there is concensus and the lock is released | ||
| if(votes >= quorum) | ||
| return resolve(); | ||
| // FAILURE: the lock could not be released | ||
| return reject(new LockError('Unable to fully release the lock on resource "' + lock.resource + '".')); | ||
| } | ||
@@ -169,0 +190,0 @@ }) |
+49
-5
@@ -40,8 +40,13 @@ 'use strict'; | ||
| it('emits a clientError event when a client error occurs', function(done){ | ||
| redlock.once('clientError', function(err) { | ||
| var emitted = 0; | ||
| function test(err) { | ||
| assert.isNotNull(err); | ||
| done(); | ||
| }); | ||
| emitted++; | ||
| } | ||
| redlock.on('clientError', test); | ||
| redlock.lock(error, 200, function(err, lock){ | ||
| redlock.removeListener('clientError', test); | ||
| assert.isNotNull(err); | ||
| assert.equal(emitted, 3); | ||
| done(); | ||
| }); | ||
@@ -90,3 +95,3 @@ }); | ||
| it('should silently fail to unlock an already-unlocked resource', function(done) { | ||
| it('should unlock an already-unlocked resource', function(done) { | ||
| assert(two, 'Could not run because a required previous test failed.'); | ||
@@ -96,2 +101,12 @@ two.unlock(done); | ||
| it('should error when unable to fully release a resource', function(done) { | ||
| assert(two, 'Could not run because a required previous test failed.'); | ||
| var failingTwo = Object.create(two); | ||
| failingTwo.resource = error; | ||
| failingTwo.unlock(function(err) { | ||
| assert.isNotNull(err); | ||
| done(); | ||
| }); | ||
| }); | ||
| it('should fail to extend a lock on an already-unlocked resource', function(done) { | ||
@@ -212,3 +227,3 @@ assert(two, 'Could not run because a required previous test failed.'); | ||
| it('should silently fail to unlock an already-unlocked resource', function(done) { | ||
| it('should unlock an already-unlocked resource', function(done) { | ||
| assert(two, 'Could not run because a required previous test failed.'); | ||
@@ -218,2 +233,12 @@ two.unlock().done(done, done); | ||
| it('should error when unable to fully release a resource', function(done) { | ||
| assert(two, 'Could not run because a required previous test failed.'); | ||
| var failingTwo = Object.create(two); | ||
| failingTwo.resource = error; | ||
| failingTwo.unlock().done(done, function(err) { | ||
| assert.isNotNull(err); | ||
| done(); | ||
| }); | ||
| }); | ||
| it('should fail to extend a lock on an already-unlocked resource', function(done) { | ||
@@ -329,2 +354,21 @@ assert(two, 'Could not run because a required previous test failed.'); | ||
| it('should call unlockErrorHandler when unable to fully release a resource', function(done) { | ||
| assert(two, 'Could not run because a required previous test failed.'); | ||
| var errs = 0; | ||
| var lock; | ||
| Promise.using( | ||
| redlock.disposer(resource, 800, function(err) { | ||
| errs++; | ||
| }), | ||
| function(l){ | ||
| lock = l; | ||
| lock.resource = error; | ||
| } | ||
| ).done(function() { | ||
| assert.equal(errs, 1); | ||
| lock.resource = resource; | ||
| lock.unlock().done(done, done); | ||
| }, done); | ||
| }); | ||
| var three_original, three_extended; | ||
@@ -331,0 +375,0 @@ var three_original_expiration; |
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
32118
11.5%625
10.04%310
11.11%