Skip to content

Add dd-openfeature SDK OpenFeature provider - #12843

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 10 commits into
masterfrom
bbujon/ffe-sdk-provider
Oct 20, 2026
Merged

gh-worker-dd-mergequeue-cf854d[bot] merged 10 commits into
masterfrom
bbujon/ffe-sdk-provider

Conversation

@PerfectSlayer

@PerfectSlayer PerfectSlayer commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

What Does This Do

Adds com.datadog.openfeature.Provider, its evaluator (DDEvaluator), hooks (metrics, logging, span enrichment) and FeatureFlagsRuntime wiring configuration delivery and event pipelines together. Also ports #12774:

  • exposures are sent from a finally-stage hook once the evaluation succeeded, deduplicated by a provider-side cache that replaces the exposure pipeline cache;
  • applications can record exposures with the public ExposureHook;
  • UFC split features are added to the flag metadata;
  • requires OpenFeature SDK 1.23.0, so provider hooks run inside a MultiProvider.

The SDK is still not published; the agent-coupled provider remains the shipped one.

Motivation

Completes the standalone SDK (APMLP-1512).

Additional Notes

Most of this PR moves code from master's products/feature-flagging/feature-flagging-api (datadog.trace.api.openfeature). Review carefully the new internal/FeatureFlagsRuntime, the new Provider / DDEvaluator wiring (agent bridge SPI and Settings instead of FeatureFlaggingGateway, bootstrap classes and reflection), and the lazy OTel resolution in FlagEvalMetrics.

New files
File Notes
internal/FeatureFlagsRuntime Shared runtime, reference-counted per class loader (acquire() / release()), replacing the agent-side gateway. Picks the configuration source from Settings (Remote Config through the bridge, failing without the agent, or the CDN) and the event transport (agent event proxy, direct intake, or proxy → direct fallback). Owns the exposure and flag evaluation pipelines (evaluation pipeline skipped by the evaluation counts kill switch, both skipped without transport). Fans configuration out to listeners and replays it to late ones. A failed start is rolled back and not cached
internal/FeatureFlagsRuntimeTest Product disabled, Remote Config without agent, agentless without API key, kill switch, both pipelines through the proxy, listener replay / removal, reference counting, failed start, exposure gating
internal/evaluation/ConfigurationStore Atomic configuration snapshot + ready latch used by DDEvaluator, previously fields of the old DDEvaluator
CanonicalFixtureTest Cross-SDK canonical fixture cases extracted from master's DDEvaluatorTest. Reads the ffe-system-test-data submodule (dd-smoke-tests/openfeature/src/test/resources/ffe-system-test-data) with JsonReading instead of Moshi
FlagEvalMetricsLateSdkForkedTest An evaluation before the OTel SDK registration does not claim GlobalOpenTelemetry, and metrics flow once the SDK is set
Moved, package rename only (no logic change)
  • Evaluator, FlagEvalMetricsHook
  • Tests: FlagEvalMetricsHookTest, FlagEvalMetricsTest
