Skip to content
This repository was archived by the owner on Jan 20, 2020. It is now read-only.

Commit 05bb202

Browse files
authored
Merge pull request #2 from coinbase/2.0.0
2.0.0
2 parents 68b376a + 1a7029d commit 05bb202

5 files changed

Lines changed: 139 additions & 92 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ build/Release
2525
# Dependency directory
2626
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
2727
node_modules
28+
.DS_Store
29+
.idea/

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "process-lock-node",
3-
"version": "1.0.1",
3+
"version": "2.0.0",
44
"description": "A very simple process locking module using redis",
55
"main": "process_lock.js",
66
"directories": {
@@ -15,6 +15,7 @@
1515
},
1616
"author": "",
1717
"dependencies": {
18-
"redis": "2.0.1"
18+
"redis": "2.0.1",
19+
"uuid": "3.1.0"
1920
}
2021
}

process_lock.js

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var redis = require('redis');
2+
var uuid = require('uuid/v4');
23
var redisClient = redis.createClient(
34
process.env.REDIS_URL
45
);
@@ -28,10 +29,13 @@ var redisClient = redis.createClient(
2829
var ProcessLock = function(key, timeout) {
2930
this.key = key;
3031
this.timeout = timeout || 10;
31-
this.id = process.pid;
32+
this.id = uuid();
3233
this.owner = false;
3334
};
3435

36+
/**
37+
* Request a lock on a process. The callback is a standard node callback that returns true if the lock was obtained.
38+
*/
3539
ProcessLock.prototype.lock = function(callback) {
3640
var self = this;
3741

@@ -41,46 +45,67 @@ ProcessLock.prototype.lock = function(callback) {
4145
self.owner = true;
4246
}
4347

44-
typeof callback === 'function' && callback();
48+
typeof callback === 'function' && callback(err, self.owner);
4549
});
4650
};
4751

48-
ProcessLock.prototype.clear = function(callback, force) {
52+
/**
53+
* Clear the lock. If force is true, the lock clearing is forced. Otherwise, the lock is only cleared if this objects owns the lock.
54+
* NB. As of version 2.0.0, the callback argument is last as per node conventions.
55+
* If the lock was not cleared because we are not the owner, the callback returns false.
56+
* The callback returns true if the lock was explicitly cleared and if the lock did not exist at all, the callback returns null
57+
*/
58+
ProcessLock.prototype.clear = function(force, callback) {
4959
var self = this;
50-
60+
if (typeof force === 'function') {
61+
callback = force;
62+
force = false;
63+
}
5164
redisClient.get(self.key, function(err, value) {
52-
if (value != self.id && !force) {
53-
typeof callback === 'function' && callback();
65+
if (value !== self.id && !force) {
66+
// We have lost the lock
67+
self.owner = false;
68+
typeof callback === 'function' && callback(err, false);
5469
return;
5570
}
5671

5772
redisClient.del(self.key, function(err, value) {
5873
self.owner = false;
59-
typeof callback === 'function' && callback();
74+
typeof callback === 'function' && callback(err, value === 1 ? true : null);
6075
});
6176
});
6277
};
6378

79+
/**
80+
* Try to renew the lock on the process. The callback will return false if a lock could not be renewed; true if it explicitly renewed the lock.
81+
*/
6482
ProcessLock.prototype.update = function(callback) {
6583
var self = this;
6684

67-
if (!self.owner) {
68-
typeof callback === 'function' && callback();
69-
return;
70-
}
85+
redisClient.get(self.key, function(err, value) {
86+
// We have lost the lock
87+
if (value != self.id) {
88+
self.owner = false;
89+
}
7190

72-
redisClient.expire(self.key, self.timeout);
91+
if (!self.owner) {
92+
typeof callback === 'function' && callback(err, false);
93+
return;
94+
}
7395

74-
typeof callback === 'function' && callback();
75-
}
96+
redisClient.expire(self.key, self.timeout, (err, val) => {
97+
typeof callback === 'function' && callback(err, val === 1);
98+
});
99+
});
100+
};
76101

77102
ProcessLock.prototype.state = function(callback) {
78103
var self = this;
79104

80105
redisClient.get(self.key, function(err, value) {
81106
value === null ? value = 'open' : value = 'locked';
82-
callback(value);
107+
callback(err, value);
83108
});
84-
}
109+
};
85110

86111
module.exports = ProcessLock;

test/process_lock_test.js

Lines changed: 91 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,108 @@
1-
var assert = require('assert');
2-
var Process = require('./support/process');
1+
const assert = require('assert');
2+
const Process = require('./support/process');
33

4-
var firstProcess = new Process('same-name');
5-
var secondProcess = new Process('same-name');
4+
const firstProcess = new Process('same-name', 1);
5+
const secondProcess = new Process('same-name', 1);
66

