UNPKG

spice-cache-redis

Version:

spice redis cache provider

129 lines (120 loc) 4.44 kB
let redis = require("redis"); const crypto = require("crypto"); const createClient = redis.createClient; function generateCacheKey(input) { // Create an MD5 hash of the input string and return it in hexadecimal format return crypto.createHash("md5").update(input).digest("hex"); //return input; } module.exports = class SpiceRedisCache { constructor(args = {}) { this.options = args; this.client = null; } async initialize(options = {}) { this.options = { ...this.options, ...options }; return new Promise((resolve, reject) => { let settled = false; // Flag to ensure promise is settled only once let client = createClient({ url: `${this.options?.use_tls === "true" ? "rediss" : "redis"}://${ this.options.username || "default" }:${this.options.password || ""}@${ this.options.server || "localhost" }:${this.options.port || 6379}`, retry_strategy: function (opts) { console.log("Retrying...", opts); if (opts.error && opts.error.code === "ECONNREFUSED") { console.log("The server refused the connection"); return new Error("The server refused the connection"); } if (opts.total_retry_time > 1000 * 60 * 60) { // 1 hour return new Error("Retry time exhausted"); } if (opts.attempt > (this.options.max_retries || 10)) { return undefined; // Stop reconnecting after the max number of retries } // Exponential back-off with jitter const backOffTime = Math.min( opts.attempt * (this.options.retry_factor || 100), this.options.max_retry_delay || 3000 ); const jitter = Math.random() * (this.options.retry_jitter || 100); // e.g., max jitter of 100ms return backOffTime + jitter; }.bind(this), }); client.on("error", function (error) { console.error("Redis Client Error:", error); if (!settled) { settled = true; reject(error); // Corrected typo: err -> error } // If already settled, this is a post-connection error. // The client will attempt to reconnect based on retry_strategy. // isReady will become false. Operations should check isReady. }); client.on("connect", () => { console.log("Redis client is attempting to connect"); }); client.on("end", () => { console.log("Redis connection closed"); }); client.on("ready", () => { console.log("Redis is ready"); if (!settled) { settled = true; resolve(); // Resolve the promise when the client is ready } }); client.connect(); this.client = client; }); } async set(key, value, options = {}) { if (this.client.isReady) { try { let working_options = { ...this.options, ...options }; const { ttl, namespace = "" } = working_options; const cacheKey = generateCacheKey(namespace + key); if (value === undefined) { console.log("Value is undefined, not setting key:", cacheKey); return; } await this.client.set(cacheKey, JSON.stringify(value)); await this.client.expire(cacheKey, ttl || 900); } catch (error) { console.error("Redis set/expire error:", error); // Optionally, you could re-throw or handle specific errors if needed // For now, we log and prevent a crash. } } } async get(key, options = {}) { try { let working_options = { ...this.options, ...options }; const { namespace = "" } = working_options; const cacheKey = generateCacheKey(namespace + key); if (this.client.isReady) { return JSON.parse(await this.client.get(cacheKey)); } return; // Returns undefined if client is not ready } catch (e) { console.error("Redis get error:", e); return null; } } async exists(key, options = {}) { try { let working_options = { ...this.options, ...options }; const { namespace = "" } = working_options; const cacheKey = generateCacheKey(namespace + key); if (this.client.isReady) { return await this.client.exists(cacheKey); } return false; } catch (e) { console.error("Redis exists error:", e); return false; } } };