Files
outline/server/storage/redis.ts

99 lines
2.9 KiB
TypeScript

import Redis, { RedisOptions } from "ioredis";
import defaults from "lodash/defaults";
import env from "@server/env";
import Logger from "@server/logging/Logger";
type RedisAdapterOptions = RedisOptions & {
/** Suffix to append to the connection name that will be displayed in Redis */
connectionNameSuffix?: string;
};
const defaultOptions: RedisOptions = {
maxRetriesPerRequest: 20,
enableReadyCheck: false,
showFriendlyErrorStack: env.isDevelopment,
retryStrategy(times: number) {
Logger.warn(`Retrying redis connection: attempt ${times}`);
return Math.min(times * 100, 3000);
},
reconnectOnError(err) {
return err.message.includes("READONLY");
},
// support Heroku Redis, see:
// https://devcenter.heroku.com/articles/heroku-redis#ioredis-module
tls: (env.REDIS_URL || "").startsWith("rediss://")
? {
rejectUnauthorized: false,
}
: undefined,
};
export default class RedisAdapter extends Redis {
constructor(
url: string | undefined,
{ connectionNameSuffix, ...options }: RedisAdapterOptions = {}
) {
/**
* For debugging. The connection name is based on the services running in
* this process. Note that this does not need to be unique.
*/
const connectionNamePrefix = env.isDevelopment ? process.pid : "outline";
const connectionName =
`${connectionNamePrefix}:${env.SERVICES.join("-")}` +
(connectionNameSuffix ? `:${connectionNameSuffix}` : "");
if (!url || !url.startsWith("ioredis://")) {
super(
env.REDIS_URL ?? "",
defaults(options, { connectionName }, defaultOptions)
);
} else {
let customOptions = {};
try {
const decodedString = Buffer.from(url.slice(10), "base64").toString();
customOptions = JSON.parse(decodedString);
} catch (error) {
throw new Error(`Failed to decode redis adapter options: ${error}`);
}
try {
super(
defaults(options, { connectionName }, customOptions, defaultOptions)
);
} catch (error) {
throw new Error(`Failed to initialize redis client: ${error}`);
}
}
// More than the default of 10 listeners is expected for the amount of queues
// we're running. Increase the max here to prevent a warning in the console:
// https://github.com/OptimalBits/bull/issues/1192
this.setMaxListeners(100);
}
private static client: RedisAdapter;
private static subscriber: RedisAdapter;
public static get defaultClient(): RedisAdapter {
return (
this.client ||
(this.client = new this(env.REDIS_URL, {
connectionNameSuffix: "client",
}))
);
}
public static get defaultSubscriber(): RedisAdapter {
return (
this.subscriber ||
(this.subscriber = new this(env.REDIS_URL, {
maxRetriesPerRequest: null,
connectionNameSuffix: "subscriber",
}))
);
}
}