Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

redlock

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

redlock - npm Package Compare versions

Comparing version 3.1.2 to 4.0.0

CHANGELOG.md

13

package.json
{
"name": "redlock",
"version": "3.1.2",
"version": "4.0.0",
"description": "A node.js redlock implementation for distributed redis locks",

@@ -29,7 +29,7 @@ "main": "redlock.js",

"devDependencies": {
"chai": "^3.5.0",
"coveralls": "^2.11.8",
"ioredis": "^3.0.0",
"chai": "^4.2.0",
"coveralls": "^3.0.4",
"ioredis": "^4.10.0",
"istanbul": "^0.4.2",
"mocha": "^2.4.5",
"mocha": "^6.1.4",
"redis": "^2.7.1"

@@ -40,2 +40,5 @@ },

},
"engines": {
"node": ">=8.0.0"
},
"config": {

@@ -42,0 +45,0 @@ "blanket": {

@@ -13,2 +13,3 @@ [![npm version](https://badge.fury.io/js/redlock.svg)](https://www.npmjs.com/package/redlock)

- [Usage (Callback Style)](#usage-callback-style)
- [Locking multiple resources](#locking-multiple-resources)
- [API Docs](#api-docs)

@@ -24,2 +25,5 @@

### Using Cluster/Sentinel
***Please make sure to use a client with built-in cluster support, such as [ioredis](https://github.com/luin/ioredis).***
It is completely possible to use a *single* redis cluster or sentinal configuration by passing one preconfigured client to redlock. While you do gain high availability and vastly increased throughput under this scheme, the failure modes are a bit different, and it becomes theoretically possible that a lock is acquired twice:

@@ -52,3 +56,3 @@

A redlock object is instantiated with an array of at least one redis client and an optional `options` object. Properties of the Redlock object should NOT be changed after it is firstused, as doing so could have unintended consequences for live locks.
A redlock object is instantiated with an array of at least one redis client and an optional `options` object. Properties of the Redlock object should NOT be changed after it is first used, as doing so could have unintended consequences for live locks.

@@ -115,3 +119,3 @@ ```js

// the maximum amount of time you want the resource locked,
// the maximum amount of time you want the resource locked in milliseconds,
// keeping in mind that you can extend the lock up until

@@ -309,2 +313,38 @@ // the point when it expires

## Locking multiple resources
Multiple resources can be locked by providing an `Array` of strings to `Redlock.prototype.lock` call. Internally a single attempt is made to `redis` by evaluating script which executes lock statements. For more details about atomicity of scripts please see [redis reference](https://redis.io/commands/eval#atomicity-of-scripts).
There are however some limitations of which you need to be aware of:
- When requesting a lock it will fail if any of requested resources is already set
- If lock attempt fails for any resource (due to whatever reason) an attempt for removing already set resources is made. However there are no guarantees that it will succeed (`redis` doesn't provide them)
- Releasing lock will fail if any of requested resources is missing
- Extending lock will fail if any of requested resources is missing
Example:
```js
redlock.lock(['locks:account:322456', 'locks:account:322457', 'locks:account:322458'], 1000).then(function(lock) {
// ...do something here...
// if you need more time, you can continue to extend
// the lock as long as you never let it expire
// this will extend the lock so that it expires
// approximitely 1s from when `extend` is called
return lock.extend(1000).then(function(lock){
// ...do something here...
// unlock your resource when you are done
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);
});
});
});
```
API Docs

@@ -314,3 +354,3 @@ --------

### `Redlock.prototype.lock(resource, ttl, ?callback) => Promise<Lock>`
- `resource (string)` resource to be locked
- `resource (string or string[])` resource(s) to be locked
- `ttl (number)` time in ms until the lock expires

@@ -337,3 +377,3 @@ - `callback (function)` callback returning:

### `Redlock.prototype.disposer(resource, ttl, ?unlockErrorHandler)`
- `resource (string)` resource to be locked
- `resource (string or string[])` resource(s) to be locked
- `ttl (number)` time in ms to extend the lock's expiration

@@ -340,0 +380,0 @@ - `callback (function)` error handler called with:

'use strict';
var util = require('util');
var crypto = require('crypto');
var Promise = require('bluebird');
var EventEmitter = require('events');
const util = require('util');
const crypto = require('crypto');
const Promise = require('bluebird');
const EventEmitter = require('events');
// support the event library provided by node < 0.11.0
if(typeof EventEmitter.EventEmitter === 'function')
EventEmitter = EventEmitter.EventEmitter;
// constants
const lockScript = `
-- Return 0 if an entry already exists.
for i, key in ipairs(KEYS) do
if redis.call("exists", key) == 1 then
return 0
end
end
-- Create an entry for each provided key.
for i, key in ipairs(KEYS) do
redis.call("set", key, ARGV[1], "PX", ARGV[2])
end
// constants
var lockScript = 'return redis.call("set", KEYS[1], ARGV[1], "NX", "PX", ARGV[2])';
var unlockScript = 'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end';
var extendScript = 'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("pexpire", KEYS[1], ARGV[2]) else return 0 end';
-- Return the number of entries added.
return table.getn(KEYS)
`;
const unlockScript = `
local count = 0
for i, key in ipairs(KEYS) do
-- Only remove entries for *this* lock value.
if redis.call("get", key) == ARGV[1] then
redis.pcall("del", key)
count = count + 1
end
end
-- Return the number of entries removed.
return count
`;
const extendScript = `
-- Return 0 if an entry exists with a *different* lock value.
for i, key in ipairs(KEYS) do
if redis.call("get", key) ~= ARGV[1] then
return 0
end
end
-- Update the entry for each provided key.
for i, key in ipairs(KEYS) do
redis.call("set", key, ARGV[1], "PX", ARGV[2])
end
-- Return the number of entries updated.
return table.getn(KEYS)
`;
// defaults
var defaults = {
const defaults = {
driftFactor: 0.01,

@@ -88,7 +127,6 @@ retryCount: 10,

this.retryDelay = typeof options.retryDelay === 'number' ? options.retryDelay : defaults.retryDelay;
this.retryJitter = typeof options.retryJitter === 'number' ? options.retryJitter : defaults.retryJitter;
this.retryJitter = typeof options.retryJitter === 'number' ? options.retryJitter : defaults.retryJitter;
this.lockScript = typeof options.lockScript === 'function' ? options.lockScript(lockScript) : lockScript;
this.unlockScript = typeof options.unlockScript === 'function' ? options.unlockScript(unlockScript) : unlockScript;
this.extendScript = typeof options.extendScript === 'function' ? options.extendScript(extendScript) : extendScript;
// set the redis servers from additional arguments

@@ -175,4 +213,9 @@ this.servers = clients;

Redlock.prototype.unlock = function unlock(lock, callback) {
var self = this;
const self = this;
// array of locked resources
const resource = Array.isArray(lock.resource)
? lock.resource
: [lock.resource];
// immediately invalidate the lock

@@ -183,14 +226,22 @@ lock.expiration = 0;

// the number of votes needed for consensus
const quorum = Math.floor(self.servers.length / 2) + 1;
// the number of servers which have agreed to release this lock
var votes = 0;
let 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
var waiting = self.servers.length;
let waiting = self.servers.length;
// release the lock on each server
self.servers.forEach(function(server){
server.eval(self.unlockScript, 1, lock.resource, lock.value, loop);
return server.eval(
[
self.unlockScript,
resource.length,
...resource,
lock.value
],
loop
)
});

@@ -201,12 +252,9 @@

// - 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 the response is less than the resource length, than one or
// more resources failed to unlock:
// - It may have been re-acquired by another process;
// - It may hava already been manually released;
// - It may have expired;
if(typeof response === 'string')
response = parseInt(response);
if(response === 0 || response === 1)
if(response === resource.length || response === '' + resource.length)
votes++;

@@ -234,3 +282,3 @@

Redlock.prototype.extend = function extend(lock, ttl, callback) {
var self = this;
const self = this;

@@ -286,10 +334,13 @@ // the lock has expired

Redlock.prototype._lock = function _lock(resource, value, ttl, callback) {
var self = this;
const self = this;
// array of locked resources
resource = Array.isArray(resource) ? resource : [resource];
return new Promise(function(resolve, reject) {
var request;
let request;
// the number of times we have attempted this lock
var attempts = 0;
let attempts = 0;
// create a new lock

@@ -299,3 +350,12 @@ if(value === null) {

request = function(server, loop){
return server.eval(self.lockScript, 1, resource, value, ttl, loop);
return server.eval(
[
self.lockScript,
resource.length,
...resource,
value,
ttl
],
loop
);
};

@@ -307,3 +367,12 @@ }

request = function(server, loop){
return server.eval(self.extendScript, 1, resource, value, ttl, loop);
return server.eval(
[
self.extendScript,
resource.length,
...resource,
value,
ttl
],
loop
);
};

@@ -316,23 +385,23 @@ }

// the time when this attempt started
var start = Date.now();
const start = Date.now();
// the number of votes needed for consensus
const quorum = Math.floor(self.servers.length / 2) + 1;
// the number of servers which have agreed to this lock
var votes = 0;
let 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
var waiting = self.servers.length;
let waiting = self.servers.length;
function loop(err, response) {
if(err) self.emit('clientError', err);
if(response) votes++;
if(response === resource.length || response === '' + resource.length) votes++;
if(waiting-- > 1) return;
// Add 2 milliseconds to the drift to account for Redis expires precision, which is 1 ms,
// plus the configured allowable drift factor
var drift = Math.round(self.driftFactor * ttl) + 2;
var lock = new Lock(self, resource, value, start + ttl - drift, attempts);
const drift = Math.round(self.driftFactor * ttl) + 2;
const lock = new Lock(self, resource, value, start + ttl - drift, attempts);
// SUCCESS: there is concensus and the lock is not expired

@@ -372,3 +441,2 @@ if(votes >= quorum && lock.expiration > Date.now())

module.exports = Redlock;

@@ -20,4 +20,5 @@ 'use strict';

var resource = 'Redlock:test:resource';
var error = 'Redlock:test:error';
var resourceString = 'Redlock:test:resource';
var resourceArray = ['Redlock:test:resource1','Redlock:test:resource2'];
var error = 'Redlock:test:error';

@@ -63,3 +64,3 @@ describe('Redlock: ' + name, function(){

unlockScript: function(unlockScript) { return unlockScript + 'and 2'; },
extendScript: function(extendScript) { return extendScript + 'and 3'; }
extendScript: function(extendScript) { return extendScript + 'and 3'; },
};

@@ -70,3 +71,3 @@ var customRedlock = new Redlock(clients, opts);

assert.equal(customRedlock.unlockScript, redlock.unlockScript + 'and ' + i++);
assert.equal(customRedlock.extendScript, redlock.extendScript + 'and ' + i);
assert.equal(customRedlock.extendScript, redlock.extendScript + 'and ' + i++);
});

@@ -79,3 +80,3 @@

for (var i = clients.length - 1; i >= 0; i--) {
clients[i].del(resource, cb);
clients[i].del(resourceString, cb);
}

@@ -86,3 +87,3 @@ });

it('should lock a resource', function(done) {
redlock.lock(resource, 200, function(err, lock){
redlock.lock(resourceString, 200, function(err, lock){
if(err) throw err;

@@ -102,3 +103,3 @@ assert.isObject(lock);

assert(one, 'Could not run because a required previous test failed.');
redlock.lock(resource, 800, function(err, lock){
redlock.lock(resourceString, 800, function(err, lock){
if(err) throw err;

@@ -123,3 +124,6 @@ assert.isObject(lock);

assert(two, 'Could not run because a required previous test failed.');
two.unlock(done);
two.unlock(function(err) {
assert.isNotNull(err)
done();
});
});

@@ -150,3 +154,3 @@

assert(two_expiration, 'Could not run because a required previous test failed.');
redlock.lock(resource, 800, function(err, lock){
redlock.lock(resourceString, 800, function(err, lock){
if(err) throw err;

@@ -179,3 +183,3 @@ assert.isObject(lock);

assert(four, 'Could not run because a required previous test failed.');
redlock.lock(resource, 200, function(err, lock){
redlock.lock(resourceString, 200, function(err, lock){
assert.isNotNull(err);

@@ -202,3 +206,3 @@ assert.instanceOf(err, Redlock.LockError);

assert(four, 'Could not run because a required previous test failed.');
redlock.lock(resource, 800, function(err, lock){
redlock.lock(resourceString, 800, function(err, lock){
if(err) throw err;

@@ -216,3 +220,3 @@ assert.isObject(lock);

for (var i = clients.length - 1; i >= 0; i--) {
clients[i].del(resource, cb);
clients[i].del(resourceString, cb);
}

@@ -227,3 +231,3 @@ });

for (var i = clients.length - 1; i >= 0; i--) {
clients[i].del(resource, cb);
clients[i].del(resourceString, cb);
}

@@ -234,3 +238,3 @@ });

it('should lock a resource', function(done) {
redlock.lock(resource, 200)
redlock.lock(resourceString, 200)
.done(function(lock){

@@ -249,3 +253,3 @@ assert.isObject(lock);

assert(one, 'Could not run because a required previous test failed.');
redlock.lock(resource, 800)
redlock.lock(resourceString, 800)
.done(function(lock){

@@ -269,3 +273,7 @@ assert.isObject(lock);

assert(two, 'Could not run because a required previous test failed.');
two.unlock().done(done, done);
two.unlock().done(function(result) {
done(new Error('Expected an error.'));
}, function(err) {
done();
});
});

@@ -298,3 +306,3 @@

assert(two_expiration, 'Could not run because a required previous test failed.');
redlock.lock(resource, 800)
redlock.lock(resourceString, 800)
.done(function(lock){

@@ -327,3 +335,3 @@ assert.isObject(lock);

assert(four, 'Could not run because a required previous test failed.');
redlock.lock(resource, 200)
redlock.lock(resourceString, 200)
.done(function(){

@@ -356,3 +364,3 @@ done(new Error('Should have failed with a LockError'));

for (var i = clients.length - 1; i >= 0; i--) {
clients[i].del(resource, cb);
clients[i].del(resourceString, cb);
}

@@ -367,3 +375,3 @@ });

for (var i = clients.length - 1; i >= 0; i--) {
clients[i].del(resource, cb);
clients[i].del(resourceString, cb);
}

@@ -376,3 +384,3 @@ });

Promise.using(
redlock.disposer(resource, 200),
redlock.disposer(resourceString, 200),
function(lock){

@@ -393,3 +401,3 @@ assert.isObject(lock);

Promise.using(
redlock.disposer(resource, 800),
redlock.disposer(resourceString, 800),
function(lock){

@@ -411,3 +419,3 @@ assert.isObject(lock);

Promise.using(
redlock.disposer(resource, 800, function(err) {
redlock.disposer(resourceString, 800, function(err) {
errs++;

@@ -421,3 +429,3 @@ }),

assert.equal(errs, 1);
lock.resource = resource;
lock.resource = resourceString;
lock.unlock().done(done, done);

@@ -433,3 +441,3 @@ }, done);

Promise.using(
redlock.disposer(resource, 200),
redlock.disposer(resourceString, 200),
function(lock){

@@ -466,7 +474,401 @@ assert.isObject(lock);

for (var i = clients.length - 1; i >= 0; i--) {
clients[i].del(resource, cb);
clients[i].del(resourceString, cb);
}
});
});
describe('callbacks - multi', function(){
before(function(done) {
var err;
var l = clients.length; function cb(e){ if(e) err = e; l--; if(l === 0) done(err); }
for (var i = clients.length - 1; i >= 0; i--) {
for (var j = resourceArray.length - 1; j >= 0; j--) {
clients[i].del(resourceArray[j], cb);
}
}
});
var one;
it('should lock a multivalue resource', function(done) {
redlock.lock(resourceArray, 200, function(err, lock){
if(err) throw err;
assert.isObject(lock);
assert.instanceOf(lock, Redlock.Lock);
assert.isAbove(lock.expiration, Date.now()-1);
assert.equal(lock.attempts, 1);
one = lock;
done();
});
});
var two;
var two_expiration;
it('should wait until a lock expires before issuing another lock', function(done) {
assert(one, 'Could not run because a required previous test failed.');
redlock.lock(resourceArray, 800, function(err, lock){
if(err) throw err;
assert.isObject(lock);
assert.isAbove(lock.expiration, Date.now()-1);
assert.isAbove(Date.now()+1, one.expiration);
assert.isAbove(lock.attempts, 1);
two = lock;
two_expiration = lock.expiration;
done();
});
});
it('should unlock a multivalue resource', function(done) {
assert(two, 'Could not run because a required previous test failed.');
two.unlock(done);
assert.equal(two.expiration, 0, 'Failed to immediately invalidate the lock.');
});
it('should unlock an already-unlocked multivalue resource', function(done) {
assert(two, 'Could not run because a required previous test failed.');
two.unlock(function(err) {
assert.isNotNull(err)
done();
});
});
it('should error when unable to fully release a multivalue 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 multivalue resource', function(done) {
assert(two, 'Could not run because a required previous test failed.');
two.extend(200, function(err, lock){
assert.isNotNull(err);
assert.instanceOf(err, Redlock.LockError);
assert.equal(err.attempts, 0);
done();
});
});
var three;
it('should issue another lock immediately after a multivalue resource is unlocked', function(done) {
assert(two_expiration, 'Could not run because a required previous test failed.');
redlock.lock(resourceArray, 800, function(err, lock){
if(err) throw err;
assert.isObject(lock);
assert.isAbove(lock.expiration, Date.now()-1);
assert.isBelow(Date.now()-1, two_expiration);
assert.equal(lock.attempts, 1);
three = lock;
done();
});
});
var four;
it('should extend an unexpired multivalue lock', function(done) {
assert(three, 'Could not run because a required previous test failed.');
three.extend(800, function(err, lock){
if(err) throw err;
assert.isObject(lock);
assert.isAbove(lock.expiration, Date.now()-1);
assert.isAbove(lock.expiration, three.expiration-1);
assert.equal(lock.attempts, 1);
assert.equal(three, lock);
four = lock;
done();
});
});
it('should fail after the maximum retry count is exceeded', function(done) {
assert(four, 'Could not run because a required previous test failed.');
redlock.lock(resourceArray, 200, function(err, lock){
assert.isNotNull(err);
assert.instanceOf(err, Redlock.LockError);
assert.equal(err.attempts, 3);
done();
});
});
it('should fail to extend an expired lock', function(done) {
assert(four, 'Could not run because a required previous test failed.');
setTimeout(function(){
three.extend(800, function(err, lock){
assert.isNotNull(err);
assert.instanceOf(err, Redlock.LockError);
assert.equal(err.attempts, 0);
done();
});
}, four.expiration - Date.now() + 100);
});
it('should issue another lock immediately after a resource is expired', function(done) {
assert(four, 'Could not run because a required previous test failed.');
redlock.lock(resourceArray, 800, function(err, lock){
if(err) throw err;
assert.isObject(lock);
assert.isAbove(lock.expiration, Date.now()-1);
assert.equal(lock.attempts, 1);
done();
});
});
after(function(done) {
var err;
var l = clients.length; function cb(e){ if(e) err = e; l--; if(l === 0) done(err); }
for (var i = clients.length - 1; i >= 0; i--) {
for (var j = resourceArray.length - 1; j >= 0; j--) {
clients[i].del(resourceArray[j], cb);
}
}
});
});
describe('promises - multi', function(){
before(function(done) {
var err;
var l = clients.length; function cb(e){ if(e) err = e; l--; if(l === 0) done(err); }
for (var i = clients.length - 1; i >= 0; i--) {
for (var j = resourceArray.length - 1; j >= 0; j--) {
clients[i].del(resourceArray[j], cb);
}
}
});
var one;
it('should lock a multivalue resource', function(done) {
redlock.lock(resourceArray, 200)
.done(function(lock){
assert.isObject(lock);
assert.isAbove(lock.expiration, Date.now()-1);
assert.equal(lock.attempts, 1);
one = lock;
done();
}, done);
});
var two;
var two_expiration;
it('should wait until a multivalue lock expires before issuing another lock', function(done) {
assert(one, 'Could not run because a required previous test failed.');
redlock.lock(resourceArray, 800)
.done(function(lock){
assert.isObject(lock);
assert.isAbove(lock.expiration, Date.now()-1);
assert.isAbove(Date.now()+1, one.expiration);
assert.isAbove(lock.attempts, 1);
two = lock;
two_expiration = lock.expiration;
done();
}, done);
});
it('should unlock a multivalue resource', function(done) {
assert(two, 'Could not run because a required previous test failed.');
two.unlock().done(done, done);
});
it('should unlock an already-unlocked multivalue resource', function(done) {
assert(two, 'Could not run because a required previous test failed.');
two.unlock().done(function(result) {
done(new Error('Expected an error.'));
}, function(err) {
done();
});
});
it('should error when unable to fully release a multivalue 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 multivalue resource', function(done) {
assert(two, 'Could not run because a required previous test failed.');
two.extend(200)
.done(function(){
done(new Error('Should have failed with a LockError'));
}, function(err){
assert.instanceOf(err, Redlock.LockError);
assert.equal(err.attempts, 0);
done();
});
});
var three;
it('should issue another lock immediately after a multivalue resource is unlocked', function(done) {
assert(two_expiration, 'Could not run because a required previous test failed.');
redlock.lock(resourceArray, 800)
.done(function(lock){
assert.isObject(lock);
assert.isAbove(lock.expiration, Date.now()-1);
assert.isBelow(Date.now()-1, two_expiration);
assert.equal(lock.attempts, 1);
three = lock;
done();
}, done);
});
var four;
it('should extend an unexpired lock', function(done) {
assert(three, 'Could not run because a required previous test failed.');
three.extend(800)
.done(function(lock){
assert.isObject(lock);
assert.isAbove(lock.expiration, Date.now()-1);
assert.isAbove(lock.expiration, three.expiration-1);
assert.equal(lock.attempts, 1);
assert.equal(three, lock);
four = lock;
done();
}, done);
});
it('should fail after the maximum retry count is exceeded', function(done) {
assert(four, 'Could not run because a required previous test failed.');
redlock.lock(resourceArray, 200)
.done(function(){
done(new Error('Should have failed with a LockError'));
}, function(err){
assert.instanceOf(err, Redlock.LockError);
assert.equal(err.attempts, 3);
done();
});
});
it('should fail to extend an expired lock', function(done) {
assert(four, 'Could not run because a required previous test failed.');
setTimeout(function(){
three.extend(800)
.done(function(){
done(new Error('Should have failed with a LockError'));
}, function(err){
assert.instanceOf(err, Redlock.LockError);
assert.equal(err.attempts, 0);
done();
});
}, four.expiration - Date.now() + 100);
});
after(function(done) {
var err;
var l = clients.length; function cb(e){ if(e) err = e; l--; if(l === 0) done(err); }
for (var i = clients.length - 1; i >= 0; i--) {
for (var j = resourceArray.length - 1; j >= 0; j--) {
clients[i].del(resourceArray[j], cb);
}
}
});
});
describe('disposer - multi', function(){
before(function(done) {
var err;
var l = clients.length; function cb(e){ if(e) err = e; l--; if(l === 0) done(err); }
for (var i = clients.length - 1; i >= 0; i--) {
for (var j = resourceArray.length - 1; j >= 0; j--) {
clients[i].del(resourceArray[j], cb);
}
}
});
var one;
var one_expiration;
it('should automatically release a lock after the using block', function(done) {
Promise.using(
redlock.disposer(resourceArray, 200),
function(lock){
assert.isObject(lock);
assert.isAbove(lock.expiration, Date.now()-1);
assert.equal(lock.attempts, 1);
one = lock;
one_expiration = lock.expiration;
}
).done(done, done);
});
var two;
var two_expiration;
it('should issue another lock immediately after a resource is unlocked', function(done) {
assert(one_expiration, 'Could not run because a required previous test failed.');
Promise.using(
redlock.disposer(resourceArray, 800),
function(lock){
assert.isObject(lock);
assert.isAbove(lock.expiration, Date.now()-1);
assert.isBelow(Date.now()-1, one_expiration);
assert.equal(lock.attempts, 1);
two = lock;
two_expiration = lock.expiration;
}
).done(done, done);
});
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(resourceArray, 800, function(err) {
errs++;
}),
function(l){
lock = l;
lock.resource = error;
}
).done(function() {
assert.equal(errs, 1);
lock.resource = resourceArray;
lock.unlock().done(done, done);
}, done);
});
var three_original, three_extended;
var three_original_expiration;
var three_extended_expiration;
it('should automatically release an extended lock', function(done) {
assert(two_expiration, 'Could not run because a required previous test failed.');
Promise.using(
redlock.disposer(resourceArray, 200),
function(lock){
assert.isObject(lock);
assert.isAbove(lock.expiration, Date.now()-1);
assert.isBelow(Date.now()-1, two_expiration);
three_original = lock;
three_original_expiration = lock.expiration;
return Promise.delay(100)
.then(function(){ return lock.extend(200); })
.then(function(extended) {
assert.isObject(extended);
assert.isAbove(extended.expiration, Date.now()-1);
assert.isBelow(Date.now()-1, three_original_expiration);
assert.isAbove(extended.expiration, three_original_expiration);
assert.equal(lock.attempts, 1);
assert.equal(extended, lock);
three_extended = extended;
three_extended_expiration = extended.expiration;
});
}
)
.then(function(){
assert.equal(three_original.expiration, 0);
assert.equal(three_extended.expiration, 0);
}).done(done, done);
});
after(function(done) {
var err;
var l = clients.length; function cb(e){ if(e) err = e; l--; if(l === 0) done(err); }
for (var i = clients.length - 1; i >= 0; i--) {
for (var j = resourceArray.length - 1; j >= 0; j--) {
clients[i].del(resourceArray[j], cb);
}
}
});
});
describe('quit', function() {

@@ -473,0 +875,0 @@ it('should quit all clients', function(done){

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc