fix(ffe): report rejected flags as parse errors - #2339
Conversation
📚 Documentation Check Results📦
|
🔒 Cargo Deny Results📦
|
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: d20319f | Docs | Datadog PR Page | Give us feedback! |
BenchmarksComparisonBenchmark execution time: 2026-08-11 01:48:08 Comparing candidate commit d20319f in PR branch Found 0 performance improvements and 1 performance regressions! Performance is the same for 13 metrics, 0 unstable metrics.
|
| cpu_model | git_commit_sha | git_commit_date | git_branch |
|---|---|---|---|
| Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz | d20319f | 1786412181 | agent/ffe-rejected-flag-errors |
| scenario | metric | min | mean ± sd | median ± mad | p75 | p95 | p99 | max | peak_to_median_ratio | skewness | kurtosis | cv | sem | runs | sample_size |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| flagevaluation_evp/coalescer/scale/2500flags_500users_20fields | execution_time | 5.415ms | 5.431ms ± 0.007ms | 5.431ms ± 0.004ms | 5.435ms | 5.443ms | 5.448ms | 5.460ms | 0.53% | 0.609 | 1.092 | 0.13% | 0.000ms | 1 | 200 |
| flagevaluation_evp/coalescer/scale/2500flags_500users_20fields | throughput | 457912.066op/s | 460326.498op/s ± 579.493op/s | 460332.154op/s ± 380.188op/s | 460740.992op/s | 461147.716op/s | 461529.317op/s | 461651.637op/s | 0.29% | -0.598 | 1.065 | 0.13% | 40.976op/s | 1 | 200 |
| flagevaluation_evp/coalescer/stress/10flags_1000users_250fields | execution_time | 7.926ms | 7.941ms ± 0.008ms | 7.940ms ± 0.003ms | 7.943ms | 7.951ms | 7.974ms | 7.988ms | 0.60% | 2.662 | 12.098 | 0.10% | 0.001ms | 1 | 200 |
| flagevaluation_evp/coalescer/stress/10flags_1000users_250fields | throughput | 125193.278op/s | 125930.889op/s ± 120.718op/s | 125945.351op/s ± 53.974op/s | 125998.816op/s | 126071.336op/s | 126107.091op/s | 126173.996op/s | 0.18% | -2.642 | 11.962 | 0.10% | 8.536op/s | 1 | 200 |
| flagevaluation_evp/coalescer/typical/100flags_50users_10fields | execution_time | 180.407µs | 185.984µs ± 39.581µs | 183.049µs ± 0.150µs | 183.296µs | 184.405µs | 185.064µs | 742.894µs | 305.84% | 14.032 | 194.925 | 21.23% | 2.799µs | 1 | 200 |
| flagevaluation_evp/coalescer/typical/100flags_50users_10fields | throughput | 134608.706op/s | 543843.324op/s ± 29130.940op/s | 546300.783op/s ± 447.661op/s | 546694.519op/s | 547127.890op/s | 550307.959op/s | 554303.369op/s | 1.46% | -13.966 | 193.696 | 5.34% | 2059.869op/s | 1 | 200 |
| scenario | metric | 95% CI mean | Shapiro-Wilk pvalue | Ljung-Box pvalue (lag=1) | Dip test pvalue |
|---|---|---|---|---|---|
| flagevaluation_evp/coalescer/scale/2500flags_500users_20fields | execution_time | [5.430ms; 5.432ms] or [-0.017%; +0.017%] | None | None | None |
| flagevaluation_evp/coalescer/scale/2500flags_500users_20fields | throughput | [460246.186op/s; 460406.811op/s] or [-0.017%; +0.017%] | None | None | None |
| flagevaluation_evp/coalescer/stress/10flags_1000users_250fields | execution_time | [7.940ms; 7.942ms] or [-0.013%; +0.013%] | None | None | None |
| flagevaluation_evp/coalescer/stress/10flags_1000users_250fields | throughput | [125914.158op/s; 125947.619op/s] or [-0.013%; +0.013%] | None | None | None |
| flagevaluation_evp/coalescer/typical/100flags_50users_10fields | execution_time | [180.498µs; 191.470µs] or [-2.949%; +2.949%] | None | None | None |
| flagevaluation_evp/coalescer/typical/100flags_50users_10fields | throughput | [539806.056op/s; 547880.592op/s] or [-0.742%; +0.742%] | None | None | None |
Group 2
| cpu_model | git_commit_sha | git_commit_date | git_branch |
|---|---|---|---|
| Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz | d20319f | 1786412181 | agent/ffe-rejected-flag-errors |
| scenario | metric | min | mean ± sd | median ± mad | p75 | p95 | p99 | max | peak_to_median_ratio | skewness | kurtosis | cv | sem | runs | sample_size |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| flagevaluation_evp/payloads/scale/2500flags_500users_20fields | execution_time | 21.492ms | 21.622ms ± 0.057ms | 21.616ms ± 0.037ms | 21.657ms | 21.729ms | 21.758ms | 21.814ms | 0.92% | 0.471 | 0.107 | 0.26% | 0.004ms | 1 | 200 |
| flagevaluation_evp/payloads/scale/2500flags_500users_20fields | throughput | 114603.539op/s | 115623.289op/s ± 304.536op/s | 115657.708op/s ± 196.142op/s | 115832.255op/s | 116057.465op/s | 116199.579op/s | 116322.645op/s | 0.57% | -0.456 | 0.086 | 0.26% | 21.534op/s | 1 | 200 |
| flagevaluation_evp/payloads/stress/10flags_1000users_250fields | execution_time | 97.117ms | 97.440ms ± 0.291ms | 97.397ms ± 0.099ms | 97.525ms | 97.702ms | 97.743ms | 100.895ms | 3.59% | 8.465 | 97.969 | 0.30% | 0.021ms | 1 | 200 |
| flagevaluation_evp/payloads/stress/10flags_1000users_250fields | throughput | 9911.342op/s | 10262.820op/s ± 29.872op/s | 10267.218op/s ± 10.412op/s | 10275.659op/s | 10289.115op/s | 10292.041op/s | 10296.885op/s | 0.29% | -8.214 | 94.004 | 0.29% | 2.112op/s | 1 | 200 |
| flagevaluation_evp/payloads/typical/100flags_50users_10fields | execution_time | 589.932µs | 591.535µs ± 0.595µs | 591.577µs ± 0.443µs | 591.863µs | 592.517µs | 592.839µs | 593.494µs | 0.32% | 0.154 | -0.051 | 0.10% | 0.042µs | 1 | 200 |
| flagevaluation_evp/payloads/typical/100flags_50users_10fields | throughput | 168493.720op/s | 169051.845op/s ± 169.985op/s | 169039.638op/s ± 126.559op/s | 169186.900op/s | 169300.131op/s | 169423.322op/s | 169510.946op/s | 0.28% | -0.148 | -0.055 | 0.10% | 12.020op/s | 1 | 200 |
| scenario | metric | 95% CI mean | Shapiro-Wilk pvalue | Ljung-Box pvalue (lag=1) | Dip test pvalue |
|---|---|---|---|---|---|
| flagevaluation_evp/payloads/scale/2500flags_500users_20fields | execution_time | [21.614ms; 21.630ms] or [-0.037%; +0.037%] | None | None | None |
| flagevaluation_evp/payloads/scale/2500flags_500users_20fields | throughput | [115581.084op/s; 115665.495op/s] or [-0.037%; +0.037%] | None | None | None |
| flagevaluation_evp/payloads/stress/10flags_1000users_250fields | execution_time | [97.400ms; 97.480ms] or [-0.041%; +0.041%] | None | None | None |
| flagevaluation_evp/payloads/stress/10flags_1000users_250fields | throughput | [10258.680op/s; 10266.960op/s] or [-0.040%; +0.040%] | None | None | None |
| flagevaluation_evp/payloads/typical/100flags_50users_10fields | execution_time | [591.453µs; 591.618µs] or [-0.014%; +0.014%] | None | None | None |
| flagevaluation_evp/payloads/typical/100flags_50users_10fields | throughput | [169028.287op/s; 169075.404op/s] or [-0.014%; +0.014%] | None | None | None |
Group 3
| cpu_model | git_commit_sha | git_commit_date | git_branch |
|---|---|---|---|
| Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz | d20319f | 1786412181 | agent/ffe-rejected-flag-errors |
| scenario | metric | min | mean ± sd | median ± mad | p75 | p95 | p99 | max | peak_to_median_ratio | skewness | kurtosis | cv | sem | runs | sample_size |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| sdk_test_data/rules-based | execution_time | 141.948µs | 145.414µs ± 5.875µs | 144.225µs ± 0.743µs | 145.071µs | 150.727µs | 168.141µs | 207.899µs | 44.15% | 7.479 | 68.451 | 4.03% | 0.415µs | 1 | 200 |
| scenario | metric | 95% CI mean | Shapiro-Wilk pvalue | Ljung-Box pvalue (lag=1) | Dip test pvalue |
|---|---|---|---|---|---|
| sdk_test_data/rules-based | execution_time | [144.599µs; 146.228µs] or [-0.560%; +0.560%] | None | None | None |
Group 4
| cpu_model | git_commit_sha | git_commit_date | git_branch |
|---|---|---|---|
| Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz | d20319f | 1786412181 | agent/ffe-rejected-flag-errors |
| scenario | metric | min | mean ± sd | median ± mad | p75 | p95 | p99 | max | peak_to_median_ratio | skewness | kurtosis | cv | sem | runs | sample_size |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| single_flag_killswitch/rules-based | execution_time | 155.946ns | 159.414ns ± 2.849ns | 158.691ns ± 1.718ns | 160.804ns | 164.643ns | 169.031ns | 173.743ns | 9.49% | 1.555 | 3.604 | 1.78% | 0.201ns | 1 | 200 |
| scenario | metric | 95% CI mean | Shapiro-Wilk pvalue | Ljung-Box pvalue (lag=1) | Dip test pvalue |
|---|---|---|---|---|---|
| single_flag_killswitch/rules-based | execution_time | [159.019ns; 159.809ns] or [-0.248%; +0.248%] | None | None | None |
Baseline
Baseline benchmark details
Group 1
| cpu_model | git_commit_sha | git_commit_date | git_branch |
|---|---|---|---|
| Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz | 130ed6b | 1786380770 | main |
| scenario | metric | min | mean ± sd | median ± mad | p75 | p95 | p99 | max | peak_to_median_ratio | skewness | kurtosis | cv | sem | runs | sample_size |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| flagevaluation_evp/coalescer/scale/2500flags_500users_20fields | execution_time | 5.408ms | 5.429ms ± 0.015ms | 5.428ms ± 0.004ms | 5.432ms | 5.439ms | 5.453ms | 5.622ms | 3.57% | 9.917 | 121.384 | 0.28% | 0.001ms | 1 | 200 |
| flagevaluation_evp/coalescer/scale/2500flags_500users_20fields | throughput | 444708.740op/s | 460506.524op/s ± 1270.877op/s | 460573.185op/s ± 340.083op/s | 460928.290op/s | 461514.449op/s | 461841.601op/s | 462243.447op/s | 0.36% | -9.705 | 117.835 | 0.28% | 89.865op/s | 1 | 200 |
| flagevaluation_evp/coalescer/stress/10flags_1000users_250fields | execution_time | 7.970ms | 7.985ms ± 0.008ms | 7.984ms ± 0.004ms | 7.988ms | 7.994ms | 8.016ms | 8.026ms | 0.52% | 2.293 | 9.261 | 0.10% | 0.001ms | 1 | 200 |
| flagevaluation_evp/coalescer/stress/10flags_1000users_250fields | throughput | 124598.699op/s | 125232.935op/s ± 119.355op/s | 125243.531op/s ± 58.932op/s | 125300.921op/s | 125372.771op/s | 125430.079op/s | 125469.024op/s | 0.18% | -2.276 | 9.165 | 0.10% | 8.440op/s | 1 | 200 |
| flagevaluation_evp/coalescer/typical/100flags_50users_10fields | execution_time | 182.319µs | 183.211µs ± 0.621µs | 183.085µs ± 0.164µs | 183.333µs | 184.167µs | 184.639µs | 189.517µs | 3.51% | 6.048 | 53.713 | 0.34% | 0.044µs | 1 | 200 |
| flagevaluation_evp/coalescer/typical/100flags_50users_10fields | throughput | 527658.542op/s | 545824.008op/s ± 1814.184op/s | 546193.409op/s ± 488.925op/s | 546586.134op/s | 547218.937op/s | 547749.592op/s | 548488.284op/s | 0.42% | -5.858 | 50.977 | 0.33% | 128.282op/s | 1 | 200 |
| scenario | metric | 95% CI mean | Shapiro-Wilk pvalue | Ljung-Box pvalue (lag=1) | Dip test pvalue |
|---|---|---|---|---|---|
| flagevaluation_evp/coalescer/scale/2500flags_500users_20fields | execution_time | [5.427ms; 5.431ms] or [-0.039%; +0.039%] | None | None | None |
| flagevaluation_evp/coalescer/scale/2500flags_500users_20fields | throughput | [460330.393op/s; 460682.655op/s] or [-0.038%; +0.038%] | None | None | None |
| flagevaluation_evp/coalescer/stress/10flags_1000users_250fields | execution_time | [7.984ms; 7.986ms] or [-0.013%; +0.013%] | None | None | None |
| flagevaluation_evp/coalescer/stress/10flags_1000users_250fields | throughput | [125216.394op/s; 125249.477op/s] or [-0.013%; +0.013%] | None | None | None |
| flagevaluation_evp/coalescer/typical/100flags_50users_10fields | execution_time | [183.125µs; 183.297µs] or [-0.047%; +0.047%] | None | None | None |
| flagevaluation_evp/coalescer/typical/100flags_50users_10fields | throughput | [545572.580op/s; 546075.437op/s] or [-0.046%; +0.046%] | None | None | None |
Group 2
| cpu_model | git_commit_sha | git_commit_date | git_branch |
|---|---|---|---|
| Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz | 130ed6b | 1786380770 | main |
| scenario | metric | min | mean ± sd | median ± mad | p75 | p95 | p99 | max | peak_to_median_ratio | skewness | kurtosis | cv | sem | runs | sample_size |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| flagevaluation_evp/payloads/scale/2500flags_500users_20fields | execution_time | 21.845ms | 21.913ms ± 0.032ms | 21.910ms ± 0.022ms | 21.933ms | 21.970ms | 21.990ms | 22.009ms | 0.45% | 0.442 | -0.011 | 0.15% | 0.002ms | 1 | 200 |
| flagevaluation_evp/payloads/scale/2500flags_500users_20fields | throughput | 113592.323op/s | 114088.268op/s ± 166.621op/s | 114105.718op/s ± 112.575op/s | 114206.588op/s | 114334.590op/s | 114417.786op/s | 114444.440op/s | 0.30% | -0.434 | -0.022 | 0.15% | 11.782op/s | 1 | 200 |
| flagevaluation_evp/payloads/stress/10flags_1000users_250fields | execution_time | 95.602ms | 96.241ms ± 0.289ms | 96.250ms ± 0.146ms | 96.389ms | 96.677ms | 97.035ms | 98.205ms | 2.03% | 1.634 | 10.334 | 0.30% | 0.020ms | 1 | 200 |
| flagevaluation_evp/payloads/stress/10flags_1000users_250fields | throughput | 10182.829op/s | 10390.694op/s ± 31.067op/s | 10389.650op/s ± 15.772op/s | 10405.836op/s | 10443.565op/s | 10454.042op/s | 10459.989op/s | 0.68% | -1.548 | 9.730 | 0.30% | 2.197op/s | 1 | 200 |
| flagevaluation_evp/payloads/typical/100flags_50users_10fields | execution_time | 610.222µs | 612.628µs ± 0.699µs | 612.590µs ± 0.486µs | 613.084µs | 613.731µs | 614.618µs | 614.719µs | 0.35% | 0.141 | 0.579 | 0.11% | 0.049µs | 1 | 200 |
| flagevaluation_evp/payloads/typical/100flags_50users_10fields | throughput | 162676.087op/s | 163231.425op/s ± 186.165op/s | 163241.326op/s ± 129.592op/s | 163360.899op/s | 163521.955op/s | 163618.100op/s | 163874.659op/s | 0.39% | -0.133 | 0.578 | 0.11% | 13.164op/s | 1 | 200 |
| scenario | metric | 95% CI mean | Shapiro-Wilk pvalue | Ljung-Box pvalue (lag=1) | Dip test pvalue |
|---|---|---|---|---|---|
| flagevaluation_evp/payloads/scale/2500flags_500users_20fields | execution_time | [21.908ms; 21.917ms] or [-0.020%; +0.020%] | None | None | None |
| flagevaluation_evp/payloads/scale/2500flags_500users_20fields | throughput | [114065.175op/s; 114111.360op/s] or [-0.020%; +0.020%] | None | None | None |
| flagevaluation_evp/payloads/stress/10flags_1000users_250fields | execution_time | [96.201ms; 96.281ms] or [-0.042%; +0.042%] | None | None | None |
| flagevaluation_evp/payloads/stress/10flags_1000users_250fields | throughput | [10386.388op/s; 10394.999op/s] or [-0.041%; +0.041%] | None | None | None |
| flagevaluation_evp/payloads/typical/100flags_50users_10fields | execution_time | [612.531µs; 612.725µs] or [-0.016%; +0.016%] | None | None | None |
| flagevaluation_evp/payloads/typical/100flags_50users_10fields | throughput | [163205.624op/s; 163257.226op/s] or [-0.016%; +0.016%] | None | None | None |
Group 3
| cpu_model | git_commit_sha | git_commit_date | git_branch |
|---|---|---|---|
| Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz | 130ed6b | 1786380770 | main |
| scenario | metric | min | mean ± sd | median ± mad | p75 | p95 | p99 | max | peak_to_median_ratio | skewness | kurtosis | cv | sem | runs | sample_size |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| sdk_test_data/rules-based | execution_time | 124.706µs | 127.606µs ± 5.294µs | 126.491µs ± 0.695µs | 127.503µs | 132.045µs | 145.099µs | 185.126µs | 46.35% | 7.809 | 73.935 | 4.14% | 0.374µs | 1 | 200 |
| scenario | metric | 95% CI mean | Shapiro-Wilk pvalue | Ljung-Box pvalue (lag=1) | Dip test pvalue |
|---|---|---|---|---|---|
| sdk_test_data/rules-based | execution_time | [126.872µs; 128.339µs] or [-0.575%; +0.575%] | None | None | None |
Group 4
| cpu_model | git_commit_sha | git_commit_date | git_branch |
|---|---|---|---|
| Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz | 130ed6b | 1786380770 | main |
| scenario | metric | min | mean ± sd | median ± mad | p75 | p95 | p99 | max | peak_to_median_ratio | skewness | kurtosis | cv | sem | runs | sample_size |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| single_flag_killswitch/rules-based | execution_time | 156.875ns | 160.430ns ± 2.752ns | 159.883ns ± 1.506ns | 161.617ns | 165.936ns | 169.821ns | 172.632ns | 7.97% | 1.507 | 2.902 | 1.71% | 0.195ns | 1 | 200 |
| scenario | metric | 95% CI mean | Shapiro-Wilk pvalue | Ljung-Box pvalue (lag=1) | Dip test pvalue |
|---|---|---|---|---|---|
| single_flag_killswitch/rules-based | execution_time | [160.049ns; 160.811ns] or [-0.238%; +0.238%] | None | None | None |
Artifact Size Benchmark Reportaarch64-alpine-linux-musl
aarch64-unknown-linux-gnu
libdatadog-x64-windows
libdatadog-x86-windows
x86_64-alpine-linux-musl
x86_64-unknown-linux-gnu
|
There was a problem hiding this comment.
Pull request overview
This PR updates libdd-ffe’s Rust and FFI error semantics so that per-flag ingestion rejections (e.g., invalid regex) are surfaced to callers as parse errors, while leaving neighboring valid flags evaluatable. It also aligns SemVer comparisons with SemVer precedence rules (ignoring build metadata) and updates tests and canonical fixtures to match the revised contract.
Changes:
- Map
FlagConfigurationInvalidto FFIReason::ErrorwithErrorCode::ParseError(and update canonical fixtures accordingly). - Add regression tests ensuring a rejected/invalid flag does not invalidate evaluation of a valid neighboring flag.
- Compare semantic versions by precedence (
cmp_precedence) so build metadata does not affect comparisons; add coverage for+buildvariants.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| libdd-ffe/src/rules_based/ufc/models.rs | Updates ingestion/evaluation comment to reflect regex compilation during ingestion (reject flag on compile failure). |
| libdd-ffe/src/rules_based/eval/eval_rules.rs | Switches SemVer equality/ordering to precedence comparison and adds tests for build metadata behavior. |
| libdd-ffe/src/rules_based/eval/eval_assignment.rs | Adds regression test proving an invalid flag does not poison a valid neighboring flag. |
| libdd-ffe/src/rules_based/error.rs | Clarifies FlagConfigurationInvalid contract: treat as per-flag parse error without invalidating the rest of the config. |
| libdd-ffe-test-suite/tests/canonical_fixtures.rs | Updates canonical adapter mappings so FlagConfigurationInvalid is treated as ERROR with PARSE_ERROR. |
| libdd-ffe-ffi/src/assignment.rs | Fixes FFI mapping: FlagConfigurationInvalid now yields ParseError, and adds a regression unit test. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| TargetingKeyMissing, | ||
|
|
||
| /// Failed to parse configuration. This should normally never happen and is likely a signal | ||
| /// that you should update SDK. |
There was a problem hiding this comment.
I feel like this comment could also be updated with something like "Failures encountered while compiling an individual flag are converted to FlagConfigurationInvalid at the per-flag ingestion boundary."
Motivation
Malformed per-flag UFC configuration is already isolated during ingestion: libdatadog stores a rejection error for that key while compiling valid neighboring flags normally. The FFI translation incorrectly exposed that rejection as a normal DEFAULT result with no error code, hiding a configuration bug from callers.
This implements the contract merged in DataDog/ffe-system-test-data#26: evaluating a rejected key returns the caller default with ERROR / PARSE_ERROR, while valid neighboring flags remain usable.
Changes
Decisions
Validation