@@ -24,7 +24,7 @@ const INTEGRATION_MODE = Object.freeze({
24
24
NO_REDIS : "NO_REDIS" ,
25
25
} ) ;
26
26
const CF_REDIS_SERVICE_LABEL = "redis-cache" ;
27
- const REDIS_CLIENT_DEFAULT_PING_INTERVAL = 5 * 60000 ;
27
+ const REDIS_CLIENT_DEFAULT_PING_INTERVAL = 4 * 60000 ;
28
28
29
29
const cfEnv = CfEnv . getInstance ( ) ;
30
30
const logger = new Logger ( COMPONENT_NAME ) ;
@@ -35,14 +35,18 @@ const MODE = Object.freeze({
35
35
} ) ;
36
36
37
37
let __messageHandlers ;
38
- let __clientOptions ;
38
+ let __customCredentials ;
39
+ let __customClientOptions ;
40
+ let __activeOptionsTuple ;
39
41
let __canGetClientPromise ;
40
42
let __mainClientPromise ;
41
43
let __subscriberClientPromise ;
42
44
let __integrationModePromise ;
43
45
const _reset = ( ) => {
44
46
__messageHandlers = new HandlerCollection ( ) ;
45
- __clientOptions = null ;
47
+ __customCredentials = null ;
48
+ __customClientOptions = null ;
49
+ __activeOptionsTuple = null ;
46
50
__canGetClientPromise = null ;
47
51
__mainClientPromise = null ;
48
52
__subscriberClientPromise = null ;
@@ -80,25 +84,20 @@ const _subscribedMessageHandler = async (message, channel) => {
80
84
) ;
81
85
} ;
82
86
83
- const _localReconnectStrategy = ( ) =>
84
- new VError ( { name : VERROR_CLUSTER_NAME } , "disabled reconnect, because we are not running on cloud foundry" ) ;
85
-
86
- /**
87
- * Lazily create a new redis client. Client creation transparently handles both the Cloud Foundry "redis-cache" service
88
- * (hyperscaler option) and a local redis-server.
89
- *
90
- * @returns {RedisClient|RedisCluster }
91
- * @private
92
- */
93
- const _createClientBase = ( clientName ) => {
94
- try {
95
- const localSocketOptions = {
96
- host : "localhost" ,
97
- port : 6379 ,
87
+ const _getRedisOptionsTuple = ( ) => {
88
+ if ( ! __activeOptionsTuple ) {
89
+ const defaultClientOptions = {
90
+ pingInterval : REDIS_CLIENT_DEFAULT_PING_INTERVAL ,
91
+ socket : {
92
+ host : "localhost" ,
93
+ port : 6379 ,
94
+ } ,
98
95
} ;
99
- const credentials = cfEnv . cfServiceCredentialsForLabel ( CF_REDIS_SERVICE_LABEL ) ;
96
+
97
+ const credentials = __customCredentials || cfEnv . cfServiceCredentialsForLabel ( CF_REDIS_SERVICE_LABEL ) ;
100
98
const hasCredentials = Object . keys ( credentials ) . length > 0 ;
101
- const { cluster_mode : isCluster } = credentials ;
99
+
100
+ const isCluster = ! ! credentials . cluster_mode ;
102
101
const credentialClientOptions = hasCredentials
103
102
? {
104
103
password : credentials . password ,
@@ -112,13 +111,16 @@ const _createClientBase = (clientName) => {
112
111
113
112
// NOTE: documentation is buried here https://github.com/redis/node-redis/blob/master/docs/client-configuration.md
114
113
const redisClientOptions = {
114
+ ...defaultClientOptions ,
115
115
...credentialClientOptions ,
116
- pingInterval : REDIS_CLIENT_DEFAULT_PING_INTERVAL ,
117
- ...__clientOptions ,
116
+ ...__customClientOptions ,
117
+ // https://nodejs.org/docs/latest-v22.x/api/net.html#socketconnectoptions-connectlistener
118
+ // https://nodejs.org/docs/latest-v22.x/api/tls.html#tlsconnectoptions-callback
119
+ // https://nodejs.org/docs/latest-v22.x/api/tls.html#tlscreatesecurecontextoptions
118
120
socket : {
119
- ...localSocketOptions ,
121
+ ...defaultClientOptions . socket ,
120
122
...credentialClientOptions ?. socket ,
121
- ...__clientOptions ?. socket ,
123
+ ...__customClientOptions ?. socket ,
122
124
} ,
123
125
} ;
124
126
@@ -132,21 +134,31 @@ const _createClientBase = (clientName) => {
132
134
redisClientOptions . socket . tls = ! ! redisClientOptions . socket . tls ;
133
135
}
134
136
135
- if (
136
- redisClientOptions . socket . host === "localhost" &&
137
- ! Object . prototype . hasOwnProperty . call ( redisClientOptions . socket , "reconnectStrategy" )
138
- ) {
139
- redisClientOptions . socket . reconnectStrategy = _localReconnectStrategy ;
140
- }
137
+ __activeOptionsTuple = [ isCluster , redisClientOptions ] ;
138
+ }
141
139
142
- if ( isCluster ) {
143
- return redis . createCluster ( {
144
- rootNodes : [ redisClientOptions ] , // NOTE: assume this ignores everything but socket/url
145
- // https://github.com/redis/node-redis/issues/1782
146
- defaults : redisClientOptions , // NOTE: assume this ignores socket/url
147
- } ) ;
148
- }
149
- return redis . createClient ( redisClientOptions ) ;
140
+ return __activeOptionsTuple ;
141
+ } ;
142
+
143
+ /**
144
+ * Lazily create a new redis client. Client creation transparently handles
145
+ * - custom credentials and client options passed in via {@link setCustomOptions},
146
+ * - Cloud Foundry service with label "redis-cache" (hyperscaler option), and
147
+ * - local redis-server.
148
+ *
149
+ * @returns {RedisClient|RedisCluster }
150
+ * @private
151
+ */
152
+ const _createClientBase = ( clientName ) => {
153
+ try {
154
+ const [ isCluster , redisClientOptions ] = _getRedisOptionsTuple ( ) ;
155
+ return isCluster
156
+ ? redis . createCluster ( {
157
+ rootNodes : [ redisClientOptions ] , // NOTE: assume this ignores everything but socket/url
158
+ // https://github.com/redis/node-redis/issues/1782
159
+ defaults : redisClientOptions , // NOTE: assume this ignores socket/url
160
+ } )
161
+ : redis . createClient ( redisClientOptions ) ;
150
162
} catch ( err ) {
151
163
throw new VError (
152
164
{ name : VERROR_CLUSTER_NAME , cause : err , info : { clientName } } ,
@@ -189,8 +201,9 @@ const _closeClientBase = async (client) => {
189
201
}
190
202
} ;
191
203
192
- const setClientOptions = ( clientOptions ) => {
193
- __clientOptions = clientOptions ;
204
+ const setCustomOptions = ( customCredentials , customClientOptions ) => {
205
+ __customCredentials = customCredentials ;
206
+ __customClientOptions = customClientOptions ;
194
207
} ;
195
208
196
209
const _canGetClient = async ( ) => {
@@ -213,7 +226,7 @@ const _getIntegrationMode = async () => {
213
226
return INTEGRATION_MODE . NO_REDIS ;
214
227
}
215
228
if ( cfEnv . isOnCf ) {
216
- const { cluster_mode : isCluster } = cfEnv . cfServiceCredentialsForLabel ( CF_REDIS_SERVICE_LABEL ) ;
229
+ const [ isCluster ] = _getRedisOptionsTuple ( ) ;
217
230
return isCluster ? INTEGRATION_MODE . CF_REDIS_CLUSTER : INTEGRATION_MODE . CF_REDIS ;
218
231
}
219
232
return INTEGRATION_MODE . LOCAL_REDIS ;
@@ -304,7 +317,7 @@ const sendCommand = async (command) => {
304
317
const mainClient = await getMainClient ( ) ;
305
318
306
319
try {
307
- const { cluster_mode : isCluster } = cfEnv . cfServiceCredentialsForLabel ( CF_REDIS_SERVICE_LABEL ) ;
320
+ const [ isCluster ] = _getRedisOptionsTuple ( ) ;
308
321
if ( isCluster ) {
309
322
// NOTE: the cluster sendCommand API has a different signature, where it takes two optional args: firstKey and
310
323
// isReadonly before the command
@@ -594,7 +607,7 @@ const removeAllMessageHandlers = (channel) => __messageHandlers.removeAllHandler
594
607
595
608
module . exports = {
596
609
REDIS_INTEGRATION_MODE : INTEGRATION_MODE ,
597
- setClientOptions ,
610
+ setCustomOptions ,
598
611
getIntegrationMode,
599
612
getMainClient,
600
613
closeMainClient,
@@ -628,7 +641,6 @@ module.exports = {
628
641
_getSubscriberClient : ( ) => __subscriberClientPromise ,
629
642
_setSubscriberClient : ( value ) => ( __subscriberClientPromise = value ) ,
630
643
_subscribedMessageHandler,
631
- _localReconnectStrategy,
632
644
_createClientBase,
633
645
_createClientAndConnect,
634
646
_clientExec,
0 commit comments