Skip to content

Commit 9386f25

Browse files
committed
Add read only redis ability
1 parent b417241 commit 9386f25

File tree

4 files changed

+55
-4
lines changed

4 files changed

+55
-4
lines changed

src/config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,15 @@ addDefaults(config, {
139139
expiryTime: 24 * 60 * 60,
140140
getTimeout: 40
141141
},
142+
redisRead: {
143+
enabled: false,
144+
socket: {
145+
host: "",
146+
port: 0
147+
},
148+
disableOfflineQueue: true,
149+
weight: 1
150+
},
142151
patreon: {
143152
clientId: "",
144153
clientSecret: "",

src/databases/Postgres.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,11 @@ export class Postgres implements IDatabase {
178178

179179
private getPool(type: string, options: QueryOption): Pool {
180180
const readAvailable = this.poolRead && options.useReplica && this.isReadQuery(type);
181-
const ignroreReadDueToFailure = this.config.postgresReadOnly.fallbackOnFail
181+
const ignoreReadDueToFailure = this.config.postgresReadOnly.fallbackOnFail
182182
&& this.lastPoolReadFail > Date.now() - 1000 * 30;
183183
const readDueToFailure = this.config.postgresReadOnly.fallbackOnFail
184184
&& this.lastPoolFail > Date.now() - 1000 * 30;
185-
if (readAvailable && !ignroreReadDueToFailure && (options.forceReplica || readDueToFailure ||
185+
if (readAvailable && !ignoreReadDueToFailure && (options.forceReplica || readDueToFailure ||
186186
Math.random() > 1 / (this.config.postgresReadOnly.weight + 1))) {
187187
return this.poolRead;
188188
} else {

src/types/config.model.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ interface RedisConfig extends redis.RedisClientOptions {
77
getTimeout: number;
88
}
99

10+
interface RedisReadOnlyConfig extends redis.RedisClientOptions {
11+
enabled: boolean;
12+
weight: number;
13+
}
14+
1015
export interface CustomPostgresConfig extends PoolConfig {
1116
enabled: boolean;
1217
maxTries: number;
@@ -61,6 +66,7 @@ export interface SBSConfig {
6166
minimumPrefix?: string;
6267
maximumPrefix?: string;
6368
redis?: RedisConfig;
69+
redisRead?: RedisReadOnlyConfig;
6470
maxRewardTimePerSegmentInSeconds?: number;
6571
postgres?: CustomPostgresConfig;
6672
postgresReadOnly?: CustomPostgresReadOnlyConfig;

src/utils/redis.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,35 @@ let exportClient: RedisSB = {
2525
quit: () => new Promise((resolve) => resolve(null)),
2626
};
2727

28+
let lastClientFail = 0;
29+
let lastReadFail = 0;
30+
2831
if (config.redis?.enabled) {
2932
Logger.info("Connected to redis");
3033
const client = createClient(config.redis);
34+
const readClient = config.redisRead?.enabled ? createClient(config.redisRead) : null;
3135
void client.connect(); // void as we don't care about the promise
36+
void readClient?.connect();
3237
exportClient = client as RedisSB;
3338

39+
3440
const get = client.get.bind(client);
41+
const getRead = readClient?.get?.bind(readClient);
3542
exportClient.get = (key) => new Promise((resolve, reject) => {
3643
const timeout = config.redis.getTimeout ? setTimeout(() => reject(), config.redis.getTimeout) : null;
37-
get(key).then((reply) => {
44+
const chosenGet = pickChoice(get, getRead);
45+
chosenGet(key).then((reply) => {
3846
if (timeout !== null) clearTimeout(timeout);
3947
resolve(reply);
40-
}).catch((err) => reject(err));
48+
}).catch((err) => {
49+
if (chosenGet === get) {
50+
lastClientFail = Date.now();
51+
} else {
52+
lastReadFail = Date.now();
53+
}
54+
55+
reject(err);
56+
});
4157
});
4258
exportClient.increment = (key) => new Promise((resolve, reject) =>
4359
void client.multi()
@@ -48,11 +64,31 @@ if (config.redis?.enabled) {
4864
.catch((err) => reject(err))
4965
);
5066
client.on("error", function(error) {
67+
lastClientFail = Date.now();
5168
Logger.error(`Redis Error: ${error}`);
5269
});
5370
client.on("reconnect", () => {
5471
Logger.info("Redis: trying to reconnect");
5572
});
73+
readClient?.on("error", function(error) {
74+
lastReadFail = Date.now();
75+
Logger.error(`Redis Read-Only Error: ${error}`);
76+
});
77+
readClient?.on("reconnect", () => {
78+
Logger.info("Redis Read-Only: trying to reconnect");
79+
});
80+
}
81+
82+
function pickChoice<T>(client: T, readClient: T): T {
83+
const readAvailable = !!readClient;
84+
const ignoreReadDueToFailure = lastReadFail > Date.now() - 1000 * 30;
85+
const readDueToFailure = lastClientFail > Date.now() - 1000 * 30;
86+
if (readAvailable && !ignoreReadDueToFailure && (readDueToFailure ||
87+
Math.random() > 1 / (config.redisRead?.weight + 1))) {
88+
return readClient;
89+
} else {
90+
return client;
91+
}
5692
}
5793

5894
export default exportClient;

0 commit comments

Comments
 (0)