mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-11 14:07:09 +03:00
Add read only redis ability
This commit is contained in:
@@ -139,6 +139,15 @@ addDefaults(config, {
|
|||||||
expiryTime: 24 * 60 * 60,
|
expiryTime: 24 * 60 * 60,
|
||||||
getTimeout: 40
|
getTimeout: 40
|
||||||
},
|
},
|
||||||
|
redisRead: {
|
||||||
|
enabled: false,
|
||||||
|
socket: {
|
||||||
|
host: "",
|
||||||
|
port: 0
|
||||||
|
},
|
||||||
|
disableOfflineQueue: true,
|
||||||
|
weight: 1
|
||||||
|
},
|
||||||
patreon: {
|
patreon: {
|
||||||
clientId: "",
|
clientId: "",
|
||||||
clientSecret: "",
|
clientSecret: "",
|
||||||
|
|||||||
@@ -178,11 +178,11 @@ export class Postgres implements IDatabase {
|
|||||||
|
|
||||||
private getPool(type: string, options: QueryOption): Pool {
|
private getPool(type: string, options: QueryOption): Pool {
|
||||||
const readAvailable = this.poolRead && options.useReplica && this.isReadQuery(type);
|
const readAvailable = this.poolRead && options.useReplica && this.isReadQuery(type);
|
||||||
const ignroreReadDueToFailure = this.config.postgresReadOnly.fallbackOnFail
|
const ignoreReadDueToFailure = this.config.postgresReadOnly.fallbackOnFail
|
||||||
&& this.lastPoolReadFail > Date.now() - 1000 * 30;
|
&& this.lastPoolReadFail > Date.now() - 1000 * 30;
|
||||||
const readDueToFailure = this.config.postgresReadOnly.fallbackOnFail
|
const readDueToFailure = this.config.postgresReadOnly.fallbackOnFail
|
||||||
&& this.lastPoolFail > Date.now() - 1000 * 30;
|
&& this.lastPoolFail > Date.now() - 1000 * 30;
|
||||||
if (readAvailable && !ignroreReadDueToFailure && (options.forceReplica || readDueToFailure ||
|
if (readAvailable && !ignoreReadDueToFailure && (options.forceReplica || readDueToFailure ||
|
||||||
Math.random() > 1 / (this.config.postgresReadOnly.weight + 1))) {
|
Math.random() > 1 / (this.config.postgresReadOnly.weight + 1))) {
|
||||||
return this.poolRead;
|
return this.poolRead;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ interface RedisConfig extends redis.RedisClientOptions {
|
|||||||
getTimeout: number;
|
getTimeout: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RedisReadOnlyConfig extends redis.RedisClientOptions {
|
||||||
|
enabled: boolean;
|
||||||
|
weight: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CustomPostgresConfig extends PoolConfig {
|
export interface CustomPostgresConfig extends PoolConfig {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
maxTries: number;
|
maxTries: number;
|
||||||
@@ -61,6 +66,7 @@ export interface SBSConfig {
|
|||||||
minimumPrefix?: string;
|
minimumPrefix?: string;
|
||||||
maximumPrefix?: string;
|
maximumPrefix?: string;
|
||||||
redis?: RedisConfig;
|
redis?: RedisConfig;
|
||||||
|
redisRead?: RedisReadOnlyConfig;
|
||||||
maxRewardTimePerSegmentInSeconds?: number;
|
maxRewardTimePerSegmentInSeconds?: number;
|
||||||
postgres?: CustomPostgresConfig;
|
postgres?: CustomPostgresConfig;
|
||||||
postgresReadOnly?: CustomPostgresReadOnlyConfig;
|
postgresReadOnly?: CustomPostgresReadOnlyConfig;
|
||||||
|
|||||||
@@ -25,19 +25,35 @@ let exportClient: RedisSB = {
|
|||||||
quit: () => new Promise((resolve) => resolve(null)),
|
quit: () => new Promise((resolve) => resolve(null)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let lastClientFail = 0;
|
||||||
|
let lastReadFail = 0;
|
||||||
|
|
||||||
if (config.redis?.enabled) {
|
if (config.redis?.enabled) {
|
||||||
Logger.info("Connected to redis");
|
Logger.info("Connected to redis");
|
||||||
const client = createClient(config.redis);
|
const client = createClient(config.redis);
|
||||||
|
const readClient = config.redisRead?.enabled ? createClient(config.redisRead) : null;
|
||||||
void client.connect(); // void as we don't care about the promise
|
void client.connect(); // void as we don't care about the promise
|
||||||
|
void readClient?.connect();
|
||||||
exportClient = client as RedisSB;
|
exportClient = client as RedisSB;
|
||||||
|
|
||||||
|
|
||||||
const get = client.get.bind(client);
|
const get = client.get.bind(client);
|
||||||
|
const getRead = readClient?.get?.bind(readClient);
|
||||||
exportClient.get = (key) => new Promise((resolve, reject) => {
|
exportClient.get = (key) => new Promise((resolve, reject) => {
|
||||||
const timeout = config.redis.getTimeout ? setTimeout(() => reject(), config.redis.getTimeout) : null;
|
const timeout = config.redis.getTimeout ? setTimeout(() => reject(), config.redis.getTimeout) : null;
|
||||||
get(key).then((reply) => {
|
const chosenGet = pickChoice(get, getRead);
|
||||||
|
chosenGet(key).then((reply) => {
|
||||||
if (timeout !== null) clearTimeout(timeout);
|
if (timeout !== null) clearTimeout(timeout);
|
||||||
resolve(reply);
|
resolve(reply);
|
||||||
}).catch((err) => reject(err));
|
}).catch((err) => {
|
||||||
|
if (chosenGet === get) {
|
||||||
|
lastClientFail = Date.now();
|
||||||
|
} else {
|
||||||
|
lastReadFail = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
exportClient.increment = (key) => new Promise((resolve, reject) =>
|
exportClient.increment = (key) => new Promise((resolve, reject) =>
|
||||||
void client.multi()
|
void client.multi()
|
||||||
@@ -48,11 +64,31 @@ if (config.redis?.enabled) {
|
|||||||
.catch((err) => reject(err))
|
.catch((err) => reject(err))
|
||||||
);
|
);
|
||||||
client.on("error", function(error) {
|
client.on("error", function(error) {
|
||||||
|
lastClientFail = Date.now();
|
||||||
Logger.error(`Redis Error: ${error}`);
|
Logger.error(`Redis Error: ${error}`);
|
||||||
});
|
});
|
||||||
client.on("reconnect", () => {
|
client.on("reconnect", () => {
|
||||||
Logger.info("Redis: trying to reconnect");
|
Logger.info("Redis: trying to reconnect");
|
||||||
});
|
});
|
||||||
|
readClient?.on("error", function(error) {
|
||||||
|
lastReadFail = Date.now();
|
||||||
|
Logger.error(`Redis Read-Only Error: ${error}`);
|
||||||
|
});
|
||||||
|
readClient?.on("reconnect", () => {
|
||||||
|
Logger.info("Redis Read-Only: trying to reconnect");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickChoice<T>(client: T, readClient: T): T {
|
||||||
|
const readAvailable = !!readClient;
|
||||||
|
const ignoreReadDueToFailure = lastReadFail > Date.now() - 1000 * 30;
|
||||||
|
const readDueToFailure = lastClientFail > Date.now() - 1000 * 30;
|
||||||
|
if (readAvailable && !ignoreReadDueToFailure && (readDueToFailure ||
|
||||||
|
Math.random() > 1 / (config.redisRead?.weight + 1))) {
|
||||||
|
return readClient;
|
||||||
|
} else {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default exportClient;
|
export default exportClient;
|
||||||
|
|||||||
Reference in New Issue
Block a user