Comparing version 4.1.0 to 4.2.0
@@ -9,2 +9,9 @@ ## v4.0.0 | ||
- Update scripts for compatibility with LUA 5.2 (via [#63](https://github.com/mike-marcacci/node-redlock/pull/63)). | ||
- Update scripts for compatibility with LUA 5.2 (via [#63](https://github.com/mike-marcacci/node-redlock/pull/63)). | ||
### v4.2.0 | ||
- Update dependencies. | ||
- Stop testing on node version 8. (Due to dev dependency requirements only.) | ||
- Update docs (@ricmatsui via [#80](https://github.com/mike-marcacci/node-redlock/pull/80)). | ||
- Use evalsha for scripts (@yosiat via [#77](https://github.com/mike-marcacci/node-redlock/pull/77)). |
{ | ||
"name": "redlock", | ||
"version": "4.1.0", | ||
"version": "4.2.0", | ||
"description": "A node.js redlock implementation for distributed redis locks", | ||
@@ -30,10 +30,10 @@ "main": "redlock.js", | ||
"chai": "^4.2.0", | ||
"coveralls": "^3.0.4", | ||
"ioredis": "^4.10.0", | ||
"coveralls": "^3.1.0", | ||
"ioredis": "^4.18.0", | ||
"istanbul": "^0.4.2", | ||
"mocha": "^6.1.4", | ||
"redis": "^2.7.1" | ||
"mocha": "^8.1.3", | ||
"redis": "^3.0.2" | ||
}, | ||
"dependencies": { | ||
"bluebird": "^3.3.3" | ||
"bluebird": "^3.7.2" | ||
}, | ||
@@ -40,0 +40,0 @@ "engines": { |
@@ -69,3 +69,3 @@ [![npm version](https://badge.fury.io/js/redlock.svg)](https://www.npmjs.com/package/redlock) | ||
// see http://redis.io/topics/distlock | ||
driftFactor: 0.01, // time in ms | ||
driftFactor: 0.01, // multiplied by lock ttl to determine drift time | ||
@@ -72,0 +72,0 @@ // the max number of times Redlock will attempt |
128
redlock.js
@@ -29,7 +29,7 @@ 'use strict'; | ||
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 | ||
-- Only remove entries for *this* lock value. | ||
if redis.call("get", key) == ARGV[1] then | ||
redis.pcall("del", key) | ||
count = count + 1 | ||
end | ||
end | ||
@@ -44,5 +44,5 @@ | ||
for i, key in ipairs(KEYS) do | ||
if redis.call("get", key) ~= ARGV[1] then | ||
return 0 | ||
end | ||
if redis.call("get", key) ~= ARGV[1] then | ||
return 0 | ||
end | ||
end | ||
@@ -52,3 +52,3 @@ | ||
for i, key in ipairs(KEYS) do | ||
redis.call("set", key, ARGV[1], "PX", ARGV[2]) | ||
redis.call("set", key, ARGV[1], "PX", ARGV[2]) | ||
end | ||
@@ -95,8 +95,9 @@ | ||
// itself. | ||
function Lock(redlock, resource, value, expiration, attempts) { | ||
this.redlock = redlock; | ||
this.resource = resource; | ||
this.value = value; | ||
this.expiration = expiration; | ||
this.attempts = attempts; | ||
function Lock(redlock, resource, value, expiration, attempts, attemptsRemaining) { | ||
this.redlock = redlock; | ||
this.resource = resource; | ||
this.value = value; | ||
this.expiration = expiration; | ||
this.attempts = attempts; | ||
this.attemptsRemaining = attemptsRemaining; | ||
} | ||
@@ -117,5 +118,2 @@ | ||
// Redlock | ||
@@ -140,2 +138,8 @@ // ------- | ||
throw new Error('Redlock must be instantiated with at least one redis server.'); | ||
this.scripts = { | ||
lockScript: { value: this.lockScript, hash: this._hashScript(this.lockScript) }, | ||
unlockScript: { value: this.unlockScript, hash: this._hashScript(this.unlockScript) }, | ||
extendScript: { value: this.extendScript, hash: this._hashScript(this.extendScript) }, | ||
}; | ||
} | ||
@@ -151,3 +155,2 @@ | ||
// quit | ||
@@ -184,5 +187,23 @@ // ---- | ||
Redlock.prototype.lock = function lock(resource, ttl, callback) { | ||
return this._lock(resource, null, ttl, callback); | ||
return this._lock(resource, null, ttl, {}, callback); | ||
}; | ||
// lockWithOptions | ||
// --------------- | ||
// This method locks a resource and overwrites some of the options | ||
// ```js | ||
// redlock.lockWithOptions( | ||
// 'some-resource', // the resource to lock | ||
// 2000, // ttl in ms | ||
// { retryCount: 1, retryDelay: 100 }, // additional options | ||
// function(err, lock) { // callback function (optional) | ||
// ... | ||
// } | ||
// ) | ||
// ``` | ||
Redlock.prototype.acquireWithOptions = | ||
Redlock.prototype.lockWithOptions = function lock(resource, ttl, options, callback) { | ||
return this._lock(resource, null, ttl, options, callback); | ||
}; | ||
// lock | ||
@@ -206,3 +227,3 @@ // ---- | ||
errorHandler = errorHandler || function(err) {}; | ||
return this._lock(resource, null, ttl).disposer(function(lock){ | ||
return this._lock(resource, null, ttl, {}).disposer(function(lock){ | ||
return lock.unlock().catch(errorHandler); | ||
@@ -244,11 +265,7 @@ }); | ||
self.servers.forEach(function(server){ | ||
return server.eval( | ||
[ | ||
self.unlockScript, | ||
return self._executeScript(server, 'unlockScript', [ | ||
resource.length, | ||
...resource, | ||
lock.value | ||
], | ||
loop | ||
) | ||
], loop); | ||
}); | ||
@@ -295,3 +312,3 @@ | ||
// extend the lock | ||
return self._lock(lock.resource, lock.value, ttl) | ||
return self._lock(lock.resource, lock.value, ttl, {}) | ||
@@ -321,2 +338,3 @@ // modify and return the original lock object | ||
// 2000, // ttl in ms | ||
// {}, // option overrides {retryCount, retryDelay} | ||
// function(err, lock) { // callback function (optional) | ||
@@ -335,2 +353,3 @@ // ... | ||
// 2000, // ttl in ms | ||
// {}, // option overrides {retryCount, retryDelay} | ||
// function(err, lock) { // callback function (optional) | ||
@@ -341,5 +360,11 @@ // ... | ||
// ``` | ||
Redlock.prototype._lock = function _lock(resource, value, ttl, callback) { | ||
Redlock.prototype._lock = function _lock(resource, value, ttl, options, callback) { | ||
const self = this; | ||
// backwards compatibility with previous method signature: _lock(resource, value, ttl, callback) | ||
if (typeof options === 'function' && typeof callback === 'undefined') { | ||
callback = options; | ||
options = {}; | ||
} | ||
// array of locked resources | ||
@@ -358,5 +383,3 @@ resource = Array.isArray(resource) ? resource : [resource]; | ||
request = function(server, loop){ | ||
return server.eval( | ||
[ | ||
self.lockScript, | ||
return self._executeScript(server, 'lockScript', [ | ||
resource.length, | ||
@@ -366,5 +389,3 @@ ...resource, | ||
ttl | ||
], | ||
loop | ||
); | ||
], loop); | ||
}; | ||
@@ -376,5 +397,3 @@ } | ||
request = function(server, loop){ | ||
return server.eval( | ||
[ | ||
self.extendScript, | ||
return self._executeScript(server, 'extendScript', [ | ||
resource.length, | ||
@@ -384,5 +403,3 @@ ...resource, | ||
ttl | ||
], | ||
loop | ||
); | ||
], loop); | ||
}; | ||
@@ -394,2 +411,5 @@ } | ||
let retryCount = options.retryCount || self.retryCount; | ||
let retryDelay = options.retryDelay || self.retryDelay; | ||
// the time when this attempt started | ||
@@ -415,3 +435,3 @@ const start = Date.now(); | ||
const drift = Math.round(self.driftFactor * ttl) + 2; | ||
const lock = new Lock(self, resource, value, start + ttl - drift, attempts); | ||
const lock = new Lock(self, resource, value, start + ttl - drift, attempts, retryCount - attempts); | ||
@@ -427,7 +447,7 @@ // SUCCESS: there is concensus and the lock is not expired | ||
// RETRY | ||
if(self.retryCount === -1 || attempts <= self.retryCount) | ||
return setTimeout(attempt, Math.max(0, self.retryDelay + Math.floor((Math.random() * 2 - 1) * self.retryJitter))); | ||
if(retryCount === -1 || attempts <= retryCount) | ||
return setTimeout(attempt, Math.max(0, retryDelay + Math.floor((Math.random() * 2 - 1) * self.retryJitter))); | ||
// FAILED | ||
return reject(new LockError('Exceeded ' + self.retryCount + ' attempts to lock the resource "' + resource + '".', attempts)); | ||
return reject(new LockError('Exceeded ' + retryCount + ' attempts to lock the resource "' + resource + '".', attempts)); | ||
}); | ||
@@ -453,2 +473,20 @@ } | ||
Redlock.prototype._executeScript = function(server, name, args, callback) { | ||
const script = this.scripts[name]; | ||
return server.evalsha(script.hash, args, (err, result) => { | ||
if(err !== null && err.message.startsWith("NOSCRIPT")) { | ||
// Script is not loaded yet, call eval and it will populate it in redis lua scripts cache | ||
args.unshift(script.value); | ||
return server.eval(args, callback); | ||
} | ||
return callback(err, result); | ||
}); | ||
} | ||
Redlock.prototype._hashScript = function(value) { | ||
return crypto.createHash('sha1').update(value).digest('hex'); | ||
} | ||
module.exports = Redlock; |
35
test.js
@@ -34,2 +34,10 @@ 'use strict'; | ||
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--) { | ||
clients[i].script('flush', cb); | ||
} | ||
}) | ||
it('should throw an error if not passed any clients', function(){ | ||
@@ -203,6 +211,31 @@ assert.throws(function(){ | ||
assert.equal(lock.attempts, 1); | ||
done(); | ||
lock.unlock(done); | ||
}); | ||
}); | ||
describe('lockWithOptions', function() { | ||
it('should lock a resource with additional options', function(done) { | ||
redlock.lockWithOptions(resourceString, 200, {retryCount:10,retryDelay:1}, 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); | ||
assert.equal(lock.attemptsRemaining, 9); | ||
lock.unlock(done); | ||
}); | ||
}); | ||
it('should be backwards compatible', function(done) { | ||
redlock._lock(resourceString, null, 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); | ||
assert.equal(lock.attemptsRemaining, 1); | ||
lock.unlock(done); | ||
}); | ||
}); | ||
}); | ||
after(function(done) { | ||
@@ -209,0 +242,0 @@ var err; |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
57868
10
1195
Updatedbluebird@^3.7.2