Skip to content

Commit

Permalink
Use evalsha for scripts
Browse files Browse the repository at this point in the history
Lazy loading the scripts: trying with evalsha if it fails on “NO SCRIPT” we will load the script in background and proceed with eval, so next run will have evalsha.
  • Loading branch information
yosiat committed Jul 14, 2020
1 parent e90e80a commit a5056eb
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 22 deletions.
54 changes: 32 additions & 22 deletions redlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,6 @@ Lock.prototype.extend = function extend(ttl, callback) {
Redlock.Lock = Lock;





// Redlock
// -------
// A redlock object is instantiated with an array of at least one redis client and an optional
Expand All @@ -132,6 +129,12 @@ function Redlock(clients, options) {
this.servers = clients;
if(this.servers.length === 0)
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) },
};
}

// Inherit all the EventEmitter methods, like `on`, and `off`
Expand All @@ -142,7 +145,6 @@ util.inherits(Redlock, EventEmitter);
// to destinguish between error types.
Redlock.LockError = LockError;


// quit
// ----
// This method runs `.quit()` on all client connections.
Expand Down Expand Up @@ -232,15 +234,11 @@ Redlock.prototype.unlock = function unlock(lock, callback) {

// release the lock on each server
self.servers.forEach(function(server){
return server.eval(
[
self.unlockScript,
return self._executeScript(server, 'unlockScript', [
resource.length,
...resource,
lock.value
],
loop
)
], loop);
});

function loop(err, response) {
Expand Down Expand Up @@ -341,32 +339,24 @@ Redlock.prototype._lock = function _lock(resource, value, ttl, callback) {
if(value === null) {
value = self._random();
request = function(server, loop){
return server.eval(
[
self.lockScript,
return self._executeScript(server, 'lockScript', [
resource.length,
...resource,
value,
ttl
],
loop
);
], loop);
};
}

// extend an existing lock
else {
request = function(server, loop){
return server.eval(
[
self.extendScript,
return self._executeScript(server, 'extendScript', [
resource.length,
...resource,
value,
ttl
],
loop
);
], loop);
};
}

Expand Down Expand Up @@ -429,4 +419,24 @@ Redlock.prototype._random = function _random(){
return crypto.randomBytes(16).toString('hex');
};

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")) {
// load the script, but don't wait for it, on the next call it will be available
server.script('load', script.value);

args.unshift(this[name]);
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;
8 changes: 8 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ function test(name, clients){
}
});

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(){
assert.throws(function(){
new Redlock([], {
Expand Down

0 comments on commit a5056eb

Please sign in to comment.