Moved with significant changes
New file Master origin Changes
Provider Provider Constructor calls AgentBridges.detect() and Settings.load(bridge), builds DDEvaluator directly (reflective loadEvaluatorClass() / EVALUATOR_IMPL removed). Span enrichment gate read from Settings (SpanEnrichmentGate removed); SpanEnrichmentHook gets bridge.spanEnricher(). FlagEvalLoggingHook always added, resolving its writer lazily from the runtime; LinkageError guard dropped. FatalError message now uses the cause message. Public constructors and Options unchanged
DDEvaluator DDEvaluator Pure evaluation now lives in internal/evaluation/FlagEvaluator (#12840). Kept: evaluate() delegating to FlagEvaluator (OpenFeature types end to end, no conversion), exposure building (via the FlagEvaluator ExposureListener) and recording, context snapshot / flatten / prune helpers. Lifecycle: package-private (Runnable, AgentBridge, Settings) constructor; synchronized initialize() / shutdown() acquire / release FeatureFlagsRuntime and register a Consumer<ServerConfiguration> (was FeatureFlaggingGateway.activate() / ConfigListener); ConfigurationStore replaces latch + AtomicReference. Removed: agent compatibility probes (SPLIT_SERIAL_ID_SUPPORTED, USE_LEGACY_EXPOSURE_API, legacy ExposureEvent constructor) and static span enrichment gate. Exposures go to runtime.recordExposure() instead of FeatureFlaggingGateway.dispatch()
FlagEvalMetrics FlagEvalMetrics Was an eager one-time GlobalOpenTelemetry.get(). Now resolves the counter lazily on record() with getOrNoop(), rebuilds it when the global instance changes, falls back to get() only for OTel API < 1.57 or a legacy OTel javaagent bridge, disables itself on LinkageError. Synchronized shutdown() with a closed flag
FlagEvalLoggingHook FlagEvalLoggingHook Single Supplier<FlagEvaluationWriter> constructor; INSTANCE singleton, gateway constructors and isFlagEvaluationEnqueueEnabled() check removed
SpanEnrichmentHook SpanEnrichmentHook Calls the bridge SpanEnricher (serialId / runtimeDefault) instead of dispatching SpanEnrichmentEvent on the gateway. Capture logic unchanged
jmh/FlagEvalHookHotPathBenchmark same Gateway enqueue toggle and @TearDown removed, writer injected through a supplier, run command now :dd-openfeature:jmh
ProviderTest ProviderTest Gateway / writer stubs replaced by fake bridges (ProxyAgentBridge, LocalHttpServer, TestSettings). Internal API linkage failure tests removed. New: product disabled, Remote Config without agent, end-to-end evaluation posted through the pipeline
DDEvaluatorTest DDEvaluatorTest Agent compatibility, legacy exposure, gateway activation and allocation date ABI tests removed; canonical fixtures moved to CanonicalFixtureTest. Exposures captured by overriding recordExposure. New: type compatibility, container cycles in context copy / flatten, empty and scalar values
DDEvaluatorSpanEnrichmentTest DDEvaluatorSpanEnrichmentForkedTest No longer forked: the gate comes from TestSettings instead of a static. Tests for agents without serial id support removed
FlagEvalLoggingHookTest FlagEvalLoggingHookTest Mockito writer; enqueue-disabled and gateway consent tests removed
SpanEnrichmentHookTest SpanEnrichmentHookTest Capturing SpanEnricher replaces the gateway listener
Shared files
  • dd-openfeature/build.gradle.kts: adds api("io.opentelemetry:opentelemetry-api:1.66.0"); OTel API becomes a hard transitive dependency (master had it compileOnly 1.47.0 and handled its absence)
  • dd-openfeature/gradle.lockfile: updated for the OTel dependency

Stack

The full change is in #12709. It was split into this stack to ease the review.

  1. Resolve Feature Flags enablement from instrumenter config #12845 (merged)
  2. Add dd-openfeature SDK flag configuration and evaluation core #12840 (merged)
  3. Add dd-openfeature SDK settings and configuration delivery #12841
  4. Add dd-openfeature SDK exposure and flag evaluation pipelines #12842
  5. Add dd-openfeature SDK OpenFeature provider #12843 👈 this PR
  6. Connect the dd-openfeature SDK to the agent #12844
  7. Add agentless and standalone OpenFeature smoke tests #12846

Jira ticket: APMLP-1512

🤖 Generated with Claude Code

@datadog-prod-us1-4

datadog-prod-us1-4 Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

🎯 Code Coverage (details)
• Patch Coverage: 100.00%
• Overall Coverage: 59.83% (+0.01%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 04c6c32 | Docs | Give us feedback!

@dd-octo-sts

dd-octo-sts Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

Suite Status
Startup 🟢 pass

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 14.02 s 13.90 s [+0.0%; +1.7%] (maybe worse)
startup:insecure-bank:tracing:Agent 12.92 s 12.88 s [-0.5%; +1.0%] (no difference)
startup:petclinic:appsec:Agent 17.08 s 16.98 s [-0.3%; +1.5%] (no difference)
startup:petclinic:iast:Agent 16.74 s 16.98 s [-2.0%; -0.8%] (maybe better)
startup:petclinic:profiling:Agent 16.89 s 16.74 s [+0.1%; +1.8%] (maybe worse)
startup:petclinic:sca:Agent 17.05 s 16.85 s [+0.3%; +2.0%] (maybe worse)
startup:petclinic:tracing:Agent 16.07 s 16.23 s [-1.9%; -0.1%] (maybe better)

Commit: 04c6c32e · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

@PerfectSlayer
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from afa5b21 to a5c7a57 Compare October 13, 2026 09:03
@PerfectSlayer
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from a5c7a57 to 7858ad8 Compare October 13, 2026 09:27
@PerfectSlayer
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from 7858ad8 to 63abca3 Compare October 13, 2026 10:07
@PerfectSlayer
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from 63abca3 to 0fb68c6 Compare October 13, 2026 10:28
@PerfectSlayer
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from 0fb68c6 to 3f9d8b8 Compare October 13, 2026 10:58
@PerfectSlayer
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from 3f9d8b8 to 27d4862 Compare October 13, 2026 13:50
@PerfectSlayer
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from 27d4862 to 107bf8b Compare October 14, 2026 05:33
@PerfectSlayer
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from 107bf8b to 6a0427d Compare October 14, 2026 06:02
PerfectSlayer and others added 4 commits October 16, 2026 18:02
The evaluator now takes the OpenFeature context and returns the
ProviderEvaluation, so the provider no longer adapts the context and
converts the result on every evaluation. Exposures are dispatched
through the evaluator ExposureListener.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep notifying the other configuration listeners when one of them fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port #12774 to the dd-openfeature provider. Exposures are sent from a
finally-stage hook once the evaluation succeeded, deduplicated by a
provider-side cache that replaces the exposure pipeline cache, and
applications can record them with ExposureHook. Split features are added
to the flag metadata. Require OpenFeature SDK 1.23.0 so provider hooks
run inside a MultiProvider.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sarahchen6
sarahchen6 marked this pull request as ready for review October 16, 2026 19:30
@sarahchen6
sarahchen6 requested review from a team as code owners October 16, 2026 19:30
@sarahchen6
sarahchen6 requested review from amarziali and removed request for a team October 16, 2026 19:30
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T19:39:58.722880Z 2d1dcc6 Draft marked ready
🔒 Security Review ✅ Completed 2026-10-16T19:35:43.618830Z 2d1dcc6 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2d1dcc6561

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@datadog-prod-us1-4 datadog-prod-us1-4 Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bits Code Review: FAIL

Shutting down the last provider can discard buffered exposures from successful evaluations. Initialization can also exceed its configured timeout and block concurrent shutdown while waiting for configuration.

Open Bits AI session

🤖 Bits Code Review · Commit 2d1dcc6

Comment thread dd-openfeature/src/main/java/com/datadog/openfeature/DDEvaluator.java Outdated
Comment thread dd-openfeature/src/main/java/com/datadog/openfeature/DDEvaluator.java Outdated
PerfectSlayer and others added 2 commits October 20, 2026 10:30
The type mapping moved to FlagEvaluator, which owns its own copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No OpenTelemetry type is exposed by the SDK public API, so declare it as
an implementation dependency rather than leaking it onto the consumers'
compile classpath. It remains a runtime dependency.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PerfectSlayer

Copy link
Copy Markdown
Collaborator Author

Pushed 2 other minor fixes and resolved thread where reports are not a regression from master.
Will proceed with merge. Thanks for the reviews!

@PerfectSlayer

Copy link
Copy Markdown
Collaborator Author

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-10-20 08:40:47 UTC ℹ️ Start processing command /merge


2026-10-20 08:40:53 UTC ℹ️ MergeQueue: Pull request is not mergeable yet

It will be processed automatically as soon as GitHub reports it as mergeable. View in MergeQueue UI.

  • Run /code blockers to see what is blocking it.
  • Run /remove to cancel it.

2026-10-20 09:27:17 UTC ℹ️ MergeQueue: merge request added to the queue

The expected merge time in master is approximately 1h (p90).


2026-10-20 10:31:20 UTC ℹ️ MergeQueue: This merge request was merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: openfeature OpenFeature tag: ai generated Largely based on code generated by an AI or LLM tag: no release notes Changes to exclude from release notes type: feature Enhancements and improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants