johnycash
Version:
Easy distributed caching for Node.js
168 lines • 6.34 kB
JavaScript
"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