@@ -25,6 +25,7 @@ import {
2525 incrementAnalyticsEvents ,
2626 incrementRedisCacheOperation ,
2727} from './metrics' ;
28+ import genericPool from "generic-pool" ;
2829
2930const logger = getLogger ( ) ;
3031
@@ -54,8 +55,6 @@ if (redisNodes.length === 0) {
5455 process . exit ( 1 ) ;
5556}
5657
57- let redisClient : Cluster | Redis | undefined ;
58-
5958export const getRedisOptions = (
6059 isTls : boolean ,
6160 password : string | undefined ,
@@ -79,15 +78,6 @@ export const getRedisOptions = (
7978 const targetErrors = [ / M O V E D / , / R E A D O N L Y / , / E T I M E D O U T / ] ;
8079
8180 logger . error ( 'Redis reconnect error:' , error ) ;
82- if ( error . message . includes ( 'MOVED' ) && redisClient instanceof Cluster ) {
83- logger . error ( 'Refreshing Redis Cluster slots cache' ) ;
84- try {
85- redisClient ?. refreshSlotsCache ( ) ;
86- } catch ( error ) {
87- logger . error ( 'Error refreshing Redis Cluster slots cache:' , error ) ;
88- }
89- }
90-
9181 return targetErrors . some ( ( targetError ) =>
9282 targetError . test ( error . message ) ,
9383 ) ;
@@ -99,83 +89,97 @@ export const getRedisOptions = (
9989 return options ;
10090} ;
10191
102- export const getRedisClient = ( ) => {
103- if ( ! redisClient ) {
104- if ( redisCluster ) {
105- logger . info ( 'Connecting to Redis Cluster...' ) ;
92+ export const buildRedisClient = ( usePipelining : boolean = true ) => {
93+ let newRedisClient : Cluster | Redis | undefined ;
10694
107- const redisOptions = getRedisOptions (
108- redisTLS ,
109- process . env . REDIS_PASSWORD ,
110- ) ;
111- const redisClusterOptions : ClusterOptions = {
112- dnsLookup : ( address , callback ) => callback ( null , address ) ,
113- scaleReads : 'slave' ,
114- slotsRefreshTimeout : 5000 ,
115- showFriendlyErrorStack : true ,
116- slotsRefreshInterval : 2000 ,
117- clusterRetryStrategy : ( times ) => Math . min ( times * 30 , 1000 ) ,
118- enableAutoPipelining : true ,
119- redisOptions,
120- } ;
95+ if ( redisCluster ) {
96+ logger . info ( 'Connecting to Redis Cluster...' ) ;
12197
122- logger . debug (
123- 'Redis Cluster options:' ,
124- JSON . stringify ( redisClusterOptions , null , 2 ) ,
125- ) ;
98+ const redisOptions = getRedisOptions (
99+ redisTLS ,
100+ process . env . REDIS_PASSWORD ,
101+ ) ;
102+ const redisClusterOptions : ClusterOptions = {
103+ dnsLookup : ( address , callback ) => callback ( null , address ) ,
104+ scaleReads : 'slave' ,
105+ slotsRefreshTimeout : 5000 ,
106+ showFriendlyErrorStack : true ,
107+ slotsRefreshInterval : 2000 ,
108+ clusterRetryStrategy : ( times ) => Math . min ( times * 30 , 1000 ) ,
109+ enableAutoPipelining : usePipelining ,
110+ redisOptions,
111+ } ;
126112
127- redisClient = new Cluster ( redisNodes , redisClusterOptions ) ;
128- } else {
129- logger . info ( 'Connecting to single Redis node' ) ;
130- redisClient = new Redis ( redisNodes [ 0 ] ) ;
131- }
113+ logger . debug (
114+ 'Redis Cluster options:' ,
115+ JSON . stringify ( redisClusterOptions , null , 2 ) ,
116+ ) ;
117+
118+ newRedisClient = new Cluster ( redisNodes , redisClusterOptions ) ;
119+ } else {
120+ logger . info ( 'Connecting to single Redis node' ) ;
121+ newRedisClient = new Redis ( redisNodes [ 0 ] ) ;
132122 }
133123
134- redisClient . on ( 'ready' , ( ) => {
124+ newRedisClient . on ( 'ready' , ( ) => {
135125 logger . info ( 'Redis ready' ) ;
136-
137- if ( redisClient instanceof Cluster ) {
138- logger . error ( 'Refreshing Redis Cluster slots cache' ) ;
139- try {
140- redisClient ?. refreshSlotsCache ( ) ;
141- } catch ( error ) {
142- logger . error ( 'Error refreshing Redis Cluster slots cache:' , error ) ;
143- }
144- }
145126 } ) ;
146127
147- redisClient . on ( 'error' , ( error ) => {
128+ newRedisClient . on ( 'error' , ( error ) => {
148129 logger . error ( 'Redis error:' , error ) ;
149130 } ) ;
150131
151- redisClient . on ( 'connect' , ( ) => {
132+ newRedisClient . on ( 'connect' , ( ) => {
152133 logger . info ( 'Connected to Redis Cluster successfully' ) ;
153134 } ) ;
154135
155- redisClient . on ( 'close' , ( ) => {
136+ newRedisClient . on ( 'close' , ( ) => {
156137 logger . info ( 'Disconnected from Redis Cluster' ) ;
157138 } ) ;
158139
159- redisClient . on ( 'reconnecting' , ( ) => {
140+ newRedisClient . on ( 'reconnecting' , ( ) => {
160141 logger . info ( 'Reconnecting to Redis Cluster' ) ;
161142 } ) ;
162143
163- redisClient . on ( 'end' , ( ) => {
144+ newRedisClient . on ( 'end' , ( ) => {
164145 logger . info ( 'Redis Cluster connection ended' ) ;
165146 } ) ;
166147
167- redisClient . on ( 'wait' , ( ) => {
148+ newRedisClient . on ( 'wait' , ( ) => {
168149 logger . info ( 'Redis Cluster waiting for connection' ) ;
169150 } ) ;
170151
171- redisClient . on ( 'select' , ( node ) => {
152+ newRedisClient . on ( 'select' , ( node ) => {
172153 logger . info ( 'Redis Cluster selected node:' , node ) ;
173154 } ) ;
174155
156+ return newRedisClient ;
157+ }
158+
159+ const redisFactory = {
160+ create : ( ) => {
161+ return Promise . resolve ( buildRedisClient ( false ) ) ;
162+ } ,
163+ destroy : ( client : Cluster | Redis ) => {
164+ return Promise . resolve ( client . disconnect ( ) ) ;
165+ } ,
166+ } ;
167+
168+ let redisClient : Cluster | Redis | undefined ;
169+
170+ export const getGlobalRedisClient = ( ) => {
171+ if ( ! redisClient ) {
172+ redisClient = buildRedisClient ( ) ;
173+ }
174+
175175 return redisClient ;
176176} ;
177177
178- export const pubClient = getRedisClient ( ) ;
178+ export const pubClient = getGlobalRedisClient ( ) ;
179+ export const pubClientPool = genericPool . createPool ( redisFactory , {
180+ max : 35 ,
181+ min : 15 ,
182+ } ) ;
179183
180184const app = express ( ) ;
181185
0 commit comments