Skip to content

Commit e777717

Browse files
committed
feat: add fallback logic for typesense client timeouts
- reintroduce random fallback values for connection and retry intervals when environment variables are missing or invalid - modify `createTypesenseClient` to reuse fallback logic - add test suite to verify default and random behavior for client config - extend test script to include `typesenseClientDefaults.spec.js`
1 parent 6320cf9 commit e777717

File tree

4 files changed

+134
-5
lines changed

4 files changed

+134
-5
lines changed

functions/src/config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ module.exports = {
1111
typesenseProtocol: process.env.TYPESENSE_PROTOCOL || "https",
1212
typesenseCollectionName: process.env.TYPESENSE_COLLECTION_NAME,
1313
typesenseAPIKey: process.env.TYPESENSE_API_KEY,
14-
typesenseConnectionTimeoutSeconds: parseInt(process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS || "60", 10),
15-
typesenseRetryIntervalSeconds: parseInt(process.env.TYPESENSE_RETRY_INTERVAL_SECONDS || "60", 10),
14+
typesenseConnectionTimeoutSeconds: parseInt(process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS, 10),
15+
typesenseRetryIntervalSeconds: parseInt(process.env.TYPESENSE_RETRY_INTERVAL_SECONDS, 10),
1616
typesenseBackfillTriggerDocumentInFirestore: "typesense_sync/backfill",
1717
typesenseBackfillBatchSize: 1000,
1818
};
Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
const config = require("./config.js");
22
const Typesense = require("typesense");
33

