murlock
Version:
A distributed locking solution for NestJS, providing a decorator for critical sections with Redis-based synchronization.
125 lines • 5.45 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MurLock = void 0;
require("reflect-metadata");
const common_1 = require("@nestjs/common");
const exceptions_1 = require("../exceptions");
const murlock_service_1 = require("../murlock.service");
function getParameterNames(func) {
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
const ARGUMENT_NAMES = /([^\s,]+)/g;
const fnStr = func.toString().replace(STRIP_COMMENTS, '');
const result = fnStr
.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')'))
.match(ARGUMENT_NAMES);
return result || [];
}
function MurLock(releaseTime, waitOrKeyParam, ...keyParams) {
let wait;
if (typeof waitOrKeyParam === 'number' ||
typeof waitOrKeyParam === 'function') {
wait = waitOrKeyParam;
}
else {
keyParams = [
...(waitOrKeyParam === undefined ? [] : [waitOrKeyParam]),
...keyParams,
];
}
const injectMurlockService = (0, common_1.Inject)(murlock_service_1.MurLockService);
return (target, propertyKey, descriptor) => {
injectMurlockService(target, 'murlockServiceDecorator');
const originalMethod = descriptor.value;
const methodParameterNames = getParameterNames(originalMethod);
function constructLockKey(args, lockKeyPrefix = 'default') {
let lockKeyElements = [];
if (lockKeyPrefix != 'custom') {
lockKeyElements.push(target.constructor.name);
lockKeyElements.push(propertyKey);
}
lockKeyElements.push(...keyParams.map((keyParam) => {
const [source, path] = keyParam.split('.');
const parameterIndex = isNumber(source)
? Number(source)
: methodParameterNames.indexOf(source);
if (parameterIndex >= 0) {
const parameterValue = findParameterValue({
args,
source,
parameterIndex,
path,
});
if (typeof parameterValue === 'undefined' ||
parameterValue === null) {
throw new exceptions_1.MurLockException(`Parameter ${source} is undefined or null.`);
}
if (path &&
typeof parameterValue === 'object' &&
parameterValue !== null &&
path in parameterValue) {
return parameterValue[path];
}
return parameterValue instanceof Object
? parameterValue.toString()
: parameterValue;
}
if (lockKeyPrefix == 'custom') {
return source;
}
throw new exceptions_1.MurLockException(`Parameter ${source} not found in method arguments.`);
}));
return lockKeyElements.join(':');
}
const wrapped = function (...args) {
return __awaiter(this, void 0, void 0, function* () {
const murLockService = this.murlockServiceDecorator;
const lockKey = constructLockKey(args, murLockService.options.lockKeyPrefix);
if (!murLockService) {
throw new exceptions_1.MurLockException('MurLockService is not available.');
}
return murLockService.runWithLock(lockKey, releaseTime, wait, () => __awaiter(this, void 0, void 0, function* () {
return originalMethod.apply(this, args);
}));
});
};
const metadataKeys = typeof Reflect.getMetadataKeys === 'function'
? Reflect.getMetadataKeys(originalMethod)
: [];
for (const key of metadataKeys) {
const value = Reflect.getMetadata(key, originalMethod);
Reflect.defineMetadata(key, value, wrapped);
}
descriptor.value = wrapped;
return descriptor;
};
}
exports.MurLock = MurLock;
function isNumber(value) {
const parsedValue = parseFloat(value);
if (!isNaN(parsedValue)) {
return true;
}
return false;
}
function isObject(value) {
return value !== null && value instanceof Object && !Array.isArray(value);
}
function findParameterValue({ args, source, parameterIndex, path }) {
if (isNumber(source) && path) {
return args[source][path];
}
if (args.length == 1 && isObject(args[0]) && !path) {
return args[0][source];
}
return args[parameterIndex];
}
//# sourceMappingURL=murlock.decorator.js.map