Skip to content

Commit 7b348ca

Browse files
add env var to configure ffe init time (#6948)
* feat(openfeature): add env vars for flagging provider configuration Add environment variable support for: - DD_FLAGGING_PROVIDER_ENABLED / DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED - DD_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS / DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS * fix(openfeature): use only DD_EXPERIMENTAL_* env vars for flagging provider Remove support for non-experimental env vars (DD_FLAGGING_PROVIDER_ENABLED and DD_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS) to align with other Datadog tracers (Go, Python, Ruby, Java, .NET) which only support the DD_EXPERIMENTAL_* variants.
1 parent 27af201 commit 7b348ca

File tree

4 files changed

+118
-3
lines changed

4 files changed

+118
-3
lines changed

index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ interface Tracer extends opentracing.Tracer {
148148
* OpenFeature Provider with Remote Config integration.
149149
*
150150
* Extends DatadogNodeServerProvider with Remote Config integration for dynamic flag configuration.
151-
* Enable with DD_FLAGGING_PROVIDER_ENABLED=true.
151+
* Enable with DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true.
152152
*
153153
* @beta This feature is in preview and not ready for production use
154154
*/
@@ -692,13 +692,15 @@ declare namespace tracer {
692692
/**
693693
* Whether to enable the feature flagging provider.
694694
* Requires Remote Config to be properly configured.
695+
* Can be configured via DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED environment variable.
695696
*
696697
* @default false
697698
*/
698699
enabled?: boolean
699700
/**
700701
* Timeout in milliseconds for OpenFeature provider initialization.
701702
* If configuration is not received within this time, initialization fails.
703+
* Can be configured via DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS environment variable.
702704
*
703705
* @default 30000
704706
*/

packages/dd-trace/src/config/index.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,7 @@ class Config {
600600
OTEL_TRACES_SAMPLER,
601601
OTEL_TRACES_SAMPLER_ARG,
602602
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED,
603+
DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS,
603604
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
604605
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
605606
OTEL_EXPORTER_OTLP_LOGS_PROTOCOL,
@@ -776,7 +777,15 @@ class Config {
776777
maybeFloat(DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS)
777778
unprocessedTarget['dynamicInstrumentation.uploadInterval'] = DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS
778779
this.#setString(target, 'env', DD_ENV || tags.env)
779-
this.#setBoolean(target, 'experimental.flaggingProvider.enabled', DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED)
780+
this.#setBoolean(
781+
target,
782+
'experimental.flaggingProvider.enabled',
783+
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED
784+
)
785+
if (DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS != null) {
786+
target['experimental.flaggingProvider.initializationTimeoutMs'] =
787+
maybeInt(DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS)
788+
}
780789
this.#setBoolean(target, 'traceEnabled', DD_TRACE_ENABLED)
781790
this.#setBoolean(target, 'experimental.aiguard.enabled', DD_AI_GUARD_ENABLED)
782791
this.#setString(target, 'experimental.aiguard.endpoint', DD_AI_GUARD_ENDPOINT)
@@ -1112,6 +1121,11 @@ class Config {
11121121
this.#setBoolean(opts, 'experimental.enableGetRumData', options.experimental?.enableGetRumData)
11131122
this.#setString(opts, 'experimental.exporter', options.experimental?.exporter)
11141123
this.#setBoolean(opts, 'experimental.flaggingProvider.enabled', options.experimental?.flaggingProvider?.enabled)
1124+
opts['experimental.flaggingProvider.initializationTimeoutMs'] = maybeInt(
1125+
options.experimental?.flaggingProvider?.initializationTimeoutMs
1126+
)
1127+
this.#optsUnprocessed['experimental.flaggingProvider.initializationTimeoutMs'] =
1128+
options.experimental?.flaggingProvider?.initializationTimeoutMs
11151129
opts.flushInterval = maybeInt(options.flushInterval)
11161130
this.#optsUnprocessed.flushInterval = options.flushInterval
11171131
opts.flushMinSpans = maybeInt(options.flushMinSpans)

packages/dd-trace/src/config/supported-configurations.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,11 @@
7373
"DD_ENV": ["A"],
7474
"DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED": ["A"],
7575
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED": ["A"],
76+
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS": ["A"],
7677
"DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED": ["A"],
7778
"DD_EXPERIMENTAL_TEST_OPT_GIT_CACHE_ENABLED": ["A"],
7879
"DD_EXPERIMENTAL_TEST_OPT_GIT_CACHE_DIR": ["A"],
7980
"DD_EXTERNAL_ENV": ["A"],
80-
"DD_FLAGGING_PROVIDER_ENABLED": ["A"],
8181
"DD_GIT_BRANCH": ["A"],
8282
"DD_GIT_COMMIT_AUTHOR_DATE": ["A"],
8383
"DD_GIT_COMMIT_AUTHOR_EMAIL": ["A"],

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

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,103 @@ describe('FlaggingProvider Initialization Timeout', () => {
255255
assert.strictEqual(errorArg.message, 'Initialization timeout after 10000ms')
256256
})
257257
})
258+
259+
describe('environment variable timeout configuration', () => {
260+
let originalEnv
261+
262+
beforeEach(() => {
263+
// Save original environment variable
264+
originalEnv = {
265+
DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS:
266+
process.env.DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS
267+
}
268+
})
269+
270+
afterEach(() => {
271+
// Restore original environment variable
272+
if (originalEnv.DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS !== undefined) {
273+
process.env.DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS =
274+
originalEnv.DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS
275+
} else {
276+
delete process.env.DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS
277+
}
278+
})
279+
280+
it('should use DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS environment variable', async () => {
281+
// Set environment variable for 6-second timeout
282+
process.env.DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS = '6000'
283+
284+
// Need to reload the config module to pick up env var
285+
delete require.cache[require.resolve('../../src/config')]
286+
const Config = require('../../src/config')
287+
const config = new Config({})
288+
289+
const provider = new FlaggingProvider(mockTracer, config)
290+
291+
// Spy on setError method to verify timeout message
292+
const setErrorSpy = sinon.spy(provider, 'setError')
293+
294+
const initPromise = provider.initialize()
295+
296+
// Attach catch handler
297+
initPromise.catch(() => {
298+
// Expected to reject
299+
})
300+
301+
// Advance time to trigger env var timeout
302+
await clock.tickAsync(6000)
303+
304+
await initPromise.catch(() => {})
305+
306+
// Verify setError was called with env var timeout error
307+
assert.strictEqual(setErrorSpy.calledOnce, true)
308+
const errorArg = setErrorSpy.firstCall.args[0]
309+
assert.ok(errorArg instanceof Error)
310+
assert.ok(errorArg.message.includes('Initialization timeout'))
311+
assert.ok(errorArg.message.includes('6000ms'))
312+
})
313+
314+
it('should use config object value over environment variables', async () => {
315+
// Set environment variable
316+
process.env.DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS = '7000'
317+
318+
// Config with explicit timeout (should override env var)
319+
const configWithTimeout = {
320+
...mockConfig,
321+
experimental: {
322+
flaggingProvider: {
323+
enabled: true,
324+
initializationTimeoutMs: 3000 // This should override env var
325+
}
326+
}
327+
}
328+
329+
const provider = new FlaggingProvider(mockTracer, configWithTimeout)
330+
331+
const initPromise = provider.initialize()
332+
333+
// Attach catch handler
334+
initPromise.catch(() => {
335+
// Expected to reject on timeout
336+
})
337+
338+
// Verify initialization is in progress
339+
assert.strictEqual(provider.initController.isInitializing(), true)
340+
341+
// Advance time by 2.9 seconds (before config timeout)
342+
await clock.tickAsync(2900)
343+
344+
// Should still be initializing
345+
assert.strictEqual(provider.initController.isInitializing(), true)
346+
347+
// Advance by another 200ms to trigger the 3-second timeout (config takes priority)
348+
await clock.tickAsync(200)
349+
350+
// Wait for promise to settle
351+
await initPromise.catch(() => {})
352+
353+
// Should now be timed out (using config value, not env var)
354+
assert.strictEqual(provider.initController.isInitializing(), false)
355+
})
356+
})
258357
})

0 commit comments

Comments
 (0)