johnycash
Version:
Easy distributed caching for Node.js
766 lines • 23.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedisCacheService = void 0;
const util_1 = require("util");
class RedisCacheService {
constructor(redis, logger) {
this.asyncMulti = async (commands) => {
const multi = this.redis.multi(commands);
const data = await (0, util_1.promisify)(multi.exec).call(multi);
return data;
};
this.redis = redis;
this.logger = logger || console;
}
async get(key) {
try {
const data = await this.redis.get(key);
if (data) {
return JSON.parse(data);
}
}
catch (error) {
if (error instanceof Error) {
this.logger.error('RedisCache - An error occurred while trying to get from redis cache.', {
cacheKey: key,
error: error?.toString(),
});
}
}
finally {
}
return undefined;
}
async getMany(keys) {
try {
const items = await this.redis.mget(keys);
const values = items.map(item => item ? JSON.parse(item) : null);
return values;
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to get many keys from redis cache.', {
cacheKeys: keys,
exception: error?.toString(),
});
}
return [];
}
finally {
}
}
async setnx(key, value, cacheNullable = true) {
try {
if (!cacheNullable && value == null) {
return false;
}
const result = await this.redis.setnx(key, JSON.stringify(value));
return result === 1;
}
finally {
}
}
async set(key, value, ttl = null, cacheNullable = true) {
if (value === undefined) {
return;
}
if (!cacheNullable && value == null) {
return;
}
if (typeof ttl === 'number' && ttl <= 0) {
return;
}
try {
if (!ttl) {
await this.redis.set(key, JSON.stringify(value));
}
else {
await this.redis.set(key, JSON.stringify(value), 'EX', ttl);
}
}
catch (error) {
if (error instanceof Error) {
this.logger.error('RedisCache - An error occurred while trying to set in redis cache.', {
cacheKey: key,
error: error?.toString(),
});
}
}
finally {
}
}
async setMany(keys, values, ttl, cacheNullable = true) {
try {
let commands = [];
if (!cacheNullable) {
commands = keys.map((key, index) => {
if (values[index] == null) {
return [];
}
return ['set', key, JSON.stringify(values[index]), 'EX', ttl.toString()];
});
commands = commands.filter(command => command.length !== 0);
}
else {
commands = keys.map((key, index) => {
return ['set', key, JSON.stringify(values[index]), 'EX', ttl.toString()];
});
}
await this.asyncMulti(commands);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('RedisCache - An error occurred while trying to set many in redis cache.', {
cacheKey: keys,
error: error?.toString(),
});
}
}
finally {
}
}
async expire(key, ttl) {
await this.redis.expire(key, ttl);
}
async pexpire(key, ttl) {
await this.redis.pexpire(key, ttl);
}
async delete(key) {
try {
await this.redis.del(key);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('RedisCache - An error occurred while trying to delete from redis cache.', {
cacheKey: key,
error: error?.toString(),
});
}
}
finally {
}
}
async deleteMany(keys) {
try {
await this.redis.del(keys);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to delete multiple keys from redis cache.', {
error: error?.toString(),
});
}
}
finally {
}
}
async deleteByPattern(keyPattern) {
try {
const stream = this.redis.scanStream({
match: keyPattern,
count: 10,
});
const dels = await new Promise((resolve, reject) => {
let delKeys = [];
stream.on('data', function (resultKeys) {
delKeys = [...delKeys, ...resultKeys.map((key) => ['del', key])];
});
stream.on('end', () => {
resolve(delKeys);
});
stream.on('error', (err) => {
reject(err);
});
});
await this.asyncMulti(dels);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to delete from redis cache by pattern.', {
error: error?.toString(),
});
}
}
finally {
}
}
async flushDb() {
try {
await this.redis.flushdb();
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to delete multiple keys from redis cache.', {
error: error?.toString(),
});
}
}
finally {
}
}
async getOrSet(key, createValueFunc, ttl, cacheNullable = true) {
const cachedData = await this.get(key);
if (cachedData !== undefined) {
return cachedData;
}
const internalCreateValueFunc = this.buildInternalCreateValueFunc(key, createValueFunc);
const value = await internalCreateValueFunc();
await this.set(key, value, ttl, cacheNullable);
return value;
}
async setOrUpdate(key, createValueFunc, ttl, cacheNullable = true) {
const internalCreateValueFunc = this.buildInternalCreateValueFunc(key, createValueFunc);
const value = await internalCreateValueFunc();
await this.set(key, value, ttl, cacheNullable);
return value;
}
async scan(pattern) {
const found = [];
let cursor = '0';
do {
const reply = await this.redis.scan(cursor, 'MATCH', pattern);
cursor = reply[0];
found.push(...reply[1]);
} while (cursor !== '0');
return found;
}
async increment(key, ttl = null) {
try {
const newValue = await this.redis.incr(key);
if (ttl) {
await this.expire(key, ttl);
}
return newValue;
}
catch (error) {
if (error instanceof Error) {
this.logger.error('RedisCache - An error occurred while trying to increment redis key.', {
cacheKey: key,
error: error?.toString(),
});
}
throw error;
}
finally {
}
}
async incrby(key, value) {
try {
return await this.redis.incrby(key, value);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('RedisCache - An error occurred while trying to incrby redis key.', {
cacheKey: key,
error: error?.toString(),
});
}
throw error;
}
finally {
}
}
async decrement(key, ttl = null) {
try {
const newValue = await this.redis.decr(key);
if (ttl) {
await this.expire(key, ttl);
}
return newValue;
}
catch (error) {
if (error instanceof Error) {
this.logger.error('RedisCache - An error occurred while trying to decrement redis key.', {
cacheKey: key,
error: error?.toString(),
});
}
throw error;
}
finally {
}
}
async hget(hash, field) {
try {
const data = await this.redis.hget(hash, field);
if (data) {
return JSON.parse(data);
}
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to hget from redis.', {
hash, field,
exception: error?.toString(),
});
}
}
finally {
}
return null;
}
async hgetall(hash) {
try {
const data = await this.redis.hgetall(hash);
if (!data) {
return null;
}
const response = {};
for (const key of Object.keys(data)) {
response[key] = JSON.parse(data[key]);
}
return response;
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to hgetall from redis.', {
hash,
exception: error?.toString(),
});
}
}
finally {
}
return null;
}
async hset(hash, field, value, cacheNullable = true) {
try {
if (!cacheNullable && value == null) {
return 0;
}
return await this.redis.hset(hash, field, JSON.stringify(value));
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to hset in redis.', {
hash, field, value,
exception: error?.toString(),
});
}
throw error;
}
finally {
}
}
async hsetMany(hash, fieldsValues, cacheNullable = true) {
try {
const hashMap = new Map();
for (const [field, value] of fieldsValues) {
if (!cacheNullable && value == null) {
continue;
}
hashMap.set(field, JSON.stringify(value));
}
if (hashMap.size === 0) {
return 0;
}
return await this.redis.hset(hash, hashMap);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to hset many in redis.', {
hash, fieldsValues,
exception: error?.toString(),
});
}
throw error;
}
finally {
}
}
async hincrby(hash, field, value) {
try {
return await this.redis.hincrby(hash, field, value);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to hincrby in redis.', {
hash, field, value,
exception: error?.toString(),
});
}
throw error;
}
finally {
}
}
async hkeys(hash) {
try {
return await this.redis.hkeys(hash);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to hkeys in redis.', {
hash,
exception: error?.toString(),
});
}
throw error;
}
finally {
}
}
async zadd(key, member, value, options = []) {
try {
return await this.redis.zadd(key, ...options, value, member);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to zadd in redis.', {
exception: error?.toString(),
key,
member,
value,
});
}
throw error;
}
finally {
}
}
async zincrby(key, member, increment) {
try {
return await this.redis.zincrby(key, increment, member);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to zincrby in redis.', {
exception: error?.toString(),
key,
member,
increment,
});
}
throw error;
}
finally {
}
}
async zrank(key, member) {
try {
return await this.redis.zrank(key, member);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to get zrank from redis.', {
exception: error?.toString(),
key,
member,
});
}
throw error;
}
finally {
}
}
async keys(key) {
try {
return await this.redis.keys(key);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to get keys from redis.', {
exception: error?.toString(),
key,
});
}
throw error;
}
finally {
}
}
async zrevrank(key, member) {
try {
return await this.redis.zrevrank(key, member);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to get zrevrank from redis.', {
exception: error?.toString(),
key,
member,
});
}
throw error;
}
finally {
}
}
async sadd(key, ...values) {
try {
return await this.redis.sadd(key, ...values);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to sadd redis.', {
exception: error?.toString(),
key,
...values,
});
}
throw error;
}
finally {
}
}
async sunionstore(destination, keys) {
try {
return await this.redis.sunionstore(destination, keys);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to sunionstore in redis.', {
destination, keys,
exception: error?.toString(),
});
}
throw error;
}
finally {
}
}
async smembers(key) {
try {
return await this.redis.smembers(key);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to smembers in redis.', {
exception: error?.toString(),
key,
});
}
throw error;
}
finally {
}
}
async zrevrange(setName, start, stop, withScores = false) {
try {
if (withScores) {
return await this.redis.zrevrange(setName, start, stop, 'WITHSCORES');
}
return await this.redis.zrevrange(setName, start, stop);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to zrevrange in redis.', {
exception: error?.toString(),
setName,
start,
stop,
withScores,
});
}
throw error;
}
finally {
}
}
async zrangebyscore(setName, start, stop, options) {
try {
if (options?.withScores) {
return await this.redis.zrangebyscore(setName, start, stop, 'WITHSCORES');
}
return await this.redis.zrangebyscore(setName, start, stop);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to get zrangebyscore in redis.', {
exception: error?.toString(),
setName,
start,
stop,
options,
});
}
throw error;
}
finally {
}
}
async zrange(setName, start, stop, options) {
try {
if (options?.order === 'REV') {
if (options?.withScores) {
return await this.redis.zrange(setName, start, stop, 'REV', 'WITHSCORES');
}
return await this.redis.zrange(setName, start, stop, 'REV');
}
if (options?.withScores) {
return await this.redis.zrange(setName, start, stop, 'WITHSCORES');
}
return await this.redis.zrange(setName, start, stop);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to get zrange in redis.', {
exception: error?.toString(),
setName,
start,
stop,
options,
});
}
throw error;
}
finally {
}
}
async zmscore(setName, ...args) {
try {
return await this.redis.zmscore(setName, args);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to zmscore in redis.', {
exception: error?.toString(),
setName,
args,
});
}
throw error;
}
finally {
}
}
async scard(key) {
try {
return await this.redis.scard(key);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to scard in redis.', {
exception: error?.toString(),
key,
});
}
throw error;
}
finally {
}
}
async zcount(key, min, max) {
try {
return await this.redis.zcount(key, min, max);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to get zcount in redis.', {
exception: error?.toString(),
key,
min,
max,
});
}
throw error;
}
finally {
}
}
defineCommand(name, definition) {
try {
return this.redis.defineCommand(name, definition);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to define command in redis.', {
exception: error?.toString(),
name,
definition,
});
}
throw error;
}
finally {
}
}
async executeCommand(name, ...args) {
try {
return await this.redis[name](args);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to execute custom command in redis.', {
exception: error?.toString(),
name,
args,
});
}
throw error;
}
finally {
}
}
async rpush(key, items) {
try {
if (items?.length > 0) {
await this.redis.rpush(key, items);
}
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to rpush to redis.', {
exception: error?.toString(),
key,
});
}
}
finally {
}
}
async lrange(key, start = 0, stop = -1) {
try {
return await this.redis.lrange(key, start, stop);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to execute lrange into redis.', {
exception: error?.toString(),
key,
});
}
throw error;
}
finally {
}
}
async lpop(key) {
const items = [];
try {
let item;
while (item = await this.redis.lpop(key)) {
items.push(item);
}
}
catch (error) {
if (error instanceof Error) {
this.logger.error('An error occurred while trying to lpop to redis.', {
exception: error?.toString(),
key,
});
}
}
finally {
}
return items;
}
buildInternalCreateValueFunc(key, createValueFunc) {
return async () => {
try {
return await createValueFunc();
}
catch (error) {
if (error instanceof Error) {
this.logger.error('RedisCache - An error occurred while trying to load value.', {
error: error?.toString(),
key,
});
}
throw error;
}
};
}
}
exports.RedisCacheService = RedisCacheService;
//# sourceMappingURL=redis-cache.service.js.map