node-redisson
Version:
Distributed lock with Redis implementation for Node.js
103 lines (102 loc) • 4.19 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedissonLock = void 0;
const ICommandExecutor_1 = require("../contracts/ICommandExecutor");
const TimeUnit_1 = require("../utils/TimeUnit");
const RedissonBaseLock_1 = require("./RedissonBaseLock");
class RedissonLock extends RedissonBaseLock_1.RedissonBaseLock {
constructor(commandExecutor, lockName, clientId) {
super(commandExecutor, lockName, clientId);
}
async tryLock(waitTime, leaseTime, unit = TimeUnit_1.TimeUnit.MILLISECONDS) {
const waitForever = waitTime === false;
let time = unit.toMillis(waitForever ? 0 : waitTime);
let current = TimeUnit_1.TimeUnit.now();
const clientId = this.clientId;
const ttl = await this.tryAcquire({ leaseTime, unit, clientId });
// lock acquired
if (ttl === null) {
return true;
}
const isTimeOver = () => {
if (waitForever)
return false;
// calc wait time
time -= TimeUnit_1.TimeUnit.now() - current;
// time over, get lock fail
return time <= 0;
};
// wait lock
while (true) {
if (isTimeOver())
return false;
const ttl = await this.tryAcquire({ leaseTime, unit, clientId });
// lock acquired
if (ttl === null) {
return true;
}
if (isTimeOver())
return false;
// waiting for message
const _waitTime = waitForever || (ttl >= 0 && ttl < time) ? ttl : time;
// console.log({ ttl, _waitTime });
const waitResult = await this.commandExecutor.waitSubscribeOnce(this.getChannelName(),
// When waitting forever, ttl has a possibility eq 0.
// So when _waitTime lte 0, set the timeout to 1000ms.
Number(_waitTime > 0 ? _waitTime : 1000));
if (!waitForever && waitResult === ICommandExecutor_1.SYMBOL_TIMEOUT) {
return false;
}
}
}
async tryAcquire(options) {
const { leaseTime, unit, clientId } = options;
/**
* ttlRemaining == null -> lock acquired
* ttlRemaining is a number -> the lock remaining time
*/
const ttlRemaining = await this.tryLockInner(leaseTime !== true && leaseTime > 0
? {
leaseTime,
unit,
clientId,
}
: {
leaseTime: this.internalLockLeaseTime,
unit: TimeUnit_1.TimeUnit.MILLISECONDS,
clientId,
});
if (ttlRemaining === null) {
// lock acquired
if (leaseTime !== true && leaseTime > 0) {
this.internalLockLeaseTime = unit.toMillis(leaseTime);
}
else {
this.scheduleExpirationRenewal(clientId);
}
}
return ttlRemaining;
}
async tryLockInner(options) {
return this.commandExecutor.redis.rTryLockInner(this.lockName, options.unit.toMillis(options.leaseTime), this.getClientName(options.clientId));
}
async unlockInner(clientId, requestId, timeout) {
const result = await this.commandExecutor.redis.rUnlockInner(this.lockName, this.getChannelName(), this.getUnlockLatchName(requestId), ICommandExecutor_1.UnlockMessages.UNLOCK, this.internalLockLeaseTime, this.getClientName(clientId), timeout);
// console.log({ clientName: this.getClientName(clientId), result });
if (result === null) {
return null;
}
return !!result;
}
async lock(leaseTime = true, unit) {
await this.tryLock(false, leaseTime, unit);
}
async forceUnlock() {
const result = await this.commandExecutor.redis.rForceUnlock(this.lockName, this.getChannelName(), ICommandExecutor_1.UnlockMessages.UNLOCK);
return !!result;
}
getChannelName() {
return RedissonBaseLock_1.RedissonBaseLock.prefixName('redisson_lock__channel', this.lockName);
}
}
exports.RedissonLock = RedissonLock;