Matias Alejo Garcia
10 years ago
10 changed files with 253 additions and 116 deletions
@ -0,0 +1,63 @@ |
|||
var _ = require('lodash'); |
|||
var $ = require('preconditions').singleton(); |
|||
|
|||
function Lock() { |
|||
this.tasks = {}; |
|||
}; |
|||
|
|||
Lock.prototype._release = function(token, task) { |
|||
if (!task.running) return; |
|||
task.running = false; |
|||
this.tasks[token] = _.without(this.tasks[token], task); |
|||
this._runOne(token); |
|||
}; |
|||
|
|||
Lock.prototype._runOne = function(token) { |
|||
var self = this; |
|||
|
|||
if (_.any(self.tasks[token], { |
|||
running: true |
|||
})) return; |
|||
|
|||
var task = _.first(self.tasks[token]); |
|||
if (!task) return; |
|||
|
|||
task.running = true; |
|||
|
|||
if (task.timeout > 0) { |
|||
setTimeout(function() { |
|||
self._release(token, task); |
|||
}, task.timeout); |
|||
} |
|||
|
|||
task.fn(null, function() { |
|||
self._release(token, task); |
|||
}); |
|||
}; |
|||
|
|||
Lock.prototype.locked = function(token, wait, max, userTask) { |
|||
var self = this; |
|||
|
|||
if (_.isUndefined(self.tasks[token])) { |
|||
self.tasks[token] = []; |
|||
} |
|||
|
|||
var task = { |
|||
timeout: max, |
|||
running: false, |
|||
fn: userTask, |
|||
}; |
|||
self.tasks[token].push(task); |
|||
|
|||
if (wait > 0) { |
|||
setTimeout(function() { |
|||
if (task.running || !_.contains(self.tasks[token], task)) return; |
|||
self.tasks[token] = _.without(self.tasks[token], task); |
|||
task.fn(new Error('Could not acquire lock ' + token)); |
|||
}, wait); |
|||
} |
|||
|
|||
self._runOne(token); |
|||
}; |
|||
|
|||
module.exports = Lock; |
@ -1,34 +1,38 @@ |
|||
var _ = require('lodash'); |
|||
var $ = require('preconditions').singleton(); |
|||
var _ = require('lodash'); |
|||
var log = require('npmlog'); |
|||
log.debug = log.verbose; |
|||
|
|||
var locks = {}; |
|||
var LocalLock = require('./locallock'); |
|||
var RemoteLock = require('locker'); |
|||
|
|||
var Lock = function() { |
|||
this.taken = false; |
|||
this.queue = []; |
|||
}; |
|||
function Lock(opts) { |
|||
opts = opts || {}; |
|||
if (opts.lockerServer) { |
|||
this.lock = new RemoteLock(opts.lockerServer.port, opts.lockerServer.host); |
|||
|
|||
Lock.prototype.free = function() { |
|||
if (this.queue.length > 0) { |
|||
var f = this.queue.shift(); |
|||
f(this); |
|||
this.lock.on('reset', function() { |
|||
log.debug('Locker server reset'); |
|||
}); |
|||
this.lock.on('error', function(error) { |
|||
log.error('Locker server threw error', error); |
|||
}); |
|||
} else { |
|||
this.taken = false; |
|||
this.lock = new LocalLock(); |
|||
} |
|||
}; |
|||
|
|||
Lock.get = function(key, callback) { |
|||
if (_.isUndefined(locks[key])) { |
|||
locks[key] = new Lock(); |
|||
} |
|||
var lock = locks[key]; |
|||
Lock.prototype.runLocked = function(token, cb, task) { |
|||
$.shouldBeDefined(token); |
|||
|
|||
if (lock.taken) { |
|||
lock.queue.push(callback); |
|||
} else { |
|||
lock.taken = true; |
|||
callback(lock); |
|||
} |
|||
this.lock.locked(token, 5 * 1000, 24 * 60 * 60 * 1000, function(err, release) { |
|||
if (err) return cb(new Error('Wallet is locked')); |
|||
var _cb = function() { |
|||
cb.apply(null, arguments); |
|||
release(); |
|||
}; |
|||
task(_cb); |
|||
}); |
|||
}; |
|||
|
|||
module.exports = Lock; |
|||
|
@ -0,0 +1,6 @@ |
|||
(function() { |
|||
var Locker = require('locker-server'), |
|||
locker = new Locker(); |
|||
|
|||
locker.listen(3003); |
|||
})(); |
@ -0,0 +1,111 @@ |
|||
'use strict'; |
|||
|
|||
var _ = require('lodash'); |
|||
var chai = require('chai'); |
|||
var sinon = require('sinon'); |
|||
var should = chai.should(); |
|||
var Lock = require('../lib/locallock'); |
|||
|
|||
|
|||
describe('Local locks', function() { |
|||
var lock; |
|||
beforeEach(function() { |
|||
this.clock = sinon.useFakeTimers(); |
|||
lock = new Lock(); |
|||
}); |
|||
afterEach(function() { |
|||
this.clock.restore(); |
|||
}); |
|||
it('should lock tasks using the same token', function() { |
|||
var a = false, |
|||
b = false; |
|||
lock.locked('123', 0, 0, function(err, release) { |
|||
should.not.exist(err); |
|||
a = true; |
|||
setTimeout(function() { |
|||
release(); |
|||
}, 5); |
|||
lock.locked('123', 0, 0, function(err, release) { |
|||
should.not.exist(err); |
|||
b = true; |
|||
release(); |
|||
}); |
|||
}); |
|||
a.should.equal(true); |
|||
b.should.equal(false); |
|||
this.clock.tick(10); |
|||
a.should.equal(true); |
|||
b.should.equal(true); |
|||
}); |
|||
it('should not lock tasks using different tokens', function() { |
|||
var i = 0; |
|||
lock.locked('123', 0, 0, function(err, release) { |
|||
should.not.exist(err); |
|||
i++; |
|||
setTimeout(function() { |
|||
release(); |
|||
}, 5); |
|||
lock.locked('456', 0, 0, function(err, release) { |
|||
should.not.exist(err); |
|||
i++; |
|||
release(); |
|||
}); |
|||
}); |
|||
i.should.equal(2); |
|||
}); |
|||
it('should return error if unable to acquire lock', function() { |
|||
lock.locked('123', 0, 0, function(err, release) { |
|||
should.not.exist(err); |
|||
setTimeout(function() { |
|||
release(); |
|||
}, 5); |
|||
lock.locked('123', 1, 0, function(err, release) { |
|||
should.exist(err); |
|||
err.toString().should.contain('Could not acquire lock 123'); |
|||
}); |
|||
}); |
|||
this.clock.tick(2); |
|||
}); |
|||
it('should release lock if acquired for a long time', function() { |
|||
var i = 0; |
|||
lock.locked('123', 0, 3, function(err, release) { |
|||
should.not.exist(err); |
|||
i++; |
|||
lock.locked('123', 20, 0, function(err, release) { |
|||
should.not.exist(err); |
|||
i++; |
|||
release(); |
|||
}); |
|||
}); |
|||
i.should.equal(1); |
|||
this.clock.tick(1); |
|||
i.should.equal(1); |
|||
this.clock.tick(10); |
|||
i.should.equal(2); |
|||
}); |
|||
it('should only release one pending task on lock timeout', function() { |
|||
var i = 0; |
|||
lock.locked('123', 0, 3, function(err, release) { |
|||
should.not.exist(err); |
|||
i++; |
|||
lock.locked('123', 5, 0, function(err, release) { |
|||
should.not.exist(err); |
|||
i++; |
|||
setTimeout(function() { |
|||
release(); |
|||
}, 5); |
|||
}); |
|||
lock.locked('123', 20, 0, function(err, release) { |
|||
should.not.exist(err); |
|||
i++; |
|||
release(); |
|||
}); |
|||
}); |
|||
i.should.equal(1); |
|||
this.clock.tick(4); |
|||
i.should.equal(2) |
|||
this.clock.tick(7); |
|||
i.should.equal(3) |
|||
}); |
|||
|
|||
}); |
@ -1,50 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
var _ = require('lodash'); |
|||
var chai = require('chai'); |
|||
var sinon = require('sinon'); |
|||
var should = chai.should(); |
|||
var Lock = require('../lib/lock'); |
|||
|
|||
describe('Lock', function() { |
|||
it('should lock tasks using the same token', function(done) { |
|||
var a = false, |
|||
b = false; |
|||
Lock.get('123', function(lock) { |
|||
a = true; |
|||
setTimeout(function() { |
|||
lock.free(); |
|||
}, 5); |
|||
Lock.get('123', function(lock) { |
|||
b = true; |
|||
lock.free(); |
|||
}); |
|||
}); |
|||
setTimeout(function() { |
|||
a.should.equal(true); |
|||
b.should.equal(false); |
|||
}, 1); |
|||
setTimeout(function() { |
|||
a.should.equal(true); |
|||
b.should.equal(true); |
|||
done(); |
|||
}, 8); |
|||
}); |
|||
it('should not lock tasks using different tokens', function(done) { |
|||
var i = 0; |
|||
Lock.get('123', function(lock) { |
|||
i++; |
|||
setTimeout(function() { |
|||
lock.free(); |
|||
}, 5); |
|||
Lock.get('456', function(lock) { |
|||
i++; |
|||
lock.free(); |
|||
}); |
|||
}); |
|||
setTimeout(function() { |
|||
i.should.equal(2); |
|||
done(); |
|||
}, 1); |
|||
}); |
|||
}); |
Loading…
Reference in new issue