Repository navigation
FFL-3568: Share precomputed fixtures and lossless numeric policy - #4101
leoromanovsky wants to merge 51 commits into
Conversation
Add FlagsConfiguration.Builder.flagSelection(tags, keyPatterns). The selection is canonicalized and validated like Compute and ffe-service, sent as flag_selection on the precompute POST, and stored with the persisted state so a cache fetched with another selection is ignored. Report DD-Flag-Selection-Ignored, and treat a valid empty configuration as loaded so a later failed fetch reports Stale. Ref: FFL-3459
Ref: FFL-3459
Persist the flag selection through the generated FlagsState model and re-apply selection filtering, empty-config handling and the ignored header on top of develop's obfuscation changes. Ref: FFL-3459
The downloader reports DD-Flag-Selection-Ignored only for requests that carried flag_selection, and passes the value through verbatim. Ref: FFL-3459
Builder.flagSelection takes List<String>? for tags and key patterns. A null list is treated as absent, matching the browser SDK; null elements inside a list remain malformed_selection. Ref: FFL-3459
State that limits count values after dedupe, that flags outside the selection return the default with FLAG_NOT_FOUND, and when the server returns the full configuration instead. Ref: FFL-3459
Add hand-written canonicalizer cases for the selection vectors with sdk expectations that the existing tests did not cover. Ref: FFL-3459
Treat an empty DD-Flag-Selection-Ignored value as absent, as the browser SDK does, so it no longer logs a server ignore with an empty reason. Clarify in the flagSelection KDoc that the byte limit applies after trimming, that unpaired UTF-16 surrogates are malformed, and that FLAG_NOT_FOUND applies when the server applies the selection. Ref: FFL-3459
Verify that FlagsClient passes the configured selection to the repository, so a persisted state saved under that selection is served. Verify that a persisted state saved without a selection is discarded once a selection is configured. Extend the trim test to every Unicode White_Space character. Ref: FFL-3459
Dedupe selection values before sorting, compute each UTF-8 sort key once, and skip sorting a list that is already over the value limit. Pass the response mapper by reference. Drop positional null arguments in tests that only restated the default obfuscation value. Ref: FFL-3459
Send the canonical selection through the generated request model that develop introduced.
The reason is reported before the body is parsed, so it is logged even when the configuration is not installed.
List all 38 cases that have an sdk expectation in the shared vectors, in file order, so each one maps to one test row.
A Java caller can pass a raw list, so the canonicalizer checks each element's type. A non-string element is malformed_selection, as in the browser SDK, instead of a ClassCastException from Flags.enable.
SDK versions without flag selection read flags-state-<instance> with any version and ignore the flagSelection field, so a rollback served a selected subset as the full configuration. Selected state now uses flags-selection-state-<instance>, and creating an instance removes the other key so one state remains per instance. Unselected state stays where older SDKs read it.
The header value reaches USER logs and telemetry, so keep the five reasons the server sends and report any other non-empty value as unknown. The selection is still treated as ignored and the full configuration is still installed.
hasFlags() and hasLoadedFlagsForContext() report whether a configuration is installed, including a valid one with zero flags. Name them hasConfiguration() and hasConfigurationForContext() so call sites read as intended.
Log the DD-Flag-Selection-Ignored reason and send its telemetry only after the response maps to a configuration and passes the obfuscation filter, just before it is installed. A body that fails to parse, or an encoded configuration this source cannot use, no longer reports a reason for a configuration that was never loaded.
Resolve the flag selection and experimentation opt-out conflicts: - Each opt-out mode gets a selection variant of its cache key (flags-selection-state, flags-selection-immediate-v1-state, flags-selection-future-v1-state). SDK versions without flag selection read only the unselected key of their mode, so they never load selected state as a full configuration, and creating an instance removes the sibling key so one persisted state remains per instance. - The per-client request factory used for opt-out clients now carries the flag selection, so a request sends both flag_selection and evaluation_options when both apply. - Keep the hasConfiguration() names, read the ignored reason from the accepted response, and map the response body.
The dropped and ignored selection telemetry events now use onlyOnce, so frequent fetches cannot flood telemetry. The USER error log is unchanged and still reports every ignored response.
With the experimentation opt-out, a newer context can supersede an accepted response, which is then not installed. Log the ignored reason after installFlags confirms the install, so the message never claims a configuration that was not loaded.
Resolve the DefaultFlagsRepository constructor conflict: keep develop's change that makes instanceName and dataStore plain constructor parameters, and treat the branch's flagSelection the same way, since it is only read while building the persistence manager.
Say that the flag selection is what every FlagsClient shares, and use a function reference for the empty ignored-reason header check.
…pendency-of-test-fixtures-on-tools-unit RUM-19577: Break dependency of test fixtures on `:tools:unit` module
Run the canonical JSON corpus through the client and persistent cache. Enforce numeric getter types, preserve legacy number reads, and automate signed fixture refreshes.
FFL-3459: Support flagSelection on Flags precompute requests
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 96b2bf6 | Docs | View more details | Give us feedback! |
|
🤖 Comment from Claude, working with Aaron: This PR and #4099 both change #4099 now checks whole values in Double reads as well as Int reads. We ran the shared fixtures from DataDog/ffe-system-test-data#65 against it with this PR's runner, merged with Two differences to settle:
Could you also update the description's statement that #4099 "still enforces the previous strict-type contract"? #4099 already has lossless Int reads across all numeric types. |
|
🤖 Comment from Claude, working with Aaron: P3 follow-ups after rebasing on #4099:
|
Preserve original numeric precision for typed reads and cache restoration. Ref: FFL-3568
Merge the dependency branch, pin shared fixtures to main at 9afc3f7, and address runner and numeric review feedback. Ref: FFL-3568
|
@aarsilv Follow-ups addressed in a45b99c342 and the numeric documentation follow-up:
The snapshot pins fixture main at |
What does this PR do?
Make native flag evaluations and emitted events conform to the merged shared contract. The 63-case snapshot pins fixture
mainat 9afc3f75c3fef9643a9c1b5e2b1e5a9adac48177 and runs through the public client before and after cache restoration. Ref: FFL-3568.Motivation
Consumers need one tested rule for numeric reads, unsupported variation types, obfuscated flag keys, and event aggregation. A typed read must not silently lose integer precision or log a successful exposure for a rejected conversion.
Additional Notes
Stacked on #4099, retaining its converter structure and bounded decimal parsing. The additional checks align it with the merged contract: fractional Double reads round, whole values must be exactly representable, and overflow/nonzero underflow return
TYPE_MISMATCH. For example,1.20000000000000001returns1.2;1e300is rejected as an inexact whole-number Double read. Malformed numeric text remains a parse error.Unsupported variation types are omitted with a once-only internal user warning, leaving valid siblings available. Missing
extraLoggingdefaults are compatible with shared responses. Obfuscated lookups and real emitted records retain plaintext flag identity. The runner reports an actionable fixture error for an applicable null targeting key; the native context API requires a string.The shared importer preserves provenance and response tokens. A weekly workflow prepares draft fixture updates. Obsolete converter detekt entries were removed; entries required by the merged code remain.
Validation: 1,007 feature unit tests and 259 integration tests pass, with 3 integration platform exclusions. Affected-module formatting and detekt pass. All snapshot integrity/source checks pass. Hosted comparison is tracked in the native preview rerun; these are unit-level checks, not deployed-backend validation.
Review checklist (to be filled by reviewers)