Enable PHP FFE exposure system tests#7031
Open
leoromanovsky wants to merge 1 commit into
Open
Conversation
🎉 All green!🧪 All tests passed 🔗 Commit SHA: c9fe071 | Docs | Datadog PR Page | Give us feedback! |
This was referenced May 28, 2026
a27a887 to
f54c036
Compare
Contributor
|
|
gh-worker-dd-mergequeue-cf854d Bot
pushed a commit
to DataDog/libdatadog
that referenced
this pull request
Jun 1, 2026
## Motivation PHP FFE exposure delivery needs a native path with a cache that persists beyond a single PHP request/thread. The shared design doc is the cross-PR reference: https://docs.google.com/document/d/1NvMfTpZWLBlFmEFNjdnlMyeVpy5l7KD8qujGFco6w2w/edit?tab=t.0 This PR is exposure-only. Metrics were split into #2052 so reviewers can evaluate exposure cache and delivery separately from OTLP evaluation metrics. ## Changes This adds caller-driven FFE exposure sidecar actions, exposure payload forwarding through the Agent EVP proxy, and a shared exposure cache that deduplicates repeated `(service, env, version, flag, subject)` assignments across PHP requests and sidecar connections. The reusable FFE-domain pieces now live in `datadog-ffe` behind the `exposure-events` feature: exposure input types, the LRU deduplication cache, and JSON payload encoding. `datadog-sidecar` keeps only sidecar-specific work: deriving the agent EVP endpoint, building the HTTP request, applying the timeout, logging delivery failures, and integrating with sidecar lifecycle/actions. Current PHP MVP path: ```mermaid flowchart LR Eval["PHP native evaluation<br/>ddog_ffe_evaluate"] Batch["PHP tracer native memory<br/>request/thread-local exposure batch"] Shutdown["PHP RSHUTDOWN<br/>flush exposure batch"] Action["sidecar action<br/>record FFE exposures"] Domain["datadog-ffe<br/>feature: exposure-events<br/>types + cache + JSON encoder"] Sidecar["shared sidecar<br/>cross-request and cross-thread exposure cache"] Agent["Datadog Agent<br/>EVP proxy"] Intake["FFE exposure intake"] Eval -->|"doLog=true assignment"| Batch Batch --> Shutdown Shutdown --> Action Action --> Domain Domain --> Sidecar Sidecar --> Agent Agent --> Intake ``` Future Python/Ruby connection: ```mermaid flowchart LR PyToday["dd-trace-py today<br/>host-language exposure writer"] RbToday["dd-trace-rb today<br/>host-language exposure writer"] PyFuture["dd-trace-py future<br/>explicit native opt-in"] RbFuture["dd-trace-rb future<br/>explicit native opt-in"] Native["libdatadog caller-driven<br/>FFE exposure action"] Shared["shared sidecar<br/>dedupe + EVP delivery"] Agent["Datadog Agent<br/>EVP proxy"] PyToday -. "current direct EVP path" .-> Agent RbToday -. "current direct EVP path" .-> Agent PyFuture -. "after ownership switch" .-> Native RbFuture -. "after ownership switch" .-> Native Native --> Shared Shared --> Agent ``` The future Python/Ruby arrows are intentionally not active behavior in this PR. They show why the reusable code lives in `datadog-ffe` rather than directly in sidecar internals, while preserving today's host-language ownership. Why Python/Ruby do not double count today: - Python and Ruby use libdatadog for evaluation only; the evaluator returns assignment metadata and does not enqueue exposure telemetry as a side effect. - This PR adds a separate caller-driven sidecar action. Exposure emission happens only when an SDK explicitly records exposure candidates into that action. PHP wires this in its companion PR; Python and Ruby do not. - Python and Ruby therefore keep exactly their current host-language EVP exposure writers. They are not also sending exposure candidates through this native sidecar path. - The sidecar cache only deduplicates exposure candidates that enter the native sidecar path. It cannot protect direct host-language EVP writers, so future Python/Ruby migration must switch ownership to native logging and disable/bypass the host exposure writer for the same evaluations. Reference implementation check: dd-trace-java follows the same exposure semantics and user ergonomics. Java's `DDEvaluator` is SDK-owned evaluation code; after resolving an assignment, it checks allocation `doLog`, builds an exposure event with flag, variant, allocation, targeting key, and context, and dispatches it through `FeatureFlaggingGateway`. `ExposureWriterImpl` subscribes to those exposure events, queues them, deduplicates with an LRU exposure cache, serializes service/env/version context, and posts to the Agent EVP proxy. Application code only calls the OpenFeature provider; it does not call an exposure API. PHP mirrors that canonical shape, with PHP-specific lifecycle mechanics: the dd-trace-php evaluation bridge records `doLog=true` exposure candidates internally, request shutdown flushes the batch, and this PR's sidecar path owns cross-request deduplication and EVP delivery. For future Python/Ruby migration, the same rule applies: wire native exposure recording inside the SDK-owned evaluation path, and turn off the existing host-language exposure writer for those evaluations. ## Decisions No telemetry is emitted automatically from shared libdatadog evaluator calls. SDKs must explicitly enqueue FFE telemetry actions. This remains required for Python/Ruby coexistence because those SDKs currently log exposures and metrics in host-language code. The sidecar cache deduplicates only exposure candidates sent through this native sidecar path; it cannot deduplicate direct host-language EVP writers. Future Python/Ruby migration must be an ownership switch, not an additional writer. When those SDKs opt into this native exposure path, their host-language exposure writers must be disabled or bypassed for the same evaluations to avoid double counting. ## Validation Current head (`8be471fbc`) local validation: ```sh cd /Users/leo.romanovsky/go/src/github.com/DataDog/libdatadog-ffe-sidecar-exposures cargo fmt --check cargo test -p datadog-ffe --features exposure-events telemetry::exposures cargo test -p datadog-sidecar ffe_exposure cargo check -p datadog-ffe cargo check -p datadog-sidecar-ffi ``` Results: datadog-ffe exposure tests passed (4 passed), sidecar exposure tests passed (6 passed), default datadog-ffe check passed, sidecar FFI check passed, fmt check passed with only the repo stable-rustfmt warnings. Prior downstream PHP behavior validation before the reusable-crate refactor, from DataDog/dd-trace-php#3910 using this PR at `6d23848a`: ```text ffe-dogfooding subject=php-3910-split-1779981442 php7_exposures=1 php8_exposures=1 php7_metrics=0 php8_metrics=0 ``` System-tests downstream validation: ```sh TEST_LIBRARY=php ./run.sh FEATURE_FLAGGING_AND_EXPERIMENTATION tests/ffe/test_exposures.py -vv ``` Result: 11 passed in 77.53 seconds. Related PRs: DataDog/dd-trace-php#3906, DataDog/dd-trace-php#3910, #2052, DataDog/system-tests#7031. Co-authored-by: leo.romanovsky <leo.romanovsky@datadoghq.com>
f54c036 to
c9fe071
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
PHP FFE exposure logging now lives in the native dd-trace-php/runtime path and needs the shared FFE exposure system tests enabled so regressions are caught outside local dogfooding.
Planning/reference doc: https://docs.google.com/document/d/1NvMfTpZWLBlFmEFNjdnlMyeVpy5l7KD8qujGFco6w2w/edit?tab=t.0
Changes
This PR enables
tests/ffe/test_exposures.pyfor PHP atv1.21.0-dev.#7003 has already merged to
main, so this PR is now intentionally just one manifest activation on top ofmain. It does not enable FFE evaluation-metric tests; those belong to the separate metrics validation stack for DataDog/dd-trace-php#3911 and #7033.Decisions
The scope is deliberately one manifest activation. The shared exposure tests already cover the required product behavior: event generation, multiple RC files, malformed/empty RC handling, same-subject deduplication, different-subject emission, allocation/variant changes,
doLog=false, missing flags, and empty targeting keys.The PHP behavior being validated here is implemented in DataDog/dd-trace-php#3910 and the sidecar delivery/cache support is implemented in DataDog/libdatadog#2026.
Related PRs
Validation
Current branch state after rebase onto
main:Current PR diff is one file, enabling only
tests/ffe/test_exposures.pyfor PHP atv1.21.0-dev.Earlier local behavior validation for this activation used a matched PHP 8.2 NTS artifact and the
php-fpm-8.2weblog:Result:
11 passed in 78.22s.