Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Disabling retries is possible now #26

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,37 @@ lock("myLock", function(done) {
});
```

### Disabling retries
Retries are very useful if you want all operations requesting the locks to
eventually be executed and completed.

But sometimes you only care about the first lock, and every subsequent attempt
to acquire the same lock should just be dropped.

You can do this by setting retryDelay timeout to 0:

```javascript
var client = require("redis").createClient(),
lock = require("redis-lock")(client, 0); // <-- disabling retries

// since we disabled retries, a new parameter "error" is passed to the callback
lock("myLock", function(err, done) {
if(!err){
// Simulate a 1 second long operation
setTimeout(done, 1000);
}
});

// this lock will fail
lock("myLock", function(err, done) {
if(err && err.code == "ALREADY_LOCKED"){
// already locked!
}else{
done()
}
});
```

## Installation

$ npm install redis-lock
Expand Down
23 changes: 18 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var util = require('util');
var defaultTimeout = 5000;
var promisify = util.promisify || function(x) { return x; };

function acquireLock(client, lockName, timeout, retryDelay, onLockAcquired) {
function acquireLock(client, lockName, timeout, retryDelay, onLockAcquired, onAlreadyLocked) {
function retry() {
setTimeout(function() {
acquireLock(client, lockName, timeout, retryDelay, onLockAcquired);
Expand All @@ -13,7 +13,10 @@ function acquireLock(client, lockName, timeout, retryDelay, onLockAcquired) {

var lockTimeoutValue = (Date.now() + timeout + 1);
client.set(lockName, lockTimeoutValue, 'PX', timeout, 'NX', function(err, result) {
if(err || result === null) return retry();
if(err || result === null){
if(retryDelay) return retry();
else return onAlreadyLocked()
}
onLockAcquired(lockTimeoutValue);
});
}
Expand All @@ -23,7 +26,7 @@ module.exports = function(client, retryDelay) {
throw new Error("You must specify a client instance of http://github.com/mranney/node_redis");
}

retryDelay = retryDelay || 50;
retryDelay = typeof retryDelay === 'undefined' ? 50 : retryDelay;

function lock(lockName, timeout, taskToPerform) {
if(!lockName) {
Expand All @@ -38,15 +41,25 @@ module.exports = function(client, retryDelay) {
lockName = "lock." + lockName;

acquireLock(client, lockName, timeout, retryDelay, function(lockTimeoutValue) {
taskToPerform(promisify(function(done) {
const doneCb = promisify(function(done) {
done = done || function() {};

if(lockTimeoutValue > Date.now()) {
client.del(lockName, done);
} else {
done();
}
}));
})

if(retryDelay){
taskToPerform(doneCb);
}else{
taskToPerform(null, doneCb);
}
}, function(){
const err = new Error("Locked already acquired")
err.code = "ALREADY_LOCKED"
taskToPerform(err) // errored acquiring lock
});
}

Expand Down
32 changes: 31 additions & 1 deletion test/test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var should = require("should"),
redisClient = require("redis").createClient(),
lock = require("../index")(redisClient)
lock = require("../index")(redisClient),
lockWithoutDelay = require("../index")(redisClient, 0),
util = require("util");

const delay = ms => new Promise(res => setTimeout(res, ms));
Expand Down Expand Up @@ -53,6 +54,35 @@ describe("redis-lock", function() {
}
});


it("should throw error in second operation if delay is zero and first has lock", function(done) {
var errored = 0, success = 0;
lockWithoutDelay("testLock", function(err, completed) {
setTimeout(function() {
if(!err){
success++;
completed();
proceed();
}
}, 500); // Longer, started first
});

lockWithoutDelay("testLock", function(err, completed) {
if(err && err.code == "ALREADY_LOCKED"){
errored++
}
proceed();
});

function proceed() {
if(errored === 1 && success == 1) {
errored.should.equal(1);
done();
}
}
});


it("shouldn't create a deadlock if the first operation doesn't release the lock within <timeout>", function(done) {
var start = new Date();
lock("testLock", 300, function(completed) {
Expand Down