Skip to content

Prepare evaluator-gated dependent flag capabilities - #13167

Draft
hhan2024 wants to merge 1 commit into
masterfrom
helen/dependent-flags-capabilities-java
Draft

hhan2024 wants to merge 1 commit into
masterfrom
helen/dependent-flags-capabilities-java

Conversation

@hhan2024

@hhan2024 hhan2024 commented Nov 2, 2026

Copy link
Copy Markdown

What Does This Do

Prepare agentless capability headers and Remote Config bit 50 for dependent-flag-variant-key-v1, gated on the SDK evaluator's report. The report remains empty, preserving current delivery until prerequisite evaluation, error propagation, and exposure reporting are implemented and integration-tested.

Motivation

Follows Python agentless #21335 and Python RC #21336. The separately shipped Java SDK must declare its support to the agent; the agent's protocol constants cannot establish evaluator support.

Additional Notes

The bridge adds a default overload so older agents remain conservative. RC opt-in requires every live subscriber to report support; subscription changes remove stale replay state and request configuration again, and capability updates avoid holding the subscriber lock while acquiring the poller lock.

Added empty-report, unknown-capability, mixed-subscriber, unsubscribe, and current initial/conditional-header coverage. Formatting with pinned google-java-format 1.36.1, limited Java 11 compilation of the new reporter/SDK bridge/protocol constants, and diff checks pass; full Gradle tests/Spotless failed during setup due to exhausted workspace storage. Keep draft pending full SDK, agent-bridge/muzzle, lifecycle/concurrency, and transport validation.

Contributor Checklist

  • Focused SDK and agent tests pass.
  • Bridge compatibility/muzzle checks pass.
  • New files fall under existing feature flagging CODEOWNERS patterns.
  • CI validation is complete.

Gate configuration negotiation on evaluation and exposure support; the current evaluator remains opted out.

Environment: Datadog workspace

Co-Authored-By: Codex GPT-6 <noreply@localhost>
@hhan2024 hhan2024 added tag: ai generated Largely based on code generated by an AI or LLM comp: openfeature OpenFeature type: feature Enhancements and improvements tag: no release notes Changes to exclude from release notes labels Nov 2, 2026 — with ChatGPT Codex Connector
@datadog-datadog-us1-prod

datadog-datadog-us1-prod Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

🎯 Code Coverage (details)
• Patch Coverage: 91.67%
• Overall Coverage: 61.32% (+1.63%)

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

@dd-octo-sts

dd-octo-sts Bot commented Nov 2, 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.83 s 14.69 s [+0.2%; +1.7%] (maybe worse)
startup:insecure-bank:tracing:Agent 13.51 s 13.70 s [-2.2%; -0.6%] (maybe better)
startup:petclinic:appsec:Agent 17.23 s 17.10 s [-0.1%; +1.6%] (no difference)
startup:petclinic:iast:Agent 17.06 s 17.11 s [-1.0%; +0.4%] (no difference)
startup:petclinic:profiling:Agent 16.54 s 16.81 s [-2.7%; -0.7%] (maybe better)
startup:petclinic:sca:Agent 17.19 s 17.10 s [-0.2%; +1.2%] (no difference)
startup:petclinic:tracing:Agent 15.78 s 16.24 s [-7.0%; +1.3%] (no difference)

Commit: 74036dca · 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.

@aarsilv aarsilv added the crucible: reviewing A Crucible review has started; this PR has reviewer attention. label Nov 4, 2026
if (supported == this.dependentFlagsAdvertised) {
return;
}
this.dependentFlagsAdvertised = supported;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 Comment from Claude working with Aaron Silverman:

Heads up: This becomes a bug when EvaluatorCapabilities.supported() is turned on. Please fix it before that change lands.

When the advertised state changes, updateDependentFlagCapability clears current and then only adds or removes the capability bit. It does not request the configuration again, although the PR description says that subscription changes "request configuration again".

