-
Notifications
You must be signed in to change notification settings - Fork 167
port ffe feature flagging sdk to php #3630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
18f7f00 to
508a8c8
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3630 +/- ##
==========================================
- Coverage 62.21% 62.11% -0.11%
==========================================
Files 141 141
Lines 13387 13387
Branches 1753 1753
==========================================
- Hits 8329 8315 -14
- Misses 4260 4273 +13
- Partials 798 799 +1 see 3 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|
ff84598 to
9f36eb7
Compare
7c43198 to
7bbed08
Compare
Benchmarks [ tracer ]Benchmark execution time: 2026-02-10 03:08:01 Comparing candidate commit a24b1ae in PR branch Found 3 performance improvements and 40 performance regressions! Performance is the same for 150 metrics, 1 unstable metrics. scenario:ComposerTelemetryBench/benchTelemetryParsing
scenario:ContextPropagationBench/benchExtractHeaders128Bit
scenario:ContextPropagationBench/benchExtractHeaders128Bit-opcache
scenario:ContextPropagationBench/benchExtractHeaders64Bit
scenario:ContextPropagationBench/benchExtractTraceContext128Bit
scenario:ContextPropagationBench/benchExtractTraceContext128Bit-opcache
scenario:ContextPropagationBench/benchExtractTraceContext64Bit
scenario:ContextPropagationBench/benchInject128Bit
scenario:ContextPropagationBench/benchInject64Bit
scenario:EmptyFileBench/benchEmptyFileBaseline
scenario:EmptyFileBench/benchEmptyFileDdprof
scenario:EmptyFileBench/benchEmptyFileOverhead
scenario:HookBench/benchHookOverheadInstallHookOnFunction
scenario:HookBench/benchHookOverheadInstallHookOnMethod
scenario:HookBench/benchHookOverheadTraceFunction
scenario:HookBench/benchHookOverheadTraceMethod
scenario:HookBench/benchWithoutHook
scenario:LaravelBench/benchLaravelBaseline
scenario:LaravelBench/benchLaravelDdprof
scenario:LaravelBench/benchLaravelOverhead
scenario:MessagePackSerializationBench/benchMessagePackSerialization
scenario:PDOBench/benchPDOBaseline
scenario:PHPRedisBench/benchRedisBaseline
scenario:SamplingRuleMatchingBench/benchGlobMatching1
scenario:SamplingRuleMatchingBench/benchGlobMatching2
scenario:SamplingRuleMatchingBench/benchGlobMatching3
scenario:SamplingRuleMatchingBench/benchGlobMatching4
scenario:SamplingRuleMatchingBench/benchRegexMatching1
scenario:SamplingRuleMatchingBench/benchRegexMatching2
scenario:SamplingRuleMatchingBench/benchRegexMatching3
scenario:SamplingRuleMatchingBench/benchRegexMatching4
scenario:SpanBench/benchDatadogAPI
scenario:SymfonyBench/benchSymfonyBaseline
scenario:SymfonyBench/benchSymfonyDdprof
scenario:SymfonyBench/benchSymfonyOverhead
scenario:TraceAnnotationsBench/benchTraceAnnotationOverhead
scenario:TraceFlushBench/benchFlushTrace
scenario:TraceSerializationBench/benchSerializeTrace
|
## Motivation Add Feature Flagging and Experimentation (FFE) support to the remote config infrastructure, enabling tracers to subscribe to FFE_FLAGS configurations via the sidecar. WIP: php tracer changes (DataDog/dd-trace-php#3630) ## Changes - Add `FfeFlags` variant to `RemoteConfigProduct` enum - Add `"FFE_FLAGS"` string mapping in Display and FromStr - Add `FfeFlagConfigurationRules = 46` to `RemoteConfigCapabilities` - Add `FfeFlags(Vec<u8>)` variant to `RemoteConfigData` to preserve raw config bytes ## Decisions - Raw bytes are preserved (not parsed) in `FfeFlags(Vec<u8>)` since each tracer handles evaluation with the `datadog-ffe` crate directly - Capability bit 46 matches the server-side FFE capability definition Co-authored-by: leo.romanovsky <leo.romanovsky@datadoghq.com>
Implement UFCv1 evaluation engine, exposure event reporting, and Remote Config integration for the FFE product. - Add DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED config - Add FFE RC product subscription and config delivery via sidecar - Add PHP evaluator with full condition/sharding support (217/217 tests pass) - Add exposure event writer with LRU deduplication cache - Add Provider API with singleton pattern - Add datadog-ffe native crate dependency and C FFI bindings - Wire get_ffe_config/ffe_config_changed internal functions
- Fix EvaluationContext construction (no Deserialize/Default) - Fix AssignmentValue::Json struct pattern - Add parse_evaluation_context helper - Resolve workspace inheritance in datadog-ffe Cargo.toml - Remove unused imports
Remove stale git references to libdatadog v25.0.0 and use local submodule path dependencies for datadog-ffe and datadog-ffe-ffi.
Add DDTrace\OpenFeature\DataDogProvider that implements the OpenFeature PHP SDK's AbstractProvider interface, wrapping the internal FFE evaluation engine.
Base FFE changes on the original pinned commit (534d009c) instead of latest main, to avoid pulling in unrelated debugger/telemetry changes that break existing tests.
Use master Cargo.lock as base and only add new workspace crates (datadog-ffe and dependencies) to avoid bumping existing crates to versions requiring edition 2024 (rmp, rmpv, time).
…DER_ENABLED The INI config defaults to false and shadows the env var in some SAPIs. Check getenv() first for reliability across all SAPIs.
Replace the pure PHP UFC evaluator with the native datadog-ffe engine from libdatadog. The evaluation path is now: RC config bytes → ddog_ffe_load_config() → native Configuration evaluate() → ddog_ffe_evaluate() → native get_assignment() - Remove Evaluator.php (482 lines of PHP reimplementation) - Remove EvaluatorTest.php - Add ffe_load_config, ffe_has_config, ffe_evaluate internal functions - Update Provider to call native engine via dd_trace_internal_fn - Add C header declarations for all ddog_ffe_* functions
1990188 to
867337e
Compare
Add ddog_ffe_load_config() FFI function to load UFC JSON config directly into the Rust FFE engine without Remote Config, enabling test-time config injection. Add 220 parametric evaluation tests driven by shared cross-tracer JSON fixtures (merged from dd-trace-py and dd-trace-java configs) and 8 LRU cache unit tests covering eviction, promotion, and edge cases.
- Fix exposure dedup cache to match Java canonical impl: create ExposureCache class with length-prefixed composite keys (no collision), add() returns bool like Java's LRUExposureCache.add(), always promotes LRU position even for duplicates - Add 12 ExposureCache tests matching Java's LRUExposureCacheTest - Add LRUCache.put() (returns old value) and size() methods with tests - Fix Provider to handle config removal via ffe_config_changed(), clear exposure cache when RC removes config - Auto-flush exposures on request shutdown via register_shutdown_function - Reduce ExposureWriter curl timeout to 500ms/100ms (was 5s/2s) - Combine FFE_CONFIG + FFE_CONFIG_CHANGED into single FfeState struct behind one Mutex for atomic updates, add RwLock justification comment - Remove unused datadog-ffe-ffi dependency from Cargo.toml - Change LRUCache eviction from while to if (only one entry added) - Clarify continue-in-switch comment in ddtrace.c ffe_evaluate handler
Motivation
PHP is the only major Datadog tracer without Feature Flagging and Experimentation (FFE) support. This PR adds it, matching the behavior of Go, Java, and Python. PHP applications can now evaluate feature flags delivered by the Datadog platform using the UFC v1 protocol.
📖 RFC - Feature Flags & Experiments in APM
Feature Flag Implementations Across dd-trace- Libraries*
Go (dd-trace-go)
Java (dd-trace-java)
Python (dd-trace-py)
Node.js (dd-trace-js)
Ruby (dd-trace-rb)
.NET (dd-trace-dotnet)
Changes
datadog-ffenative evaluation crate (ready for future use)DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLEDas a new tracer configuration variableFFE_FLAGSRC product and capabilitySystem diagram
flowchart TB Agent["Datadog Agent\n(Remote Config endpoint)"] Sidecar["Sidecar\n(polls agent, shared memory)"] Rust["Rust: ddog_process_remote_configs()\nRemoteConfigData::FfeFlags(bytes)\nstored in FFE_CONFIG"] Provider["PHP: Provider.php"] Evaluator["Evaluator\n(UFC v1 engine)"] Cache["LRU Cache\n(65K dedup)"] Writer["Exposure Writer"] EVP["Agent EVP Proxy\n/evp_proxy/v2/api/v2/exposures"] App["PHP Application\nevaluate(flag, type, default, targetingKey, attrs)"] Agent -- "FFE_FLAGS configs" --> Sidecar Sidecar -- "SIGVTALRM → VM interrupt" --> Rust Rust -- "dd_trace_internal_fn('get_ffe_config')" --> Provider App --> Provider Provider --> Evaluator Provider --> Cache Provider --> Writer Evaluator -- "flag value + variant" --> Provider Cache -- "skip duplicates" --> Writer Writer -- "POST exposures" --> EVPCompanion PRs
Decisions
datadog-ffeRust crate is linked with C FFI bindings ready, and can replace the PHP evaluator later for performance.Vec<u8>for RC data: The RC layer stores raw JSON bytes rather than parsing UFC types, keepingdatadog-remote-configdecoupled fromdatadog-ffe.MD5(salt + "-" + targetingKey), first 8 hex chars as big-endian uint32, modulo totalShards."id"only if not in attributes, matching Go behavior.