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('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() { |
function Lock(opts) { |
||||
this.taken = false; |
opts = opts || {}; |
||||
this.queue = []; |
if (opts.lockerServer) { |
||||
}; |
this.lock = new RemoteLock(opts.lockerServer.port, opts.lockerServer.host); |
||||
|
|
||||
Lock.prototype.free = function() { |
this.lock.on('reset', function() { |
||||
if (this.queue.length > 0) { |
log.debug('Locker server reset'); |
||||
var f = this.queue.shift(); |
}); |
||||
f(this); |
this.lock.on('error', function(error) { |
||||
|
log.error('Locker server threw error', error); |
||||
|
}); |
||||
} else { |
} else { |
||||
this.taken = false; |
this.lock = new LocalLock(); |
||||
} |
} |
||||
}; |
}; |
||||
|
|
||||
Lock.get = function(key, callback) { |
Lock.prototype.runLocked = function(token, cb, task) { |
||||
if (_.isUndefined(locks[key])) { |
$.shouldBeDefined(token); |
||||
locks[key] = new Lock(); |
|
||||
} |
|
||||
var lock = locks[key]; |
|
||||
|
|
||||
if (lock.taken) { |
this.lock.locked(token, 5 * 1000, 24 * 60 * 60 * 1000, function(err, release) { |
||||
lock.queue.push(callback); |
if (err) return cb(new Error('Wallet is locked')); |
||||
} else { |
var _cb = function() { |
||||
lock.taken = true; |
cb.apply(null, arguments); |
||||
callback(lock); |
release(); |
||||
} |
}; |
||||
|
task(_cb); |
||||
|
}); |
||||
}; |
}; |
||||
|
|
||||
module.exports = Lock; |
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