Skip to content

Commit 4955d6d

Browse files
allow providing a custom timeout to openfeature datadog provider (#6930)
1 parent fb4be89 commit 4955d6d

File tree

5 files changed

+105
-6
lines changed

5 files changed

+105
-6
lines changed

index.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,13 @@ declare namespace tracer {
654654
* @default false
655655
*/
656656
enabled?: boolean
657+
/**
658+
* Timeout in milliseconds for OpenFeature provider initialization.
659+
* If configuration is not received within this time, initialization fails.
660+
*
661+
* @default 30000
662+
*/
663+
initializationTimeoutMs?: number
657664
}
658665
};
659666

packages/dd-trace/src/config_defaults.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ module.exports = {
7777
'experimental.enableGetRumData': false,
7878
'experimental.exporter': undefined,
7979
'experimental.flaggingProvider.enabled': false,
80+
'experimental.flaggingProvider.initializationTimeoutMs': 30_000,
8081
flushInterval: 2000,
8182
flushMinSpans: 1000,
8283
gitMetadataEnabled: true,

packages/dd-trace/src/openfeature/flagging_provider.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,17 @@ class FlaggingProvider extends DatadogNodeServerProvider {
1515
* @param {import('../config')} config - Tracer configuration object
1616
*/
1717
constructor (tracer, config) {
18-
// Call parent constructor with required options
18+
// Call parent constructor with required options and timeout
1919
super({
20-
exposureChannel: channel(EXPOSURE_CHANNEL)
20+
exposureChannel: channel(EXPOSURE_CHANNEL),
21+
initializationTimeoutMs: config.experimental.flaggingProvider.initializationTimeoutMs
2122
})
2223

2324
this._tracer = tracer
2425
this._config = config
2526

26-
log.debug(this.constructor.name + ' created')
27+
log.debug(this.constructor.name + ' created with timeout: ' +
28+
config.experimental.flaggingProvider.initializationTimeoutMs + 'ms')
2729
}
2830

2931
/**

packages/dd-trace/test/openfeature/flagging_provider.spec.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ describe('FlaggingProvider', () => {
2323
mockConfig = {
2424
service: 'test-service',
2525
version: '1.0.0',
26-
env: 'test'
26+
env: 'test',
27+
experimental: {
28+
flaggingProvider: {
29+
enabled: true,
30+
initializationTimeoutMs: 30_000
31+
}
32+
}
2733
}
2834

2935
mockChannel = {
@@ -65,7 +71,7 @@ describe('FlaggingProvider', () => {
6571
const provider = new FlaggingProvider(mockTracer, mockConfig)
6672

6773
expect(provider).to.exist
68-
expect(log.debug).to.have.been.calledWith('FlaggingProvider created')
74+
expect(log.debug).to.have.been.calledWith('FlaggingProvider created with timeout: 30000ms')
6975
})
7076
})
7177

packages/dd-trace/test/openfeature/flagging_provider_timeout.spec.js

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ describe('FlaggingProvider Initialization Timeout', () => {
2828
mockConfig = {
2929
service: 'test-service',
3030
version: '1.0.0',
31-
env: 'test'
31+
env: 'test',
32+
experimental: {
33+
flaggingProvider: {
34+
enabled: true,
35+
initializationTimeoutMs: 30_000 // Default timeout
36+
}
37+
}
3238
}
3339

3440
mockChannel = {
@@ -173,4 +179,81 @@ describe('FlaggingProvider Initialization Timeout', () => {
173179
expect(readyEventSpy).to.have.been.calledOnce
174180
expect(provider.getConfiguration()).to.equal(ufc)
175181
})
182+
183+
describe('custom timeout configuration', () => {
184+
it('should use custom timeout when specified in config', async () => {
185+
const customConfig = {
186+
...mockConfig,
187+
experimental: {
188+
flaggingProvider: {
189+
enabled: true,
190+
initializationTimeoutMs: 5000 // Custom 5-second timeout
191+
}
192+
}
193+
}
194+
195+
const provider = new FlaggingProvider(mockTracer, customConfig)
196+
197+
const initPromise = provider.initialize()
198+
199+
// Attach catch handler
200+
initPromise.catch(() => {
201+
// Expected to reject on timeout
202+
})
203+
204+
// Verify initialization is in progress
205+
expect(provider.initController.isInitializing()).to.be.true
206+
207+
// Advance time by 4.9 seconds (before custom timeout)
208+
await clock.tickAsync(4900)
209+
210+
// Should still be initializing
211+
expect(provider.initController.isInitializing()).to.be.true
212+
213+
// Advance by another 200ms to trigger the 5-second timeout
214+
await clock.tickAsync(200)
215+
216+
// Wait for promise to settle
217+
await initPromise.catch(() => {})
218+
219+
// Should now be timed out
220+
expect(provider.initController.isInitializing()).to.be.false
221+
})
222+
223+
it('should call setError with custom timeout value in message', async () => {
224+
const customConfig = {
225+
...mockConfig,
226+
experimental: {
227+
flaggingProvider: {
228+
enabled: true,
229+
initializationTimeoutMs: 10_000 // Custom 10-second timeout
230+
}
231+
}
232+
}
233+
234+
const provider = new FlaggingProvider(mockTracer, customConfig)
235+
236+
// Spy on setError method
237+
const setErrorSpy = sinon.spy(provider, 'setError')
238+
239+
const initPromise = provider.initialize()
240+
241+
// Attach catch handler
242+
initPromise.catch(() => {
243+
// Expected to reject
244+
})
245+
246+
// Advance time to trigger custom timeout
247+
await clock.tickAsync(10000)
248+
249+
await initPromise.catch(() => {})
250+
251+
// Verify setError was called with custom timeout error
252+
expect(setErrorSpy).to.have.been.calledOnce
253+
const errorArg = setErrorSpy.firstCall.args[0]
254+
expect(errorArg).to.be.instanceOf(Error)
255+
expect(errorArg.message).to.include('Initialization timeout')
256+
expect(errorArg.message).to.include('10000ms')
257+
})
258+
})
176259
})

0 commit comments

Comments
 (0)