77
describe('ProcessLock', function() {
8-
it('firstProcess should be able to obtain lock', function(done) {
9-
firstProcess.processLock.lock(function(){
10-
assert(firstProcess.processLock.owner);
11-
done();
8+
it('firstProcess should be able to obtain lock', function(done) {
9+
firstProcess.processLock.lock((err, result) => {
10+
assert.ifError(err);
11+
assert(firstProcess.processLock.owner);
12+
assert.equal(result, true);
13+
done();
14+
});
1215
});
13-
});
1416

15-
it('secondProcess should not be able to obtain the lock', function(done) {
16-
secondProcess.processLock.lock(function() {
17-
assert.equal(false, secondProcess.processLock.owner);
18-
done();
17+
it('secondProcess should not be able to obtain the lock', function(done) {
18+
secondProcess.processLock.lock((err, result) => {
19+
assert.ifError(err);
20+
assert.equal(result, false);
21+
assert.equal(false, secondProcess.processLock.owner);
22+
done();
23+
});
1924
});
20-
});
2125

22-
it('firstProcess should be able to let go of the lock', function(done) {
23-
firstProcess.processLock.clear(function() {
24-
assert.equal(false, firstProcess.processLock.owner);
25-
done();
26+
it('firstProcess should be able to let go of the lock', function(done) {
27+
firstProcess.processLock.clear((err, result) => {
28+
assert.ifError(err);
29+
assert.equal(result, true);
30+
assert.equal(false, firstProcess.processLock.owner);
31+
done();
32+
});
2633
});
27-
});
2834

29-
it('secondProcess should be able to obtain the lock', function(done) {
30-
secondProcess.processLock.lock(function() {
31-
assert.equal(true, secondProcess.processLock.owner);
32-
done();
35+
it('secondProcess should be able to obtain the lock', function(done) {
36+
secondProcess.processLock.lock((err, result) => {
37+
assert.ifError(err);
38+
assert.equal(secondProcess.processLock.timeout, 1);
39+
assert.equal(result, true);
40+
assert.equal(true, secondProcess.processLock.owner);
41+
done();
42+
});
3343
});
34-
});
3544

36-
it('secondProcess should be able to update the lock', function(done) {
37-
this.timeout(7000);
38-
39-
setTimeout(function() {
40-
secondProcess.processLock.update(function() {
41-
assert(secondProcess.processLock.owner);
42-
done();
43-
});
44-
}, 5000)
45-
});
46-
47-
it('firstProcess should not be able to update the lock', function(done) {
48-
this.timeout(5000);
49-
50-
setTimeout(function() {
51-
firstProcess.processLock.update(function() {
52-
assert.equal(false, firstProcess.processLock.owner);
53-
done();
54-
});
55-
}, 2000);
56-
});
57-
58-
it('firstProcess should not be able to lock because secondProcess recently updated', function(done) {
59-
this.timeout(15000);
45+
it('secondProcess should be able to update the lock', function(done) {
46+
setTimeout(() => {
47+
secondProcess.processLock.update((err, result) => {
48+
assert.ifError(err);
49+
assert.equal(result, true);
50+
assert(secondProcess.processLock.owner);
51+
done();
52+
});
53+
}, 500)
54+
});
6055

61-
setTimeout(function() {
62-
firstProcess.processLock.lock(function() {
63-
assert.equal(false, firstProcess.processLock.owner);
64-
done();
65-
});
66-
}, 7000);
67-
});
56+
it('firstProcess should not be able to update the lock', function(done) {
57+
setTimeout(() => {
58+
firstProcess.processLock.update((err, result) => {
59+
assert.ifError(err);
60+
assert.equal(result, false);
61+
assert.equal(false, firstProcess.processLock.owner);
62+
done();
63+
});
64+
}, 500);
65+
});
6866

69-
it('firstProcess should be able to lock due to timeout', function(done) {
70-
this.timeout(12000)
67+
it('firstProcess should not be able to lock because secondProcess recently updated', function(done) {
68+
setTimeout(() => {
69+
firstProcess.processLock.lock((err, result) => {
70+
assert.ifError(err);
71+
assert.equal(result, false);
72+
assert.equal(false, firstProcess.processLock.owner);
73+
done();
74+
});
75+
}, 250);
76+
});
7177

72-
setTimeout(function() {
73-
firstProcess.processLock.lock(function() {
74-
assert(firstProcess.processLock.owner);
75-
done();
76-
});
77-
}, 10000)
78-
})
78+
it('firstProcess should be able to lock due to timeout', function(done) {
79+
setTimeout(() => {
80+
firstProcess.processLock.lock((err, result) => {
81+
assert.ifError(err);
82+
assert.equal(result, true);
83+
assert(firstProcess.processLock.owner);
84+
done();
85+
});
86+
}, 1500)
87+
});
7988

80-
it('secondProcess should be able to force the lock to clear', function(done) {
81-
secondProcess.processLock.clear(function(){
82-
secondProcess.processLock.state(function(state) {
83-
assert.equal('open', state);
84-
done();
85-
})
86-
}, true)
87-
})
89+
it('secondProcess should be able to force the lock to clear', function(done) {
90+
secondProcess.processLock.clear(true, (err, result) => {
91+
assert.ifError(err);
92+
assert.equal(result, true);
93+
secondProcess.processLock.state((err, state) => {
94+
assert.ifError(err);
95+
assert.equal('open', state);
96+
done();
97+
})
98+
});
99+
});
88100

101+
it('second lock clear returns null', (done) => {
102+
secondProcess.processLock.clear(true, (err, result) => {
103+
assert.ifError(err);
104+
assert.equal(result, null);
105+
done();
106+
});
107+
});
89108
});

test/support/process.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
var ProcessLock = require('../../process_lock');
22

3-
var Process = function(name) {
3+
var Process = function(name, timeout) {
44
this.name = name;
5-
this.processLock = new ProcessLock(this.name, 10);
5+
this.processLock = new ProcessLock(this.name, timeout);
66
};
77

88
module.exports = Process;

0 commit comments

Comments
 (0)