Skip to content

Add dd-openfeature SDK OpenFeature provider - #12843

Draft
PerfectSlayer wants to merge 3 commits into
bbujon/ffe-sdk-eventsfrom
bbujon/ffe-sdk-provider
Draft

PerfectSlayer wants to merge 3 commits into
bbujon/ffe-sdk-eventsfrom
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. 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 (connector 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 connector, 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 Connectors.detect() and Settings.load(connector), builds DDEvaluator directly (reflective loadEvaluatorClass() / EVALUATOR_IMPL removed). Span enrichment gate read from Settings (SpanEnrichmentGate removed); SpanEnrichmentHook gets connector.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, Connector, 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 connector 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 connectors (ProxyConnector, 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
  2. Add dd-openfeature SDK flag configuration and evaluation core #12840
  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

This comment has been minimized.

@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 13.97 s 13.85 s [+0.2%; +1.5%] (maybe worse)
startup:insecure-bank:tracing:Agent 12.86 s 12.90 s [-1.0%; +0.4%] (no difference)
startup:petclinic:appsec:Agent 17.17 s 17.23 s [-1.2%; +0.5%] (no difference)
startup:petclinic:iast:Agent 16.92 s 17.13 s [-2.0%; -0.5%] (maybe better)
startup:petclinic:profiling:Agent 16.55 s 16.76 s [-2.4%; -0.1%] (maybe better)
startup:petclinic:sca:Agent 17.16 s 17.15 s [-0.7%; +0.8%] (no difference)
startup:petclinic:tracing:Agent 16.27 s 16.15 s [-0.5%; +1.9%] (no difference)

Commit: d3753d09 · 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
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from 6a0427d to 1bd038c Compare October 14, 2026 06:15
@PerfectSlayer
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from 1bd038c to 3272ddb Compare October 14, 2026 07:10
@PerfectSlayer
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from 3272ddb to a39c1f6 Compare October 14, 2026 08:06
@PerfectSlayer
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from a39c1f6 to 613f720 Compare October 14, 2026 08:38
PerfectSlayer and others added 3 commits October 14, 2026 10:40
Add the com.datadog.openfeature.Provider OpenFeature provider, its
evaluator and hooks (metrics, logging, span enrichment), and the runtime
wiring the configuration delivery and event pipelines together. The SDK
is still not published, the agent-coupled provider remains the shipped
one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The evaluator now builds OpenFeature ImmutableMetadata directly, so the
provider no longer copies it on every evaluation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
@PerfectSlayer
PerfectSlayer force-pushed the bbujon/ffe-sdk-provider branch from 613f720 to d3753d0 Compare October 14, 2026 08:42

This branch has not been deployed

No deployments
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.

1 participant