spice-cache-redis
Version:
spice redis cache provider
111 lines (103 loc) • 3.81 kB
JavaScript
let redis = require("redis");
const retry = require("retry");
const createClient = redis.createClient;
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 ? "rediss" : "redis"}://${
this.options.username || "default"
}:${this.options.password || ""}@${
this.options.server || "localhost"
}:${this.options.port || 6379}`,
tls: {},
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;
if (value === undefined) {
console.log("Value is undefined, not setting key:", namespace + key);
return;
}
await this.client.set(namespace + key, JSON.stringify(value));
await this.client.expire(namespace + key, ttl || 900000);
} 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;
if (this.client.isReady) {
return JSON.parse(await this.client.get(namespace + key));
}
return; // Returns undefined if client is not ready
} catch (e) {
console.error("Redis get error:", e);
return null;
}
}
async exists(key) {
return await this.client.exists(key);
}
};