UNPKG

@getlarge/nestjs-tools-lock

Version:
102 lines 3.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LockService = void 0; const tslib_1 = require("tslib"); const common_1 = require("@nestjs/common"); const ioredis_1 = require("ioredis"); const redlock_1 = require("redlock"); const constants_1 = require("./constants"); let LockService = class LockService { constructor(options) { this.options = options; this.defaultLockOptions = { retryCount: 1, retryDelay: 200, // automaticExtensionThreshold: 500, }; } async onModuleInit() { this.createConnection(); await this.waitUntilInitialized(); } async onModuleDestroy() { await this.close(); } async waitUntilInitialized(timeout = 5000) { return new Promise((resolve, reject) => { const controller = new AbortController(); const signal = controller.signal; const interval = setInterval(() => { if (this.isInitialized) { controller.abort(); resolve(); } }, 150); const timer = setTimeout(() => { if (!this.isInitialized) { controller.abort(); reject(new Error('Initialization timed out')); } }, timeout); signal.addEventListener('abort', () => { clearInterval(interval); clearTimeout(timer); }); }); } get isInitialized() { const validRedisStatus = ['connected', 'ready']; return !!this.redis && validRedisStatus.includes(this.redis.status) && !!this.redlock; } checkInitialization() { if (!this.isInitialized) { throw new Error('Redis was not yet initialized'); } } errorHandler(error) { // Ignore cases where a resource is explicitly marked as locked on a client. if (error instanceof redlock_1.ResourceLockedError) { return; } // Log all other errors. console.error(error); } createConnection() { this.redis = new ioredis_1.default(this.options.redis); this.redlock = new redlock_1.default([this.redis], { ...this.defaultLockOptions, ...(this.options.lock || {}) }); // this.redlock.on('error', this.errorHandler); } optimistic(key, ttl, cb) { this.checkInitialization(); return this.redlock.using([key], ttl, cb); } lock(key, ttl) { this.checkInitialization(); return this.redlock.acquire([key], ttl); } async get(key, lockId) { this.checkInitialization(); const value = await this.redis.get(key); // provide fake data for attempts and expiration as this lock would be used for release only return value !== lockId ? null : new redlock_1.Lock(this.redlock, [key], lockId, [], 100); } async unlock(key, lockId) { this.checkInitialization(); const lock = await this.get(key, lockId); if (!lock) { throw new Error(`Lock ${key} - ${lockId} not found.`); } return this.redlock.release(lock); } close() { this.redlock?.removeAllListeners('error'); return this.redlock?.quit(); } }; exports.LockService = LockService; exports.LockService = LockService = tslib_1.__decorate([ (0, common_1.Injectable)(), tslib_1.__param(0, (0, common_1.Inject)(constants_1.LOCK_SERVICE_OPTIONS)), tslib_1.__metadata("design:paramtypes", [Object]) ], LockService); //# sourceMappingURL=lock.service.js.map