Skip to content

Commit a3d2489

Browse files
committed
chore: Adds LDDataSystemOptions for configuring the Data System.
1 parent fa634f5 commit a3d2489

File tree

10 files changed

+398
-51
lines changed

10 files changed

+398
-51
lines changed

packages/shared/common/src/datasource/CompositeDataSource.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ interface TransitionRequest {
2929
err?: Error;
3030
}
3131

32+
// TODO SDK-858: move this out of API directory to neighbor datasource folder
3233
/**
3334
* The {@link CompositeDataSource} can combine a number of {@link DataSystemInitializer}s and {@link DataSystemSynchronizer}s
3435
* into a single {@link DataSource}, implementing fallback and recovery logic internally to choose where data is sourced from.

packages/shared/sdk-server/__tests__/options/Configuration.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,4 +408,8 @@ describe('when setting different options', () => {
408408
},
409409
]);
410410
});
411+
412+
it('drop', () => {
413+
414+
}
411415
});

packages/shared/sdk-server/src/LDClientImpl.ts

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ import {
3030
import { Hook } from './api/integrations/Hook';
3131
import { BigSegmentStoreMembership } from './api/interfaces';
3232
import { LDWaitForInitializationOptions } from './api/LDWaitForInitializationOptions';
33+
import {
34+
isPollingOptions,
35+
isStandardOptions,
36+
isStreamingOptions,
37+
} from './api/options/LDDataSystemOptions';
3338
import BigSegmentsManager from './BigSegmentsManager';
3439
import BigSegmentStoreStatusProvider from './BigSegmentStatusProviderImpl';
3540
import { createStreamListeners } from './data_sources/createStreamListeners';
@@ -219,25 +224,34 @@ export default class LDClientImpl implements LDClient {
219224
const listeners = createStreamListeners(dataSourceUpdates, this._logger, {
220225
put: () => this._initSuccess(),
221226
});
222-
const makeDefaultProcessor = () =>
223-
config.stream
224-
? new StreamingProcessor(
225-
clientContext,
226-
'/all',
227-
[],
228-
listeners,
229-
baseHeaders,
230-
this._diagnosticsManager,
231-
(e) => this._dataSourceErrorHandler(e),
232-
this._config.streamInitialReconnectDelay,
233-
)
234-
: new PollingProcessor(
235-
config,
236-
new Requestor(config, this._platform.requests, baseHeaders),
237-
dataSourceUpdates,
238-
() => this._initSuccess(),
239-
(e) => this._dataSourceErrorHandler(e),
240-
);
227+
const makeDefaultProcessor = () => {
228+
if (isPollingOptions(config.dataSystem.dataSource)) {
229+
return new PollingProcessor(
230+
new Requestor(config, this._platform.requests, baseHeaders),
231+
config.dataSystem.dataSource.pollInterval ?? 30,
232+
dataSourceUpdates,
233+
config.logger,
234+
() => this._initSuccess(),
235+
(e) => this._dataSourceErrorHandler(e),
236+
);
237+
}
238+
// TODO: SDK-858 Hook up composite data source and config
239+
const reconnectDelay =
240+
isStandardOptions(config.dataSystem.dataSource) ||
241+
isStreamingOptions(config.dataSystem.dataSource)
242+
? config.dataSystem.dataSource.streamInitialReconnectDelay
243+
: 1;
244+
return new StreamingProcessor(
245+
clientContext,
246+
'/all',
247+
[],
248+
listeners,
249+
baseHeaders,
250+
this._diagnosticsManager,
251+
(e) => this._dataSourceErrorHandler(e),
252+
reconnectDelay,
253+
);
254+
};
241255

242256
if (!(config.offline || config.useLdd)) {
243257
this._updateProcessor =
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import { LDClientContext, subsystem } from '@launchdarkly/js-sdk-common';
2+
3+
import { LDDataSourceUpdates, LDFeatureStore } from '../subsystems';
4+
5+
/**
6+
* Configuration options for the Data System that the SDK uses to get and maintain flags and other
7+
* data from LaunchDarkly and other sources.
8+
*
9+
* Example (Recommended):
10+
* ```typescript
11+
* let dataSystemOptions = {
12+
* dataSource: {
13+
* type: 'standard';
14+
* },
15+
* }
16+
*
17+
* Example (Polling with DynamoDB Persistent Store):
18+
* ```typescript
19+
* import { DynamoDBFeatureStore } from '@launchdarkly/node-server-sdk-dynamodb';
20+
*
21+
* let dataSystemOptions = {
22+
* dataSource: {
23+
* type: 'polling';
24+
* pollInterval: 300;
25+
* },
26+
* persistentStore: DynamoDBFeatureStore('your-table', { cacheTTL: 30 });
27+
* }
28+
* const client = init('my-sdk-key', { hooks: [new TracingHook()] });
29+
* ```
30+
*/
31+
export interface LDDataSystemOptions {
32+
/**
33+
* Configuration options for the Data Source that the SDK uses to get flags and other
34+
* data from the LaunchDarkly servers. Choose one of {@link StandardDataSourceOptions},
35+
* {@link StreamingDataSourceOptions}, or {@link PollingDataSourceOptions}; setting the
36+
* type and the optional fields you want to customize.
37+
*
38+
* If not specified, this defaults to using the {@link StandardDataSourceOptions} which
39+
* pefroms a combination of streaming and polling.
40+
*
41+
* See {@link LDDataSystemOptions} decoumentation for exmaples.
42+
*/
43+
dataSource?: DataSourceOptions;
44+
45+
/**
46+
* A component that obtains feature flag data and puts it in the feature store. Setting
47+
* this supercedes {@link LDDataSystemOptions#dataSource}.
48+
*/
49+
updateProcessor?:
50+
| object
51+
| ((
52+
clientContext: LDClientContext,
53+
dataSourceUpdates: LDDataSourceUpdates,
54+
initSuccessHandler: VoidFunction,
55+
errorHandler?: (e: Error) => void,
56+
) => subsystem.LDStreamProcessor);
57+
58+
/**
59+
* Before data has arrived from LaunchDarkly, the SDK is able to evaluate flags using
60+
* data from the persistent store. Once fresh data is available, the SDK will no longer
61+
* read from the persistent store, although it will keep it up-to-date for future startups.
62+
*/
63+
persistentStore?: LDFeatureStore | ((clientContext: LDClientContext) => LDFeatureStore);
64+
}
65+
66+
export type DataSourceOptions =
67+
| StandardDataSourceOptions
68+
| StreamingDataSourceOptions
69+
| PollingDataSourceOptions;
70+
71+
/**
72+
* This standard data source is the recommended datasource for most customers. It will use
73+
* a combination of streaming and polling to initialize the SDK, provide real time updates,
74+
* and can switch between streaming and polling automatically to provide redundancy.
75+
*/
76+
export interface StandardDataSourceOptions {
77+
type: 'standard';
78+
79+
/**
80+
* Sets the initial reconnect delay for the streaming connection, in seconds. Default if omitted.
81+
*
82+
* The streaming service uses a backoff algorithm (with jitter) every time the connection needs
83+
* to be reestablished. The delay for the first reconnection will start near this value, and then
84+
* increase exponentially for any subsequent connection failures.
85+
*
86+
* The default value is 1.
87+
*/
88+
streamInitialReconnectDelay?: number;
89+
90+
/**
91+
* The time between polling requests, in seconds. Default if omitted.
92+
*/
93+
pollInterval?: number;
94+
}
95+
96+
/**
97+
* This data source will make best effort to maintain a streaming connection to LaunchDarkly services
98+
* to provide real time data updates.
99+
*/
100+
export interface StreamingDataSourceOptions {
101+
type: 'streaming';
102+
103+
/**
104+
* Sets the initial reconnect delay for the streaming connection, in seconds. Default if omitted.
105+
*
106+
* The streaming service uses a backoff algorithm (with jitter) every time the connection needs
107+
* to be reestablished. The delay for the first reconnection will start near this value, and then
108+
* increase exponentially up to a maximum for any subsequent connection failures.
109+
*
110+
* The default value is 1.
111+
*/
112+
streamInitialReconnectDelay?: number;
113+
}
114+
115+
/**
116+
* This data source will periodically make a request to LaunchDarkly services to retrieve updated data.
117+
*/
118+
export interface PollingDataSourceOptions {
119+
type: 'polling';
120+
121+
/**
122+
* The time between polling requests, in seconds. Default if omitted.
123+
*/
124+
pollInterval?: number;
125+
}
126+
127+
export function isStandardOptions(u: any): u is StandardDataSourceOptions {
128+
return u.kind === 'standard';
129+
}
130+
131+
export function isStreamingOptions(u: any): u is StreamingDataSourceOptions {
132+
return u.kind === 'streaming';
133+
}
134+
135+
export function isPollingOptions(u: any): u is PollingDataSourceOptions {
136+
return u.kind === 'polling';
137+
}

