Skip to content

Commit 38ca87d

Browse files
committed
feat: add initial polling retries to BrowserDataManager
This update introduces a new option for initial polling retries in the BrowserDataManager. Changes: - The `initialPollingRetries` parameter has been added to the `BrowserIdentifyOptions` interface, allowing users to specify the maximum number of retries for the initial polling request, defaulting to 3. - The `_requestPayload` method has been refactored to implement this retry logic, enhancing the robustness of the polling mechanism.
1 parent 6e10b9f commit 38ca87d

File tree

2 files changed

+75
-15
lines changed

2 files changed

+75
-15
lines changed

packages/sdk/browser/src/BrowserDataManager.ts

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ import {
66
DataSourcePaths,
77
DataSourceState,
88
FlagManager,
9+
httpErrorMessage,
910
internal,
1011
LDEmitter,
1112
LDHeaders,
1213
LDIdentifyOptions,
1314
makeRequestor,
1415
Platform,
16+
shouldRetry,
17+
sleep,
1518
} from '@launchdarkly/js-client-sdk-common';
1619

1720
import { readFlagsFromBootstrap } from './bootstrap';
@@ -97,34 +100,84 @@ export default class BrowserDataManager extends BaseDataManager {
97100
this._debugLog('Identify - Flags loaded from cache. Continuing to initialize via a poll.');
98101
}
99102

100-
await this._finishIdentifyFromPoll(context, identifyResolve, identifyReject);
103+
await this._finishIdentifyFromPoll(
104+
context,
105+
identifyResolve,
106+
identifyReject,
107+
browserIdentifyOptions?.initialPollingRetries,
108+
);
101109
}
102110
this._updateStreamingState();
103111
}
104112

113+
/**
114+
* A helper function for the initial poll request. This is mainly here to facilitate
115+
* the retry logic.
116+
*
117+
* @param context - LDContext to request payload for.
118+
* @param maxRetries - Maximum number of retries to attempt. Defaults to 3.
119+
* @returns Payload as a string.
120+
*/
121+
private async _requestPayload(context: Context, maxRetries: number = 3): Promise<string> {
122+
const plainContextString = JSON.stringify(Context.toLDContext(context));
123+
const pollingRequestor = makeRequestor(
124+
plainContextString,
125+
this.config.serviceEndpoints,
126+
this.getPollingPaths(),
127+
this.platform.requests,
128+
this.platform.encoding!,
129+
this.baseHeaders,
130+
[],
131+
this.config.withReasons,
132+
this.config.useReport,
133+
this._secureModeHash,
134+
);
135+
136+
let lastError: any;
137+
let validMaxRetries = maxRetries ?? 3;
138+
139+
if (validMaxRetries < 1) {
140+
this.logger.warn(
141+
`initialPollingRetries is set to ${maxRetries}, which is less than 1. This is not supported and will be ignored. Defaulting to 3 retries.`,
142+
);
143+
validMaxRetries = 3;
144+
}
145+
146+
for (let attempt = 0; attempt <= validMaxRetries; attempt += 1) {
147+
try {
148+
// eslint-disable-next-line no-await-in-loop
149+
return await pollingRequestor.requestPayload();
150+
} catch (e: any) {
151+
if (!shouldRetry(e)) {
152+
throw e;
153+
}
154+
lastError = e;
155+
this._debugLog(httpErrorMessage(e, 'initial poll request', 'will retry'));
156+
// NOTE: current we are hardcoding the retry interval to 1 second.
157+
// We can make this configurable in the future.
158+
// TODO: Reviewer any thoughts on this? Probably the easiest thing is to make this configurable
159+
// however, we can also look into using the backoff logic to calculate the delay?
160+
if (attempt < validMaxRetries) {
161+
// eslint-disable-next-line no-await-in-loop
162+
await sleep(1000);
163+
}
164+
}
165+
}
166+
167+
throw lastError;
168+
}
169+
105170
private async _finishIdentifyFromPoll(
106171
context: Context,
107172
identifyResolve: () => void,
108173
identifyReject: (err: Error) => void,
174+
initialPollingRetries: number = 3,
109175
) {
110176
try {
111177
this.dataSourceStatusManager.requestStateUpdate(DataSourceState.Initializing);
112178

113-
const plainContextString = JSON.stringify(Context.toLDContext(context));
114-
const pollingRequestor = makeRequestor(
115-
plainContextString,
116-
this.config.serviceEndpoints,
117-
this.getPollingPaths(),
118-
this.platform.requests,
119-
this.platform.encoding!,
120-
this.baseHeaders,
121-
[],
122-
this.config.withReasons,
123-
this.config.useReport,
124-
this._secureModeHash,
125-
);
179+
const payload = await this._requestPayload(context, initialPollingRetries);
126180

127-
const payload = await pollingRequestor.requestPayload();
128181
try {
129182
const listeners = this.createStreamListeners(context, identifyResolve);
130183
const putListener = listeners.get('put');

packages/sdk/browser/src/BrowserIdentifyOptions.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,11 @@ export interface BrowserIdentifyOptions extends Omit<LDIdentifyOptions, 'waitFor
2828
* For more information, see the [SDK Reference Guide](https://docs.launchdarkly.com/sdk/features/bootstrapping#javascript).
2929
*/
3030
bootstrap?: unknown;
31+
32+
/**
33+
* The number of retries to attempt for the initial polling request.
34+
*
35+
* Defaults to 3.
36+
*/
37+
initialPollingRetries?: number;
3138
}

0 commit comments

Comments
 (0)