Scenario, assuming the Remote Config payload does not change when bit 50 is added (for example, an org without dependent flags), so the poller does not call accept again:

  1. The first supported subscriber registers, receives the cached document, and the flip to "advertised" clears current.
  2. Later, that subscription closes (for example, the last provider shuts down and the runtime releases its subscription). The flip back clears current again.
  3. A new runtime subscribes. It gets no replay, so its provider waits for a document until its initialization timeout. An unsupported subscriber that joins while the bit is advertised waits in the same way.

Fix: after the capability bit changes, call requestAgain() outside the subscribers lock, so the poller registers the product again and the next poll delivers the document with the new bits. Add a test for the sequence above that asserts the listener is registered again (or simulates the next poll and its accept) before asserting that the new subscriber receives the document.

I found this by reading the code; I did not run it.


private boolean started;
private final Map<Consumer<byte[]>, Boolean> dependentFlagSubscribers = new IdentityHashMap<>();
private boolean dependentFlagsAdvertised;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 Comment from Claude working with Aaron Silverman:

[P2] Possible causes of the red spotless and test_base jobs (from reading the code; I did not run Gradle or read the job logs):

  • spotless: the @TableTest rows in CdnConfigurationSourceTest were split into "…" + "…". The repository's Spotless chain runs googleJavaFormat and then tableTestFormatter, which formats those tables itself. Running ./gradlew spotlessApply and committing the result should settle it.
  • test_base: this job runs with -PcheckCoverage. The new default method on FeatureFlaggingGateway.Backend and the two-argument FeatureFlagsBackend.subscribeRemoteConfig have no tests. A test that calls each and checks that it delegates would cover them.


/** Keep empty until prerequisite evaluation, errors, and exposures are integration-tested. */
public static Set<String> supported() {
return emptySet();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 Comment from Claude working with Aaron Silverman:

[P1] Coordination note with #13166: nothing turns this report on, and #13166 sends the header by itself.

  • This method returns emptySet(), so this PR sends neither the header nor bit 50.
  • feat: Dependent Flags #13166 implements the evaluator, but it adds the header unconditionally in CdnConfigurationSource and never sets bit 50.
  • A three-way merge of the two PRs' versions of CdnConfigurationSource.java and its test has no textual conflict, but the merged test asserts both that the header is present and that it is absent.

Suggested order: land this PR first. Then #13166 deletes its own header and constants, and this method returns singleton(DEPENDENT_FLAG_VARIANT_KEY_V1) (with import static java.util.Collections.singleton;), with EvaluatorCapabilitiesTest and the header assertions updated. Before that change, RemoteConfigServiceImpl must request the configuration again when the capability bit changes. This is a sketch; I did not build it.

if (EvaluatorCapabilities.supported()
.contains(EvaluatorCapabilities.DEPENDENT_FLAG_VARIANT_KEY_V1)) {
request.header(
"X-DD-FEATURE-FLAGS-CAPABILITIES", EvaluatorCapabilities.DEPENDENT_FLAG_VARIANT_KEY_V1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 Comment from Claude working with Aaron Silverman:

Heads up: when the evaluator report is turned on, this gate sends X-DD-FEATURE-FLAGS-CAPABILITIES on direct UFC requests (the report is empty today). The corresponding capability-gated code in DataDog/dd-trace-go#5800 and DataDog/dd-trace-js#11038 now uses the query parameter cap_dependent-flags-variant-key=v1 on direct UFC requests instead of this header (their capability reports are currently empty; DataDog/dd-trace-js#11083 states the reason: keep the capabilities header for client-to-edge negotiation, and negotiate the direct UFC response shape through the cache-keyed URL). If that is the agreed protocol, please switch this path to the same query parameter, so all SDKs negotiate the same way.

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 crucible: reviewing A Crucible review has started; this PR has reviewer attention. 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.

2 participants