@@ -24,7 +24,7 @@ const INTEGRATION_MODE = Object.freeze({
2424 NO_REDIS : "NO_REDIS" ,
2525} ) ;
2626const CF_REDIS_SERVICE_LABEL = "redis-cache" ;
27- const REDIS_CLIENT_DEFAULT_PING_INTERVAL = 5 * 60000 ;
27+ const REDIS_CLIENT_DEFAULT_PING_INTERVAL = 4 * 60000 ;
2828
2929const cfEnv = CfEnv . getInstance ( ) ;
3030const logger = new Logger ( COMPONENT_NAME ) ;
@@ -35,14 +35,18 @@ const MODE = Object.freeze({
3535} ) ;
3636
3737let __messageHandlers ;
38- let __clientOptions ;
38+ let __customCredentials ;
39+ let __customClientOptions ;
40+ let __activeOptionsTuple ;
3941let __canGetClientPromise ;
4042let __mainClientPromise ;
4143let __subscriberClientPromise ;
4244let __integrationModePromise ;
4345const _reset = ( ) => {
4446 __messageHandlers = new HandlerCollection ( ) ;
45- __clientOptions = null ;
47+ __customCredentials = null ;
48+ __customClientOptions = null ;
49+ __activeOptionsTuple = null ;
4650 __canGetClientPromise = null ;
4751 __mainClientPromise = null ;
4852 __subscriberClientPromise = null ;
@@ -80,25 +84,20 @@ const _subscribedMessageHandler = async (message, channel) => {
8084 ) ;
8185} ;
8286
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+ } ,
9895 } ;
99- const credentials = cfEnv . cfServiceCredentialsForLabel ( CF_REDIS_SERVICE_LABEL ) ;
96+
97+ const credentials = __customCredentials || cfEnv . cfServiceCredentialsForLabel ( CF_REDIS_SERVICE_LABEL ) ;
10098 const hasCredentials = Object . keys ( credentials ) . length > 0 ;
101- const { cluster_mode : isCluster } = credentials ;
99+
100+ const isCluster = ! ! credentials . cluster_mode ;
102101 const credentialClientOptions = hasCredentials
103102 ? {
104103 password : credentials . password ,
@@ -112,13 +111,16 @@ const _createClientBase = (clientName) => {
112111
113112 // NOTE: documentation is buried here https://github.com/redis/node-redis/blob/master/docs/client-configuration.md
114113 const redisClientOptions = {
114+ ...defaultClientOptions ,
115115 ...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
118120 socket : {
119- ...localSocketOptions ,
121+ ...defaultClientOptions . socket ,
120122 ...credentialClientOptions ?. socket ,
121- ...__clientOptions ?. socket ,
123+ ...__customClientOptions ?. socket ,
122124 } ,
123125 } ;
124126
@@ -132,21 +134,31 @@ const _createClientBase = (clientName) => {
132134 redisClientOptions . socket . tls = ! ! redisClientOptions . socket . tls ;
133135 }
134136
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+ }
141139
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 ) ;
150162 } catch ( err ) {
151163 throw new VError (
152164 { name : VERROR_CLUSTER_NAME , cause : err , info : { clientName } } ,
@@ -189,8 +201,9 @@ const _closeClientBase = async (client) => {
189201 }
190202} ;
191203
192- const setClientOptions = ( clientOptions ) => {
193- __clientOptions = clientOptions ;
204+ const setCustomOptions = ( customCredentials , customClientOptions ) => {
205+ __customCredentials = customCredentials ;
206+ __customClientOptions = customClientOptions ;
194207} ;
195208
196209const _canGetClient = async ( ) => {
@@ -213,7 +226,7 @@ const _getIntegrationMode = async () => {
213226 return INTEGRATION_MODE . NO_REDIS ;
214227 }
215228 if ( cfEnv . isOnCf ) {
216- const { cluster_mode : isCluster } = cfEnv . cfServiceCredentialsForLabel ( CF_REDIS_SERVICE_LABEL ) ;
229+ const [ isCluster ] = _getRedisOptionsTuple ( ) ;
217230 return isCluster ? INTEGRATION_MODE . CF_REDIS_CLUSTER : INTEGRATION_MODE . CF_REDIS ;
218231 }
219232 return INTEGRATION_MODE . LOCAL_REDIS ;
@@ -304,7 +317,7 @@ const sendCommand = async (command) => {
304317 const mainClient = await getMainClient ( ) ;
305318
306319 try {
307- const { cluster_mode : isCluster } = cfEnv . cfServiceCredentialsForLabel ( CF_REDIS_SERVICE_LABEL ) ;
320+ const [ isCluster ] = _getRedisOptionsTuple ( ) ;
308321 if ( isCluster ) {
309322 // NOTE: the cluster sendCommand API has a different signature, where it takes two optional args: firstKey and
310323 // isReadonly before the command
@@ -594,7 +607,7 @@ const removeAllMessageHandlers = (channel) => __messageHandlers.removeAllHandler
594607
595608module . exports = {
596609 REDIS_INTEGRATION_MODE : INTEGRATION_MODE ,
597- setClientOptions ,
610+ setCustomOptions ,
598611 getIntegrationMode,
599612 getMainClient,
600613 closeMainClient,
@@ -628,7 +641,6 @@ module.exports = {
628641 _getSubscriberClient : ( ) => __subscriberClientPromise ,
629642 _setSubscriberClient : ( value ) => ( __subscriberClientPromise = value ) ,
630643 _subscribedMessageHandler,
631- _localReconnectStrategy,
632644 _createClientBase,
633645 _createClientAndConnect,
634646 _clientExec,
0 commit comments