99 lines
2.9 KiB
TypeScript
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",
|
|
}))
|
|
);
|
|
}
|
|
}
|