UNPKG

johnycash

Version:

Easy distributed caching for Node.js

168 lines 6.34 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JohnyCacheService = void 0; const ioredis_1 = require("ioredis"); const cache_settings_1 = require("./cache-settings"); const redis_events_1 = require("./redis/redis-events"); const local_cache_service_1 = require("./local-cache/local-cache.service"); const redlock_1 = require("redlock"); const redis_pub_sub_service_1 = require("./redis/redis-pub-sub.service"); const logger_1 = require("./logger"); const redis_conig_parser_1 = require("./redis/redis-conig-parser"); const redis_cache_service_1 = require("./redis/redis-cache.service"); class JohnyCacheService { constructor(redisConnectionString, logger, defaultLockOptions) { this.instanceId = Math.random().toString(36).substring(7); this.defaultLockOptions = { retryCount: 1, retryDelay: 100, retryJitter: 50, }; this.logger = logger || new logger_1.Logger(); if (defaultLockOptions) { this.defaultLockOptions = { ...this.defaultLockOptions, ...defaultLockOptions, }; } const redisConfig = (0, redis_conig_parser_1.parseRedisConnectionString)(redisConnectionString); this.redis = new ioredis_1.default(redisConfig); this.memoryCache = new local_cache_service_1.LocalCacheService(); this.redisCache = new redis_cache_service_1.RedisCacheService(this.redis); this.redisPubSubServices = new redis_pub_sub_service_1.RedisPubSubService(redisConfig); this.defaultRedlock = new redlock_1.default([this.redis], this.defaultLockOptions); } async set(cacheSettings, value) { let promises = []; if (cacheSettings.localTtl) { promises.push(this.memoryCache.setCacheValue(cacheSettings.getKey(), value, cacheSettings.localTtl)); } if (cacheSettings.remoteTtl) { promises.push(this.redisCache.set(cacheSettings.getKey(), value, cacheSettings.remoteTtl)); } await Promise.all(promises); if (cacheSettings.remoteTtl && cacheSettings.refreshTtl) { await this.sendRefreshRemoteKeysEvent([cacheSettings.getKey()], cacheSettings.remoteTtl); } } async get(cacheSettings) { const memoryValue = await this.memoryCache.getCacheValue(cacheSettings.getKey()); if (memoryValue !== undefined) { return memoryValue; } const redisValue = await this.redisCache.get(cacheSettings.getKey()); return redisValue === undefined ? null : redisValue; } async deleteMulti(keys) { await Promise.all(keys.map(async (key) => { await this.deleteByKey(key); })); } async deleteByKey(key, localTtl) { if (!localTtl) { await this.redisCache.delete(key); return; } await Promise.all([ this.memoryCache.deleteCacheKey(key), this.redisCache.delete(key), this.sendDeleteRemoteKeysEvent([key]), ]); } async delete(cacheSettings) { if (!cacheSettings.localTtl) { await this.redisCache.delete(cacheSettings.getKey()); return; } await Promise.all([ this.memoryCache.deleteCacheKey(cacheSettings.getKey()), this.redisCache.delete(cacheSettings.getKey()), this.sendDeleteRemoteKeysEvent([cacheSettings.getKey()]), ]); } async getOrSetCache(cacheSettings, promise, forceRefresh) { let value = await this.get(cacheSettings); if (!value || forceRefresh) { value = await promise(); await this.set(cacheSettings, value); } return value; } async acquireLock(cacheSettings) { const lockOptions = { ...this.defaultLockOptions, ...(cacheSettings.lockOptions || {}), }; let redlockToUse; if (lockOptions.retryCount === this.defaultLockOptions.retryCount && lockOptions.retryDelay === this.defaultLockOptions.retryDelay && lockOptions.retryJitter === this.defaultLockOptions.retryJitter) { redlockToUse = this.defaultRedlock; } else { redlockToUse = new redlock_1.default([this.redis], lockOptions); } try { return await redlockToUse.acquire([cacheSettings.getKey()], cache_settings_1.LockCacheSettings.getTtl(cacheSettings)); } catch (error) { return null; } } async releaseLock(lock) { try { await lock.release(); } catch (error) { } } async sendDeleteRemoteKeysEvent(keys) { try { await this.redisPubSubServices.publish(redis_events_1.CACHE_DELETE_NOTIFICATION_EVENT, { keys, instanceId: this.instanceId, }); } catch (error) { this.logger.error(error); } } async sendRefreshRemoteKeysEvent(keys, ttl) { try { await this.redisPubSubServices.publish(redis_events_1.CACHE_REFRESH_NOTIFICATION_EVENT, { keys, ttl, instanceId: this.instanceId, }); } catch (error) { this.logger.error(error); } } async handleDeleteRemoteKeysEvent(event) { if (event.instanceId === this.instanceId) { return; } for (const key of event.keys) { if (key.includes('*')) { this.logger.warn('Pattern should not exist in local memory? "*"...'); } await this.memoryCache.deleteCacheKey(key); } } async handleRefreshRemoteKeysEvent(event) { if (event.instanceId === this.instanceId) { return; } let promises = []; for (const key of event.keys) { if (key.includes('*')) { this.logger.warn('Pattern should not exist in local memory? "*"...'); } promises.push(this.memoryCache.refreshCacheLocalTtl(key, event.ttl)); } await Promise.all(promises); } } exports.JohnyCacheService = JohnyCacheService; //# sourceMappingURL=johny-cache.js.map