UNPKG

hypershield

Version:

Middleware suite for high-performance and resilient APIs

243 lines 9.37 kB
"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