Skip to content

Commit e5b50e4

Browse files
authored
Fb/fixes after trails (#97)
* silent client should disable reconnect strategy for quick connection check * no need for localhost and default port in default client options * better handling of default-env * fix jsdocs order * update snaps * easier to read code * changelog for fixes
1 parent 5753273 commit e5b50e4

File tree

4 files changed

+70
-29
lines changed

4 files changed

+70
-29
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
99

1010
## v1.2.5 - tbd
1111

12+
### Fixed
13+
14+
- core: if a `./default-env.json` file was present and `NODE_ENV !== "production"`, we had assumed that cf env variables
15+
`VCAP_APPLCATION` and `VCAP_SERVICES` are always present in the file. now this is more resilient.
16+
17+
- core: [regression] initialize timing was noticeably slower, because the initial connection check was running with the
18+
default reconnect strategy that takes 2000ms. now the reconnect strategy is disabled for the initial connection check.
19+
1220
## v1.2.4 - 2025-02-19
1321

1422
### Changed

src/redis-adapter.js

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ const INTEGRATION_MODE = Object.freeze({
2323
LOCAL_REDIS: "LOCAL_REDIS",
2424
NO_REDIS: "NO_REDIS",
2525
});
26+
const CLIENT_NAME = Object.freeze({
27+
SILENT: "SILENT",
28+
MAIN: "MAIN",
29+
SUBSCRIBER: "SUBSCRIBER",
30+
});
2631
const CF_REDIS_SERVICE_LABEL = "redis-cache";
2732
const REDIS_CLIENT_DEFAULT_PING_INTERVAL = 4 * 60000;
2833

@@ -88,10 +93,6 @@ const _getRedisOptionsTuple = () => {
8893
if (!__activeOptionsTuple) {
8994
const defaultClientOptions = {
9095
pingInterval: REDIS_CLIENT_DEFAULT_PING_INTERVAL,
91-
socket: {
92-
host: "localhost",
93-
port: 6379,
94-
},
9596
};
9697

9798
const credentials = __customCredentials || cfEnv.cfServiceCredentialsForLabel(CF_REDIS_SERVICE_LABEL);
@@ -100,11 +101,11 @@ const _getRedisOptionsTuple = () => {
100101
const isCluster = !!credentials.cluster_mode;
101102
const credentialClientOptions = hasCredentials
102103
? {
103-
password: credentials.password,
104+
...(Object.prototype.hasOwnProperty.call(credentials, "password") && { password: credentials.password }),
104105
socket: {
105-
host: credentials.hostname,
106-
port: credentials.port,
107-
tls: credentials.tls,
106+
...(Object.prototype.hasOwnProperty.call(credentials, "hostname") && { host: credentials.hostname }),
107+
...(Object.prototype.hasOwnProperty.call(credentials, "port") && { port: credentials.port }),
108+
...(Object.prototype.hasOwnProperty.call(credentials, "tls") && { tls: credentials.tls }),
108109
},
109110
}
110111
: undefined;
@@ -140,18 +141,28 @@ const _getRedisOptionsTuple = () => {
140141
return __activeOptionsTuple;
141142
};
142143

144+
const _noReconnectStrategy = () => new VError({ name: VERROR_CLUSTER_NAME }, "disabled reconnect");
145+
143146
/**
144147
* Lazily create a new redis client. Client creation transparently handles
145148
* - custom credentials and client options passed in via {@link setCustomOptions},
146149
* - Cloud Foundry service with label "redis-cache" (hyperscaler option), and
147150
* - local redis-server.
148151
*
152+
* @param {string} clientName
153+
* @param {CreateOptions} [options]
149154
* @returns {RedisClient|RedisCluster}
150155
* @private
151156
*/
152-
const _createClientBase = (clientName) => {
157+
const _createClientBase = (clientName, options = {}) => {
158+
const { doReconnect = true } = options;
153159
try {
154160
const [isCluster, redisClientOptions] = _getRedisOptionsTuple();
161+
162+
if (!doReconnect) {
163+
redisClientOptions.socket.reconnectStrategy = _noReconnectStrategy;
164+
}
165+
155166
return isCluster
156167
? redis.createCluster({
157168
rootNodes: [redisClientOptions], // NOTE: assume this ignores everything but socket/url
@@ -167,10 +178,21 @@ const _createClientBase = (clientName) => {
167178
}
168179
};
169180

170-
const _createClientAndConnect = async (clientName, { doLogEvents = true } = {}) => {
181+
/**
182+
* @typedef CreateOptions
183+
* @type object
184+
* @property {boolean} [doLogEvents] log error events, defaults to true
185+
* @property {boolean} [doReconnect] setting to false disables reconnect strategy, defaults to true
186+
*/
187+
/**
188+
* @param {string} clientName
189+
* @param {CreateOptions} [options]
190+
*/
191+
const _createClientAndConnect = async (clientName, options = {}) => {
192+
const { doLogEvents = true } = options;
171193
let client = null;
172194
try {
173-
client = _createClientBase(clientName);
195+
client = _createClientBase(clientName, options);
174196
} catch (err) {
175197
throw new VError({ name: VERROR_CLUSTER_NAME, cause: err, info: { clientName } }, "error during create client");
176198
}
@@ -210,7 +232,10 @@ const _canGetClient = async () => {
210232
if (__canGetClientPromise === null) {
211233
__canGetClientPromise = (async () => {
212234
try {
213-
const silentClient = await _createClientAndConnect("silent", { doLogEvents: false });
235+
const silentClient = await _createClientAndConnect(CLIENT_NAME.SILENT, {
236+
doLogEvents: false,
237+
doReconnect: false,
238+
});
214239
await _closeClientBase(silentClient);
215240
return true;
216241
} catch {} // eslint-disable-line no-empty
@@ -251,7 +276,7 @@ const getIntegrationMode = async () => {
251276
*/
252277
const getMainClient = async () => {
253278
if (!__mainClientPromise) {
254-
__mainClientPromise = _createClientAndConnect("main");
279+
__mainClientPromise = _createClientAndConnect(CLIENT_NAME.MAIN);
255280
}
256281
return await __mainClientPromise;
257282
};
@@ -277,7 +302,7 @@ const closeMainClient = async () => {
277302
*/
278303
const getSubscriberClient = async () => {
279304
if (!__subscriberClientPromise) {
280-
__subscriberClientPromise = _createClientAndConnect("subscriber");
305+
__subscriberClientPromise = _createClientAndConnect(CLIENT_NAME.SUBSCRIBER);
281306
}
282307
return await __subscriberClientPromise;
283308
};

src/shared/cf-env.js

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
"use strict";
22

3+
const pathlib = require("path");
4+
const fs = require("fs");
5+
36
const ENV = Object.freeze({
47
USER: "USER",
58
CF_APP: "VCAP_APPLICATION",
@@ -23,18 +26,26 @@ class CfEnv {
2326
} catch (err) {} // eslint-disable-line no-empty
2427
}
2528

26-
constructor(env = process.env) {
27-
if (env.NODE_ENV !== "production" && env.USE_DEFAULT_ENV) {
28-
try {
29-
const { VCAP_APPLICATION, VCAP_SERVICES } = require(process.cwd() + "/default-env.json");
30-
if (VCAP_APPLICATION && !Object.prototype.hasOwnProperty.call(env, "VCAP_APPLICATION")) {
31-
env.VCAP_APPLICATION = JSON.stringify(VCAP_APPLICATION);
32-
}
33-
if (VCAP_SERVICES && !Object.prototype.hasOwnProperty.call(env, "VCAP_SERVICES")) {
34-
env.VCAP_SERVICES = JSON.stringify(VCAP_SERVICES);
29+
static readDefaultEnv() {
30+
try {
31+
const defaultEnvRaw = fs.readFileSync(pathlib.resolve(process.cwd(), "default-env.json"), "utf8");
32+
return Object.entries(JSON.parse(defaultEnvRaw)).reduce((acc, [key, value]) => {
33+
if (value !== null) {
34+
acc[key] = JSON.stringify(value);
3535
}
36-
} catch (err) {} // eslint-disable-line no-empty
37-
}
36+
return acc;
37+
}, {});
38+
} catch (err) {} // eslint-disable-line no-empty
39+
}
40+
41+
constructor(inputEnv = process.env) {
42+
const env =
43+
inputEnv.NODE_ENV === "production"
44+
? inputEnv
45+
: {
46+
...CfEnv.readDefaultEnv(),
47+
...inputEnv,
48+
};
3849

3950
this.isOnCf = env[ENV.USER] === "vcap";
4051
this.cfApp = CfEnv.parseEnvVar(env, ENV.CF_APP) || {};

test/redis-adapter.test.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,7 @@ describe("redis-adapter test", () => {
7474
[
7575
{
7676
"pingInterval": 240000,
77-
"socket": {
78-
"host": "localhost",
79-
"port": 6379,
80-
},
77+
"socket": {},
8178
},
8279
]
8380
`);

0 commit comments

Comments
 (0)