4+
// eslint-disable-next-line require-jsdoc
5+
function getRandomNumber(min, max) {
6+
return Math.floor(Math.random() * (max - min + 1)) + min;
7+
}
8+
49
module.exports = function () {
10+
const connectionTimeout = (!process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS || Number.isNaN(config.typesenseConnectionTimeoutSeconds))
11+
? getRandomNumber(60, 90)
12+
: config.typesenseConnectionTimeoutSeconds;
13+
14+
const retryInterval = (!process.env.TYPESENSE_RETRY_INTERVAL_SECONDS || Number.isNaN(config.typesenseRetryIntervalSeconds))
15+
? getRandomNumber(60, 120)
16+
: config.typesenseRetryIntervalSeconds;
17+
518
return new Typesense.Client({
619
nodes: config.typesenseHosts.map((h) => {
720
return {
@@ -11,7 +24,7 @@ module.exports = function () {
1124
};
1225
}),
1326
apiKey: config.typesenseAPIKey,
14-
connectionTimeoutSeconds: config.typesenseConnectionTimeoutSeconds,
15-
retryIntervalSeconds: config.typesenseRetryIntervalSeconds,
27+
connectionTimeoutSeconds: connectionTimeout,
28+
retryIntervalSeconds: retryInterval,
1629
});
1730
};

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
"scripts": {
55
"emulator": "cross-env DOTENV_CONFIG=extensions/test-params-flatten-nested-false.local.env firebase emulators:start --import=emulator_data",
66
"export": "firebase emulators:export emulator_data",
7-
"test": "npm run test:flatttened && npm run test:unflattened && npm run test:subcollection",
7+
"test": "npm run test:flatttened && npm run test:unflattened && npm run test:subcollection && npm run test:typesenseClientDefaults",
88
"test:flatttened": "cp -f extensions/test-params-flatten-nested-true.local.env functions/.env && cross-env NODE_OPTIONS=--experimental-vm-modules DOTENV_CONFIG=extensions/test-params-flatten-nested-true.local.env firebase emulators:exec --only functions,firestore,extensions 'jest --testRegex=\"WithFlattening\" --testRegex=\"backfill.spec\"'",
99
"test:unflattened": "cp -f extensions/test-params-flatten-nested-false.local.env functions/.env && cross-env NODE_OPTIONS=--experimental-vm-modules DOTENV_CONFIG=extensions/test-params-flatten-nested-false.local.env firebase emulators:exec --only functions,firestore,extensions 'jest --testRegex=\"WithoutFlattening\"'",
1010
"test:subcollection": "jest --testRegex=\"writeLogging\" --testRegex=\"Subcollection\" --detectOpenHandles",
11+
"test:typesenseClientDefaults": "jest --testRegex=\"typesenseClientDefaults\"",
1112
"typesenseServer": "docker compose up",
1213
"lint:fix": "eslint . --fix",
1314
"lint": "eslint .",
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
describe("Typesense Client Default Random Configuration", () => {
2+
let originalConnectionTimeout;
3+
let originalRetryInterval;
4+
5+
beforeEach(() => {
6+
originalConnectionTimeout = process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS;
7+
originalRetryInterval = process.env.TYPESENSE_RETRY_INTERVAL_SECONDS;
8+
9+
jest.resetModules();
10+
11+
process.env.TYPESENSE_HOSTS = "localhost";
12+
process.env.TYPESENSE_API_KEY = "test-key";
13+
process.env.TYPESENSE_COLLECTION_NAME = "test-collection";
14+
});
15+
16+
afterEach(() => {
17+
if (originalConnectionTimeout !== undefined) {
18+
process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS = originalConnectionTimeout;
19+
} else {
20+
delete process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS;
21+
}
22+
23+
if (originalRetryInterval !== undefined) {
24+
process.env.TYPESENSE_RETRY_INTERVAL_SECONDS = originalRetryInterval;
25+
} else {
26+
delete process.env.TYPESENSE_RETRY_INTERVAL_SECONDS;
27+
}
28+
29+
delete process.env.TYPESENSE_HOSTS;
30+
delete process.env.TYPESENSE_API_KEY;
31+
delete process.env.TYPESENSE_COLLECTION_NAME;
32+
33+
jest.resetModules();
34+
});
35+
36+
describe("when environment variables are not set", () => {
37+
beforeEach(() => {
38+
delete process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS;
39+
delete process.env.TYPESENSE_RETRY_INTERVAL_SECONDS;
40+
41+
jest.resetModules();
42+
});
43+
44+
it("should use random connection timeout between 60 and 90 seconds", () => {
45+
const createTypesenseClient = require("../functions/src/createTypesenseClient");
46+
const client = createTypesenseClient();
47+
48+
expect(client.configuration.connectionTimeoutSeconds).toBeGreaterThanOrEqual(60);
49+
expect(client.configuration.connectionTimeoutSeconds).toBeLessThanOrEqual(90);
50+
expect(Number.isInteger(client.configuration.connectionTimeoutSeconds)).toBe(true);
51+
});
52+
53+
it("should use random retry interval between 60 and 120 seconds", () => {
54+
const createTypesenseClient = require("../functions/src/createTypesenseClient");
55+
const client = createTypesenseClient();
56+
57+
expect(client.configuration.retryIntervalSeconds).toBeGreaterThanOrEqual(60);
58+
expect(client.configuration.retryIntervalSeconds).toBeLessThanOrEqual(120);
59+
expect(Number.isInteger(client.configuration.retryIntervalSeconds)).toBe(true);
60+
});
61+
62+
it("should generate different random values on multiple client creations", () => {
63+
const createTypesenseClient = require("../functions/src/createTypesenseClient");
64+
const clients = [];
65+
const connectionTimeouts = new Set();
66+
const retryIntervals = new Set();
67+
68+
for (let i = 0; i < 10; i++) {
69+
const client = createTypesenseClient();
70+
clients.push(client);
71+
connectionTimeouts.add(client.configuration.connectionTimeoutSeconds);
72+
retryIntervals.add(client.configuration.retryIntervalSeconds);
73+
}
74+
75+
expect(connectionTimeouts.size).toBeGreaterThan(1);
76+
expect(retryIntervals.size).toBeGreaterThan(1);
77+
});
78+
});
79+
80+
describe("when environment variables are set", () => {
81+
beforeEach(() => {
82+
process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS = "45";
83+
process.env.TYPESENSE_RETRY_INTERVAL_SECONDS = "30";
84+
85+
jest.resetModules();
86+
});
87+
88+
it("should use the configured values instead of random ones", () => {
89+
const createTypesenseClient = require("../functions/src/createTypesenseClient");
90+
const client = createTypesenseClient();
91+
92+
expect(client.configuration.connectionTimeoutSeconds).toBe(45);
93+
expect(client.configuration.retryIntervalSeconds).toBe(30);
94+
});
95+
});
96+
97+
describe("when environment variables are set to invalid values", () => {
98+
beforeEach(() => {
99+
process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS = "invalid";
100+
process.env.TYPESENSE_RETRY_INTERVAL_SECONDS = "also-invalid";
101+
102+
jest.resetModules();
103+
});
104+
105+
it("should fall back to random values when parseInt returns NaN", () => {
106+
const createTypesenseClient = require("../functions/src/createTypesenseClient");
107+
const client = createTypesenseClient();
108+
109+
expect(client.configuration.connectionTimeoutSeconds).toBeGreaterThanOrEqual(60);
110+
expect(client.configuration.connectionTimeoutSeconds).toBeLessThanOrEqual(90);
111+
expect(client.configuration.retryIntervalSeconds).toBeGreaterThanOrEqual(60);
112+
expect(client.configuration.retryIntervalSeconds).toBeLessThanOrEqual(120);
113+
});
114+
});
115+
});

0 commit comments

Comments
 (0)