proper-lockfile
Advanced tools
Comparing version 0.3.1 to 0.4.0
36
index.js
@@ -30,7 +30,2 @@ 'use strict'; | ||
function acquireLock(file, options, callback) { | ||
// Fast fail if a lock is acquired here | ||
if (locks[file]) { | ||
return callback(errcode('Lock file is already being hold', 'ELOCKED', { file: file })); | ||
} | ||
// Use mkdir to create the lockfile (atomic operation) | ||
@@ -40,4 +35,3 @@ options.fs.mkdir(getLockFile(file), function (err) { | ||
// If we successfuly created the lockfile, | ||
// write the uidfile and we are done | ||
// If we successfuly created the lockfile, write the uidfile and we are done | ||
if (!err) { | ||
@@ -127,5 +121,3 @@ uid = uuid.v4(); | ||
if (lock.lastUpdate <= Date.now() - options.stale) { | ||
return unlock(file, extend({}, options, { resolve: false }), function () { | ||
lock.compromised(lock.updateError || errcode('Unable to update lock within the stale threshold', 'EUPDATE')); | ||
}); | ||
return compromisedLock(file, lock, lock.updateError || 'Unable to update lock within the stale threshold'); | ||
} | ||
@@ -137,5 +129,3 @@ | ||
if (err.code === 'ENOENT') { | ||
return unlock(file, extend({}, options, { resolve: false }), function () { | ||
lock.compromised(err); | ||
}); | ||
return compromisedLock(file, lock, err); | ||
} | ||
@@ -150,5 +140,3 @@ | ||
if (result.read.toString().trim() !== lock.uid) { | ||
lock.released = true; | ||
delete locks[file]; | ||
return lock.compromised(errcode('Lock uid mismatch', 'EMISMATCH')); | ||
return compromisedLock(file, lock, 'Lock uid mismatch'); | ||
} | ||
@@ -165,2 +153,12 @@ | ||
function compromisedLock(file, lock, err) { | ||
lock.released = true; | ||
if (locks[file] === lock) { | ||
delete locks[file]; | ||
} | ||
lock.compromised(errcode(err, 'ECOMPROMISED')); | ||
} | ||
// ----------------------------------------- | ||
@@ -229,6 +227,4 @@ | ||
callback(null, function (releasedCallback) { | ||
releasedCallback = releasedCallback || function () {}; | ||
if (lock.released) { | ||
return releasedCallback(errcode('Lock is already released', 'ERELEASED')); | ||
return releasedCallback && releasedCallback(errcode('Lock is already released', 'ERELEASED')); | ||
} | ||
@@ -268,3 +264,3 @@ | ||
if (!lock) { | ||
return callback(errcode('Lock is not acquired', 'ENOTACQUIRED')); | ||
return callback(errcode('Lock is not acquired/owned by you', 'ENOTACQUIRED')); | ||
} | ||
@@ -271,0 +267,0 @@ |
{ | ||
"name": "proper-lockfile", | ||
"version": "0.3.1", | ||
"version": "0.4.0", | ||
"description": "A inter-process and inter-machine lockfile utility that works on a local or network file system.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -36,2 +36,12 @@ # proper-lockfile [![Build Status](https://travis-ci.org/IndigoUnited/node-proper-lockfile.svg?branch=master)](https://travis-ci.org/IndigoUnited/node-proper-lockfile) [![Coverage Status](https://coveralls.io/repos/IndigoUnited/node-proper-lockfile/badge.png?branch=master)](https://coveralls.io/r/IndigoUnited/node-proper-lockfile?branch=master) | ||
### When can a lock be compromised? | ||
1. When the `lockfile` is manually deleted and the update of the lock `mtime` then fails with ENOENT | ||
2. When the `lockfile` is manually deleted and someone else acquires the lock within the update period | ||
3. When different `stale` and/or `update` configurations are being used for the same file | ||
4. When the update of the `lockfile` took longer than the `stale` threshold | ||
As you can see, all these points are originated from human intervention, except point `4` which is unlikely to happen unless you block the event loop for high periods of times or the `fs` calls are really slow. | ||
## Usage | ||
@@ -38,0 +48,0 @@ |
@@ -341,7 +341,6 @@ 'use strict'; | ||
it('should call the compromised function if ENOENT was detected when updating the lockfile mtime', function (next) { | ||
this.timeout(10000); | ||
lockfile.lock(tmpFile, { update: 1000 }, function (err) { | ||
expect(err).to.be.an(Error); | ||
expect(err.code).to.be('ENOENT'); | ||
expect(err.code).to.be('ECOMPROMISED'); | ||
expect(err.message).to.contain('ENOENT'); | ||
@@ -372,2 +371,3 @@ lockfile.lock(tmpFile, function (err) { | ||
expect(err.message).to.contain('foo'); | ||
expect(err.code).to.be('ECOMPROMISED'); | ||
@@ -394,8 +394,37 @@ next(); | ||
expect(err).to.be.an(Error); | ||
expect(err.code).to.be('EUPDATE'); | ||
expect(err.code).to.be('ECOMPROMISED'); | ||
expect(err.message).to.contain('threshold'); | ||
expect(fs.existsSync(tmpFileLock)).to.be(true); | ||
next(); | ||
}, function (err) { | ||
expect(err).to.not.be.ok(); | ||
}); | ||
}); | ||
it('should call the compromised function if lock was acquired by someone else due to staleness', function (next) { | ||
var customFs = extend({}, fs); | ||
customFs.utimes = function (path, atime, mtime, callback) { | ||
setTimeout(function () { | ||
callback(new Error('foo')); | ||
}, 6000); | ||
}; | ||
this.timeout(10000); | ||
lockfile.lock(tmpFile, { fs: customFs, update: 1000, stale: 5000 }, function (err) { | ||
expect(err).to.be.an(Error); | ||
expect(err.code).to.be('ECOMPROMISED'); | ||
expect(fs.existsSync(tmpFileLock)).to.be(true); | ||
next(); | ||
}, function (err) { | ||
expect(err).to.not.be.ok(); | ||
setTimeout(function () { | ||
lockfile.lock(tmpFile, { stale: 5000 }, function (err) { | ||
expect(err).to.not.be.ok(); | ||
}); | ||
}, 5500); | ||
}); | ||
@@ -405,4 +434,2 @@ }); | ||
it('should call the compromised function if the lock uid mismatches', function (next) { | ||
var lock; | ||
this.timeout(10000); | ||
@@ -412,3 +439,4 @@ | ||
expect(err).to.be.an(Error); | ||
expect(err.code).to.be('EMISMATCH'); | ||
expect(err.code).to.be('ECOMPROMISED'); | ||
expect(err.message).to.contain('mismatch'); | ||
expect(fs.existsSync(tmpFileLock)).to.be(true); | ||
@@ -435,3 +463,3 @@ | ||
originalException = process.listeners('uncaughtException').pop() | ||
originalException = process.listeners('uncaughtException').pop(); | ||
process.removeListener('uncaughtException', originalException); | ||
@@ -441,3 +469,3 @@ | ||
expect(err).to.be.an(Error); | ||
expect(err.code).to.be('ENOENT'); | ||
expect(err.code).to.be('ECOMPROMISED'); | ||
@@ -444,0 +472,0 @@ process.nextTick(function () { |
48484
1005
131