feat(feature-flags): add agentless configuration keys, settings, and endpoint derivation - #9040
Conversation
BenchmarksBenchmark execution time: 2026-08-27 07:29:10 Comparing candidate commit b3f5318 in PR branch Found 1 performance improvements and 0 performance regressions! Performance is the same for 71 metrics, 0 unstable metrics, 67 known flaky benchmarks, 59 flaky benchmarks without significant changes.
|
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (9040) and master. ✅ No regressions detected |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac79e9b3c4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
This comment has been minimized.
This comment has been minimized.
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (9040) and master.
|
|||||||||||||||||||||||||||||||||||
| Metric | Master (Mean ± 95% CI) | Current (Mean ± 95% CI) | Change | Status |
|---|---|---|---|---|
| .NET Framework 4.8 - Baseline | ||||
| duration | 190.35 ± (190.91 - 191.69) ms | 212.09 ± (212.06 - 212.91) ms | +11.4% | ❌⬆️ |
| .NET Framework 4.8 - Bailout | ||||
| duration | 194.19 ± (194.09 - 194.56) ms | 218.74 ± (218.21 - 219.21) ms | +12.6% | ❌⬆️ |
| .NET Framework 4.8 - CallTarget+Inlining+NGEN | ||||
| duration | 1151.33 ± (1150.44 - 1155.97) ms | 1259.84 ± (1259.66 - 1265.99) ms | +9.4% | ❌⬆️ |
leoromanovsky
left a comment
There was a problem hiding this comment.
functionality looks in-sync with other implementations 👍
92546cd to
ada921d
Compare
andrewlock
left a comment
There was a problem hiding this comment.
Thanks - there's unfortunately still an issue with env which annoyingly may mean we need to redesign this whole thing again 🤦♂️
…o.khrebto/EX-2703/ffe-config-and-endpoint
…o.khrebto/EX-2703/ffe-config-and-endpoint
… the other tracers
…o.khrebto/EX-2703/ffe-config-and-endpoint
## Summary of changes Adds the agentless delivery mechanism: a `UfcConfigurationParser` that validates the JSON:API envelope returned by the CDN endpoint, and an `AgentlessConfigurationSource` HTTP poller with retries, ETag caching, gzip decompression, and shutdown safety. This is PR 2 of a stacked PR series implementing agentless Feature Flags configuration delivery (FFL-2703), porting functionality from [dd-trace-py#19331](DataDog/dd-trace-py#19331) and [dd-trace-java#11892](DataDog/dd-trace-java#11892). Stacked on [PR 1 #9040](#9040). ## Reason for change PR 1 added the configuration layer (settings, endpoint derivation). This PR adds the actual HTTP delivery mechanism that polls the CDN endpoint for UFC flag configuration. It is self-contained: the poller and parser are fully testable in isolation and are not yet wired to the `FeatureFlagsModule` (that happens in PR 3). ## Implementation details - **`UfcConfigurationParser`** — validates the JSON:API Universal Flag Configuration response envelope, extracts `data.attributes` into `ServerConfiguration`. Rejects raw UFC documents (without the JSON:API wrapper) so all delivery sources agree on one wire format. Returns descriptive errors; never throws. - **`AgentlessConfigurationSource`** — HTTP poller with: - Fixed-delay polling (no overlap) - In-tick retries (max 3) for 408/429/5xx/network failures, with jittered backoff derived from the poll interval (`clamp(P/6, 2s, 10s)` then `clamp(P/3, 5s, 30s)`) - ETag / `If-None-Match` conditional polling; ETag advances only after parse + apply succeed - Gzip decompression (neither transport decompresses automatically) - Self-tracing suppression (`x-datadog-tracing-enabled: false` header) - API key sent only to managed (Datadog-derived) endpoints, never custom ones - Warn-once per failure category (authentication, http, request) to prevent log flooding - Shutdown safety: `CancellationTokenSource` cancels the loop; shutdown checks after request and before `ApplyAsync` prevent use-after-dispose - `Create()` factory returns `null` when endpoint can'"'t be built or API key is missing - Idempotent `Start()` via `Interlocked.CompareExchange` ## Test coverage - **`UfcConfigurationParserTests`** (16 tests) — valid envelope parsing, malformed JSON rejection, invalid envelope rejection (wrong type, missing data, raw UFC), invalid attributes rejection (missing format/createdAt/environment/flags, wrong types), flag parsing - **`AgentlessConfigurationSourceTests`** (12 tests) — 200 success, ETag forwarding, 304/401/malformed handling, disposal before/after request, retry on 500 then success, max retries on 500, no retry on 400, gzip response, network error, idempotent start ## Other details <!-- Fixes #{issue} --> Stacked PRs: - PR 1 [#9040](#9040): Configuration keys, settings, and endpoint derivation - PR 2 (this): UFC parser and agentless HTTP poller - PR 3: Module wiring and OpenFeature activation - PR 4: Manual API activation <!--⚠️ Note: Where possible, please obtain 2 approvals prior to merging. Unless CODEOWNERS specifies otherwise, for external teams it is typically best to have one review from a team member, and one review from apm-dotnet. Trivial changes do not require 2 reviews. MergeQueue is NOT enabled in this repository. If you have write access to the repo, the PR has 1-2 approvals (see above), and all of the required checks have passed, you can use the Squash and Merge button to merge the PR. If you don't have write access, or you need help, reach out in the #apm-dotnet channel in Slack. --> System tests: [DataDog/system-tests#7496](DataDog/system-tests#7496)
…InitializeAsync (#9044) ## Summary of changes Wires the agentless poller and RC subscription into `FeatureFlagsModule` with source-agnostic activation, and adds `InitializeAsync` through the OpenFeature provider lifecycle so delivery starts when application code initializes the provider. This is PR 3 of a stacked PR series implementing agentless Feature Flags configuration delivery (FFL-2703), porting functionality from [dd-trace-py#19331](DataDog/dd-trace-py#19331) and [dd-trace-java#11892](DataDog/dd-trace-java#11892). Stacked on [PR 1 #9040](#9040) and [PR 2 #9042](#9042). ## Reason for change PR 1 added the configuration layer and PR 2 added the poller and parser. This PR connects them: the module now selects the delivery source at construction time, starts polling only when the provider is initialized, and waits for the first configuration before returning from `InitializeAsync`. ## Implementation details - **`TracerSettings`** — replaces `IsFlaggingProviderEnabled` with `FeatureFlagsSettings FeatureFlags` property - **`FeatureFlagsModule`** — source-agnostic constructor: - RC subscription only when `Source == RemoteConfig` (gated, no longer always-on) - `Create()` checks `settings.FeatureFlags.Enabled` - `Activate()` starts the agentless poller or marks RC intent, with `_deliveryStarted` tracking - `InitializeAsync()` calls `Activate()`, waits for `FirstConfigReceived` with timeout, skips the wait if no delivery started - `ApplyConfiguration()` — public method used by both agentless and RC paths, signals `FirstConfigReceived` - `Dispose()` now also disposes the agentless source and unsubscribes RC - **`OpenFeatureSdkInitializeAsyncIntegration`** (new CallTarget) — intercepts `FeatureFlagsSdk.InitializeAsync` stub and calls `FeatureFlagsModule.InitializeAsync` - **`DatadogProvider.InitializeAsync`** — override that delegates to `FeatureFlagsSdk.InitializeAsync` - **`FeatureFlagsSdk.InitializeAsync`** — no-op stub in the OpenFeature package - **`supported_calltargets.g.json`** — registers the new integration - **`SpanEnrichmentIntegrationTests`** — suppresses `[Obsolete]` warning on `FlaggingProviderEnabled` ## Test coverage - **`FeatureFlagsModuleTests`** (9 tests) — RC config update/reset, agentless source doesn't subscribe to RC, RC source subscribes and advertises capability, disabled returns null, `InitializeAsync` returns immediately when config already applied, returns when config arrives while waiting, returns on timeout without throwing, returns on cancellation without throwing, returns immediately when agentless source cannot start (no API key) ## Other details <!-- Fixes #{issue} --> Stacked PRs: - PR 1 [#9040](#9040): Configuration keys, settings, and endpoint derivation - PR 2 [#9042](#9042): UFC parser and agentless HTTP poller - PR 3 (this): Module wiring and OpenFeature activation - PR 4: Manual API activation <!--⚠️ Note: Where possible, please obtain 2 approvals prior to merging. Unless CODEOWNERS specifies otherwise, for external teams it is typically best to have one review from a team member, and one review from apm-dotnet. Trivial changes do not require 2 reviews. MergeQueue is NOT enabled in this repository. If you have write access to the repo, the PR has 1-2 approvals (see above), and all of the required checks have passed, you can use the Squash and Merge button to merge the PR. If you don't have write access, or you need help, reach out in the #apm-dotnet channel in Slack. --> System tests: [DataDog/system-tests#7496](DataDog/system-tests#7496)
Summary of changes
Adds the foundational configuration layer for agentless Feature Flags delivery: new configuration keys, a
FeatureFlagsSettingsclass with source resolution and validation, and anAgentlessEndpointstruct for deriving the CDN URL from the Datadog site or a custom base URL.This is PR 1 of a stacked PR series implementing agentless Feature Flags configuration delivery (FFL-2703), porting functionality from dd-trace-py#19331 and dd-trace-java#11892.
Reason for change
The .NET tracer currently delivers Feature Flags configuration exclusively through the Datadog Agent's Remote Configuration. To support agentless (CDN-backed) delivery, we need a configuration layer that:
agentless(new default),remote_config, anddisabledsourcesDD_SITEor accepts a customDD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URLDD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLEDusers onto Remote ConfigurationImplementation details
FeatureFlagsSourceenum —Disabled,Agentless,RemoteConfigFeatureFlagsSettings— reads all new env vars, resolves the source with the cross-SDK precedence contract (kill switch → explicit source → fail-closed → legacy grandfathering → default agentless), validates poll interval (1–3600s) and request timeout (>0s)AgentlessEndpoint— deriveshttps://ufc-server.ff-cdn.<site>/api/v2/feature-flagging/config/rules-based/serverfrom the site, appendsdd_envwhen configured, accepts custom HTTP/HTTPS URLs, and never echoes URLs in error messages (credentials safety)supported-configurations.yaml+ generatedConfigurationKeys.FeatureFlags.g.cs:DD_FEATURE_FLAGS_ENABLED(defaulttrue, supersedesDD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED)DD_FEATURE_FLAGS_CONFIGURATION_SOURCE(defaultagentless)DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URLDD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS(default 30, typeint)DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS(default 5, typeint)DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS(default 10000, implementation B)[Obsolete]onFlaggingProviderEnabledwith pragma suppression inFeatureFlagsSettingsThis PR does not wire anything to
FeatureFlagsModuleyet — that happens in a later PR in the stack.Test coverage
FeatureFlagsSettingsTests— 43 test cases covering source resolution (kill switch, explicit source, legacy grandfathering,offlinesentinel, invalid values), blank/whitespace handling, casing normalization, defaults, poll interval validation, request timeout validation, initialization timeout, and base URL readingAgentlessEndpointTests— 18 test cases covering managed endpoint derivation (site lowercasing, staging, govcloud),dd_envquery parameter (addition, null, escaping), custom endpoint path handling (origin-only gets canonical path, custom path used verbatim), HTTP acceptance for custom endpoints, invalid URL rejection, empty/whitespace site rejection, and credentials-in-error safetyOther details
Stacked PRs:
System tests: DataDog/system-tests#7496