Add FFE exposure emission#3910
Conversation
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 92ef9a3 | Docs | Datadog PR Page | Give us feedback! |
Benchmarks [ tracer ]Benchmark execution time: 2026-05-23 03:40:57 Comparing candidate commit 92ef9a3 in PR branch Found 0 performance improvements and 5 performance regressions! Performance is the same for 188 metrics, 1 unstable metrics. scenario:MessagePackSerializationBench/benchMessagePackSerialization
scenario:MessagePackSerializationBench/benchMessagePackSerialization-opcache
scenario:SamplingRuleMatchingBench/benchRegexMatching2
scenario:SamplingRuleMatchingBench/benchRegexMatching4
scenario:TraceSerializationBench/benchSerializeTrace
|
The canonical fixture PHPT explicitly enumerates the FFE classes it requires before instantiating the Datadog FeatureFlags client. The shared evaluation-completed envelope/hook added on this branch made Client::createWithDependencies() reference NoopEvaluationCompletedHook, EvaluationCompletedHook, and EvaluationCompleted, but the test helper had not been updated, so the packaged/extension PHPT failed with "Class DDTrace\FeatureFlags\Internal\NoopEvaluationCompletedHook not found" before any fixture case ran. Add the three new files to require_feature_flag_api so the PHPT matches the runtime class graph used by Client::evaluate().
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 92ef9a34dc
ℹ️ 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".
| if (count($this->buffer) >= $this->bufferLimit) { | ||
| $this->dropped++; | ||
| return false; |
There was a problem hiding this comment.
Flush full exposure buffers instead of dropping
In long-running PHP runtimes such as Swoole/RoadRunner/CLI workers, the default hook only registers a shutdown flush, so this buffer can stay resident across many requests. Once 1,000 unique exposures accumulate, every later unique exposure is dropped here until the process exits, and the buffered events may not reach the Agent for hours or days; flushing when the buffer reaches the limit or from a request/periodic lifecycle hook would preserve exposure delivery for these runtimes.
Useful? React with 👍 / 👎.
Adds Mermaid sources and rendered PNGs for the hook (this) PR plus a README documenting the regeneration workflow. - `docs/php-ffe-stack/stack-pr3909.mmd` + `.png` — 4-PR stack with this PR highlighted (M1 done; EVP and metrics as siblings to come). - `docs/php-ffe-stack/system-pr3909.mmd` + `.png` — target system architecture; this PR contributes the EvaluationCompletedHook + OpenFeature provider hook surface. All downstream nodes (writers, sidecar FFI, sidecar process, backends) marked future. - `docs/php-ffe-stack/README.md` — npx invocation for regenerating PNGs locally; PR-by-PR diagram table; architectural rule note. The architectural rule encoded in the system diagram (all I/O via the libdatadog sidecar) is the same rule Bob applied to PR #3910. See DataDog/libdatadog#2026 for the sidecar-side support.
Motivation
Milestone 2 needs PHP feature flag evaluations to emit server-side EVP exposures when the real evaluator marks an evaluation with
doLog=true. This PR also lands the M2/M3 shared evaluation-completed seam (internal envelope and hook interface invoked byClient::evaluate()) so PHP 7 Datadog API and PHP 8 OpenFeature bridge share one path.(Note: the shared seam was previously split out as a separate base PR #3909, which has been folded into this PR for review.)
Shared planning doc: https://docs.google.com/document/d/1NvMfTpZWLBlFmEFNjdnlMyeVpy5l7KD8qujGFco6w2w/edit?tab=t.0
Changes
Shared base (formerly #3909):
EvaluationCompletedenvelope andEvaluationCompletedHookinterface invoked byClient::evaluate(). Hook failures never alter returned values.NoopEvaluationCompletedHook,CompositeEvaluationCompletedHook, andDefaultEvaluationCompletedHook::create()factory.Client::createWithDependencies()to accept an optional hook (defaults to noop). ProductionClient::create()uses the default composite (exposure here, metric added in PR Add FFE evaluation metrics #3911).M2 exposure emission:
/evp_proxy/v2/api/v2/exposuresusing theevent-platform-intakeEVP subdomain.Client::create()and the default OpenFeature provider to use the exposure hook throughDefaultEvaluationCompletedHook.doLog, payload shape, empty targeting keys, dedup/cache behavior, buffer overflow/drop behavior, transport failures, and Agent EVP request shape.Decisions
subject.id="", matching the M1 empty targeting key handling.(flag key, subject id)and cache(allocation key, variant)with default capacity65536.Validation:
php -l src/api/FeatureFlags/Internal/EvaluationCompleted.php && php -l src/api/FeatureFlags/Internal/EvaluationCompletedHook.php && php -l src/api/FeatureFlags/Internal/NoopEvaluationCompletedHook.phpphp -l src/api/FeatureFlags/Internal/Exposure/ExposureTransport.php && php -l src/api/FeatureFlags/Internal/Exposure/AgentExposureTransport.php && php -l src/api/FeatureFlags/Internal/Exposure/ExposureWriter.php && php -l src/api/FeatureFlags/Internal/Exposure/ExposureHook.phpphp -l src/api/FeatureFlags/Client.php && php -l src/DDTrace/OpenFeature/DataDogProvider.php && php -l tests/api/Unit/FeatureFlags/ExposureWriterTest.phpphp vendor/bin/phpunit --config phpunit.xml tests/api/Unit/FeatureFlagsphp vendor/bin/phpunit --config phpunit.xml tests/OpenFeature/DataDogProviderTest.phpmake test_featureflagsgit diff --checkffe-dogfooding-string-flag(experiment allocation,doLog=true):EVP exposures OK: php7=4 php8-openfeature=4andOTLP metrics OK: php7=4 php8-openfeature=4viaFLAG_KEY=ffe-dogfooding-string-flag EXPECT_EVP=1 scripts/validate-local-php-telemetry.sh.