2016-10-11 01:51:11 +08:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var REDIS_DISTLOCK = {
|
|
|
|
DB: 5,
|
|
|
|
PREFIX: 'batch:locks:'
|
|
|
|
};
|
|
|
|
|
|
|
|
var Redlock = require('redlock');
|
2016-10-17 23:48:28 +08:00
|
|
|
var debug = require('../../util/debug')('leader:redis-distlock');
|
2016-10-11 01:51:11 +08:00
|
|
|
|
2019-12-24 01:19:08 +08:00
|
|
|
function RedisDistlockLocker (redisPool) {
|
2016-10-11 01:51:11 +08:00
|
|
|
this.pool = redisPool;
|
2016-10-13 19:48:06 +08:00
|
|
|
this.redlock = new Redlock([{}], {
|
|
|
|
// see http://redis.io/topics/distlock
|
|
|
|
driftFactor: 0.01, // time in ms
|
|
|
|
// the max number of times Redlock will attempt to lock a resource before failing
|
|
|
|
retryCount: 3,
|
|
|
|
// the time in ms between attempts
|
|
|
|
retryDelay: 100
|
|
|
|
});
|
2016-10-11 01:51:11 +08:00
|
|
|
this._locks = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = RedisDistlockLocker;
|
|
|
|
module.exports.type = 'redis-distlock';
|
|
|
|
|
2019-12-24 01:19:08 +08:00
|
|
|
function resourceId (resource) {
|
2016-10-14 18:56:41 +08:00
|
|
|
return REDIS_DISTLOCK.PREFIX + resource;
|
2016-10-11 01:51:11 +08:00
|
|
|
}
|
|
|
|
|
2019-12-24 01:19:08 +08:00
|
|
|
RedisDistlockLocker.prototype.lock = function (resource, ttl, callback) {
|
2016-10-11 01:51:11 +08:00
|
|
|
var self = this;
|
2016-10-14 18:56:41 +08:00
|
|
|
debug('RedisDistlockLocker.lock(%s, %d)', resource, ttl);
|
|
|
|
var lockId = resourceId(resource);
|
2016-10-11 01:51:11 +08:00
|
|
|
|
2016-10-14 18:56:41 +08:00
|
|
|
var lock = this._getLock(lockId);
|
2019-12-24 01:19:08 +08:00
|
|
|
function acquireCallback (err, _lock) {
|
2016-10-11 01:51:11 +08:00
|
|
|
if (err) {
|
|
|
|
return callback(err);
|
|
|
|
}
|
2016-10-14 18:56:41 +08:00
|
|
|
self._setLock(lockId, _lock);
|
2016-10-11 01:51:11 +08:00
|
|
|
return callback(null, _lock);
|
|
|
|
}
|
|
|
|
if (lock) {
|
2019-12-24 01:19:08 +08:00
|
|
|
return this._tryExtend(lock, ttl, function (err, _lock) {
|
2016-10-11 01:51:11 +08:00
|
|
|
if (err) {
|
2016-10-14 18:56:41 +08:00
|
|
|
return self._tryAcquire(lockId, ttl, acquireCallback);
|
2016-10-11 01:51:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return callback(null, _lock);
|
|
|
|
});
|
|
|
|
} else {
|
2016-10-14 18:56:41 +08:00
|
|
|
return this._tryAcquire(lockId, ttl, acquireCallback);
|
2016-10-11 01:51:11 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-12-24 01:19:08 +08:00
|
|
|
RedisDistlockLocker.prototype.unlock = function (resource, callback) {
|
2016-10-13 19:48:06 +08:00
|
|
|
var self = this;
|
2016-10-14 18:56:41 +08:00
|
|
|
var lock = this._getLock(resourceId(resource));
|
2016-10-13 19:48:06 +08:00
|
|
|
if (lock) {
|
2020-05-18 17:32:41 +08:00
|
|
|
this.pool.acquire(REDIS_DISTLOCK.DB)
|
|
|
|
.then(client => {
|
|
|
|
self.redlock.servers = [client];
|
|
|
|
self.redlock.unlock(lock, function (err) {
|
|
|
|
self.pool.release(REDIS_DISTLOCK.DB, client)
|
|
|
|
.catch(err => debug(err))
|
|
|
|
.finally(() => err ? callback(err) : callback());
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.catch(err => callback(err));
|
2016-10-11 01:51:11 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-12-24 01:19:08 +08:00
|
|
|
RedisDistlockLocker.prototype._getLock = function (resource) {
|
2019-12-27 01:12:47 +08:00
|
|
|
if (Object.prototype.hasOwnProperty.call(this._locks, resource)) {
|
2016-10-11 01:51:11 +08:00
|
|
|
return this._locks[resource];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
2019-12-24 01:19:08 +08:00
|
|
|
RedisDistlockLocker.prototype._setLock = function (resource, lock) {
|
2016-10-11 01:51:11 +08:00
|
|
|
this._locks[resource] = lock;
|
|
|
|
};
|
|
|
|
|
2019-12-24 01:19:08 +08:00
|
|
|
RedisDistlockLocker.prototype._tryExtend = function (lock, ttl, callback) {
|
2016-10-13 19:48:06 +08:00
|
|
|
var self = this;
|
2020-05-18 17:32:41 +08:00
|
|
|
this.pool.acquire(REDIS_DISTLOCK.DB)
|
|
|
|
.then(client => {
|
|
|
|
self.redlock.servers = [client];
|
|
|
|
lock.extend(ttl, function (err, _lock) {
|
|
|
|
self.pool.release(REDIS_DISTLOCK.DB, client)
|
|
|
|
.catch(err => debug(err))
|
|
|
|
.finally(() => err ? callback(err) : callback(null, _lock));
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.catch(err => callback(err));
|
2016-10-11 01:51:11 +08:00
|
|
|
};
|
|
|
|
|
2019-12-24 01:19:08 +08:00
|
|
|
RedisDistlockLocker.prototype._tryAcquire = function (resource, ttl, callback) {
|
2016-10-11 01:51:11 +08:00
|
|
|
var self = this;
|
2020-05-18 17:32:41 +08:00
|
|
|
this.pool.acquire(REDIS_DISTLOCK.DB)
|
|
|
|
.then(client => {
|
|
|
|
self.redlock.servers = [client];
|
|
|
|
self.redlock.lock(resource, ttl, function (err, _lock) {
|
|
|
|
self.pool.release(REDIS_DISTLOCK.DB, client)
|
|
|
|
.catch(err => debug(err))
|
|
|
|
.finally(() => err ? callback(err) : callback(null, _lock));
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.catch(err => callback(err));
|
2016-10-11 01:51:11 +08:00
|
|
|
};
|