packages/shared/sdk-server/src/api/options/LDOptions.ts

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { LDClientContext, LDLogger, subsystem, VoidFunction } from '@launchdarkl
33
import { Hook } from '../integrations/Hook';
44
import { LDDataSourceUpdates, LDFeatureStore } from '../subsystems';
55
import { LDBigSegmentsOptions } from './LDBigSegmentsOptions';
6+
import { LDDataSystemOptions } from './LDDataSystemOptions';
67
import { LDProxyOptions } from './LDProxyOptions';
78
import { LDTLSOptions } from './LDTLSOptions';
89

@@ -67,9 +68,48 @@ export interface LDOptions {
6768
* Some implementations provide the store implementation object itself, while others
6869
* provide a factory function that creates the store implementation based on the SDK
6970
* configuration; this property accepts either.
71+
*
72+
* If you specify the {@link LDOptions#dataSystem}, this setting will be ignored.
73+
*
74+
* @deprecated This is now superceded by {@link LDOptions#dataSystem} using property
75+
* {@link LDDataSystemOptions#persistentStore}. The scope of the {@link LDFeatureStore}
76+
* that you provide is being reduced in the next major version to only being responsible
77+
* for persistence. See documention on {@link LDDataSystemOptions#persistentStore} for
78+
* more information.
7079
*/
7180
featureStore?: LDFeatureStore | ((clientContext: LDClientContext) => LDFeatureStore);
7281

82+
/**
83+
* Configuration options for the Data System that the SDK uses to get and maintain flags and other
84+
* data from LaunchDarkly and other sources.
85+
*
86+
* Setting this option supercedes
87+
*
88+
* Example (Recommended):
89+
* ```typescript
90+
* let dataSystemOptions = {
91+
* dataSource: {
92+
* type: 'standard';
93+
* // options can be customized here, though defaults are recommended
94+
* },
95+
* }
96+
*
97+
* Example (Polling with DynamoDB Persistent Store):
98+
* ```typescript
99+
* import { DynamoDBFeatureStore } from '@launchdarkly/node-server-sdk-dynamodb';
100+
*
101+
* let dataSystemOptions = {
102+
* dataSource: {
103+
* type: 'polling';
104+
* pollInterval: 300;
105+
* },
106+
* persistentStore: DynamoDBFeatureStore('your-table', { cacheTTL: 30 });
107+
* }
108+
* const client = init('my-sdk-key', { hooks: [new TracingHook()] });
109+
* ```
110+
*/
111+
dataSystem?: LDDataSystemOptions;
112+
73113
/**
74114
* Additional parameters for configuring the SDK's Big Segments behavior.
75115
*
@@ -86,7 +126,10 @@ export interface LDOptions {
86126
/**
87127
* A component that obtains feature flag data and puts it in the feature store.
88128
*
89-
* By default, this is the client's default streaming or polling component.
129+
* If you specify the {@link LDOptions#dataSystem}, this setting will be ignored.
130+
*
131+
* @deprecated This has moved to {@link LDOptions#dataSystem}. Use property
132+
* {@link LDDataSystemOptions#updateProcessor} instead.
90133
*/
91134
updateProcessor?:
92135
| object
@@ -104,6 +147,12 @@ export interface LDOptions {
104147

105148
/**
106149
* The time between polling requests, in seconds. Ignored in streaming mode.
150+
*
151+
* If you specify the {@link LDOptions#dataSystem}, this setting will be ignored.
152+
*
153+
* @deprecated This functionality is now controlled by {@link LDOptions#dataSystem} by
154+
* specifying the {@link LDDataSystemOptions#dataSource}. Specifying the polling interval is still
155+
* available when using {@link StandardDataSourceOptions} or {@link PollingDataSourceOptions}.
107156
*/
108157
pollInterval?: number;
109158

@@ -122,6 +171,13 @@ export interface LDOptions {
122171
*
123172
* This is true by default. If you set it to false, the client will use polling.
124173
* Streaming should only be disabled on the advice of LaunchDarkly support.
174+
*
175+
* If you specify the {@link LDOptions#dataSystem}, this setting will be ignored.
176+
*
177+
* @deprecated This functionality is now controlled by {@link LDOptions#dataSystem}
178+
* by specifying {@link LDDataSystemOptions#dataSource} options. To opt out of
179+
* streaming, you can use the {@link PollingDataSourceOptions}. Streaming should
180+
* only be disabled on the advice of LaunchDarkly support.
125181
*/
126182
stream?: boolean;
127183

@@ -133,6 +189,13 @@ export interface LDOptions {
133189
* increase exponentially for any subsequent connection failures.
134190
*
135191
* The default value is 1.
192+
*
193+
* If you specify the {@link LDOptions#dataSystem}, this setting will be ignored.
194+
*
195+
* @deprecated This functionality is now controlled by {@link LDOptions#dataSystem}
196+
* by specifying {@link LDDataSystemOptions#dataSource} options. Specifying the reconnect
197+
* delay is still available when using {@link StandardDataSourceOptions} or
198+
* {@link StreamingDataSourceOptions}.
136199
*/
137200
streamInitialReconnectDelay?: number;
138201

packages/shared/sdk-server/src/api/options/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './LDOptions';
33
export * from './LDProxyOptions';
44
export * from './LDTLSOptions';
55
export * from './LDMigrationOptions';
6+
export * from './LDDataSystemOptions';

packages/shared/sdk-server/src/data_sources/PollingProcessor.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Configuration from '../options/Configuration';
1313
import { deserializePoll } from '../store';
1414
import VersionedDataKinds from '../store/VersionedDataKinds';
1515
import Requestor from './Requestor';
16+
import { isPollingOptions, isStandardOptions } from '../api/options/LDDataSystemOptions';
1617

1718
export type PollingErrorHandler = (err: LDPollingError) => void;
1819

@@ -22,22 +23,16 @@ export type PollingErrorHandler = (err: LDPollingError) => void;
2223
export default class PollingProcessor implements subsystem.LDStreamProcessor {
2324
private _stopped = false;
2425

25-
private _logger?: LDLogger;
26-
27-
private _pollInterval: number;
28-
2926
private _timeoutHandle: any;
3027

3128
constructor(
32-
config: Configuration,
3329
private readonly _requestor: Requestor,
30+
private readonly _pollInterval: number,
3431
private readonly _featureStore: LDDataSourceUpdates,
32+
private readonly _logger?: LDLogger,
3533
private readonly _initSuccessHandler: VoidFunction = () => {},
3634
private readonly _errorHandler?: PollingErrorHandler,
37-
) {
38-
this._logger = config.logger;
39-
this._pollInterval = config.pollInterval;
40-
}
35+
) {}
4136

4237
private _poll() {
4338
if (this._stopped) {

0 commit comments

Comments
 (0)