hypershield
Version:
Middleware suite for high-performance and resilient APIs
243 lines • 9.37 kB
JavaScript
"use strict";
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.RedisCache = void 0;
const redis_1 = require("redis");
const compressionService_1 = require("../../domains/compression/application/compressionService");
const constants_1 = require("../../core/constants/constants");
class RedisCache {
constructor(options) {
this.isConnecting = false;
this.reconnectAttempts = 0;
this.options = Object.assign({ maxRetries: 3, retryDelay: 1000, compression: true, reconnectStrategy: {
maxAttempts: 10,
initialDelay: 1000,
maxDelay: 30000
} }, options);
this.compression = new compressionService_1.CompressionService();
this.initializeClient();
}
initializeClient() {
const url = this.buildRedisUrl();
this.client = (0, redis_1.createClient)({
url,
socket: {
reconnectStrategy: (retries) => {
const { maxAttempts, initialDelay, maxDelay } = this.options.reconnectStrategy;
if (retries >= maxAttempts)
return false;
const delay = Math.min(initialDelay * Math.pow(2, retries), maxDelay);
return delay;
}
}
});
this.setupEventHandlers();
this.connect();
}
buildRedisUrl() {
const { host, port, password, tls, db } = this.options;
const protocol = tls ? 'rediss' : 'redis';
const auth = password ? `:${password}@` : '';
const database = db ? `/${db}` : '';
return `${protocol}://${auth}${host}:${port}${database}`;
}
setupEventHandlers() {
this.client.on('error', this.handleError.bind(this));
this.client.on('connect', () => {
this.reconnectAttempts = 0;
console.log('Redis connected successfully');
});
this.client.on('reconnecting', () => {
this.reconnectAttempts++;
console.log(`Redis reconnecting... Attempt ${this.reconnectAttempts}`);
});
}
handleError(error) {
console.error('Redis client error:', error);
}
connect() {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if (this.isConnecting) {
return;
}
try {
this.isConnecting = true;
if (!((_a = this.client) === null || _a === void 0 ? void 0 : _a.isOpen)) {
yield this.client.connect();
yield new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('Redis connection timeout'));
}, 5000);
this.client.on('ready', () => {
clearTimeout(timeout);
resolve();
});
this.client.on('error', (error) => {
clearTimeout(timeout);
reject(error);
});
});
}
}
finally {
this.isConnecting = false;
}
});
}
disconnect() {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if ((_a = this.client) === null || _a === void 0 ? void 0 : _a.isOpen) {
yield this.client.quit();
}
});
}
ensureConnection() {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if (!((_a = this.client) === null || _a === void 0 ? void 0 : _a.isOpen)) {
yield this.connect();
}
});
}
get(key) {
return __awaiter(this, void 0, void 0, function* () {
yield this.ensureConnection();
try {
const value = yield this.client.get(key);
if (!value)
return null;
const compressed = value.startsWith(constants_1.CACHE.COMPRESSION_PREFIX);
const data = compressed ? value.slice(constants_1.CACHE.COMPRESSION_PREFIX.length) : value;
return yield this.maybeDecompress(data, compressed);
}
catch (error) {
console.error(`Redis get error: ${error}`);
throw new Error(`Redis operation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
});
}
set(key, value, ttl) {
return __awaiter(this, void 0, void 0, function* () {
yield this.ensureConnection();
try {
const [data, compressed] = yield this.maybeCompress(value);
const finalValue = compressed ? constants_1.CACHE.COMPRESSION_PREFIX + data : data;
if (ttl) {
yield this.client.setEx(key, ttl, finalValue);
}
else {
yield this.client.set(key, finalValue);
}
}
catch (error) {
console.error(`Redis set error: ${error}`);
}
});
}
delete(key) {
return __awaiter(this, void 0, void 0, function* () {
yield this.ensureConnection();
try {
yield this.client.del(key);
}
catch (error) {
console.error(`Redis delete error: ${error}`);
}
});
}
clear() {
return __awaiter(this, void 0, void 0, function* () {
yield this.ensureConnection();
try {
yield this.client.flushDb();
}
catch (error) {
console.error('Redis clear error:', error);
}
});
}
mget(keys) {
return __awaiter(this, void 0, void 0, function* () {
try {
const values = yield this.client.mGet(keys);
return yield Promise.all(values.map((value) => __awaiter(this, void 0, void 0, function* () {
if (!value)
return null;
const compressed = value.startsWith(constants_1.CACHE.COMPRESSION_PREFIX);
const data = compressed ? value.slice(constants_1.CACHE.COMPRESSION_PREFIX.length) : value;
return yield this.maybeDecompress(data, compressed);
})));
}
catch (error) {
console.error(`Redis mget error: ${error}`);
return new Array(keys.length).fill(null);
}
});
}
exists(key) {
return __awaiter(this, void 0, void 0, function* () {
try {
const result = yield this.client.exists(key);
return result === 1;
}
catch (error) {
console.error(`Redis exists error: ${error}`);
return false;
}
});
}
updateTTL(key, ttl) {
return __awaiter(this, void 0, void 0, function* () {
try {
return yield this.client.expire(key, ttl);
}
catch (error) {
console.error(`Redis updateTTL error: ${error}`);
return false;
}
});
}
getTTL(key) {
return __awaiter(this, void 0, void 0, function* () {
try {
const ttl = yield this.client.ttl(key);
return ttl >= 0 ? ttl : null;
}
catch (error) {
console.error(`Redis getTTL error: ${error}`);
return null;
}
});
}
maybeCompress(value) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.options.compression)
return [JSON.stringify(value), false];
const stringified = JSON.stringify(value);
if (stringified.length < 1024)
return [stringified, false];
const compressed = yield this.compression.compress(stringified);
return [compressed, true];
});
}
maybeDecompress(data, compressed) {
return __awaiter(this, void 0, void 0, function* () {
if (!compressed)
return JSON.parse(data);
const decompressed = yield this.compression.decompress(data);
return JSON.parse(decompressed.toString());
});
}
}
exports.RedisCache = RedisCache;
//# sourceMappingURL=redis.js.map