-
Notifications
You must be signed in to change notification settings - Fork 7
docs: amend ADR 0009 to define cache behavior with no targeting key #87
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: main
Are you sure you want to change the base?
Changes from all commits
f75af5e
1c1d013
4563415
dc25e72
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,8 @@ Accepted | |
|
|
||
| Proposed amendment (2026-06-19): tie the cache key to the OFREP resource the evaluation was fetched from by including the provider's bound `domain`, the OFREP base URL, and the auth credential, in addition to the `targetingKey`, and expose a cache-key generator function so applications can customize the key. See [open-feature/spec#393](https://github.com/open-feature/spec/pull/393). | ||
|
|
||
| Proposed amendment (2026-09-29): do not persist evaluations for contexts with no `targetingKey`, since the cache key would then carry no identity and the entry could be served to a different subject. Applications that want persistence for anonymous contexts opt in with a cache-key generator. | ||
|
|
||
| ## Context | ||
|
|
||
| OFREP static-context providers evaluate all flags in one request and then serve evaluations from a local cache. | ||
|
|
@@ -237,6 +239,18 @@ In `network-first` mode, fallback to a persisted entry is limited to network err | |
|
|
||
| When the provider has already initialized from cache (cache hit path in `local-cache-first` mode), authorization or configuration errors from the background refresh should be logged and emitted as `PROVIDER_ERROR` events. The provider should continue serving cached values for the current session rather than revoking a working state. Auth or config errors alone should not invalidate the persisted entry; the cache TTL is what governs when it stops being served. This avoids degrading subsequent cold starts to defaults while the error is investigated. | ||
|
|
||
| ### Missing targeting key | ||
|
|
||
| The `targetingKey` is optional, so a provider may be asked to evaluate a context without one. Providers must still evaluate normally and must not raise `TARGETING_KEY_MISSING`, but they should not persist the result. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '1,85p' service/adrs/0009-local-storage-for-static-context-providers.md
sed -n '190,260p' service/adrs/0009-local-storage-for-static-context-providers.md
sed -n '300,340p' service/adrs/0009-local-storage-for-static-context-providers.md
sed -n '600,625p' service/openapi.yamlRepository: open-feature/protocol Length of output: 20835 🏁 Script executed: printf '%s\n' '--- OFREP endpoint and schema references ---'
rg -n -C 4 'evaluate/flags|targetingKey|evaluation context|context:' service/openapi.yaml
printf '%s\n' '--- ADR provider workflow and OFREP references ---'
rg -n -C 3 'OFREP|request|context|targetingKey|evaluate' service/adrs/0009-local-storage-for-static-context-providers.md | head -220Repository: open-feature/protocol Length of output: 26198 Align the bulk OFREP contract with the missing- The ADR's static-context workflow refreshes through 🤖 Prompt for AI Agents |
||
|
|
||
| Without a `targetingKey` the cache key carries no identity, so it reduces to the OFREP resource inputs (base URL, auth credential, and bound `domain`) and is identical for every context against that resource. Nothing invalidates the entry when the subject or the rest of the context changes. An application that signs a user out and continues anonymously, or that changes anonymous properties such as `country` or `plan`, would be served the previous evaluation even though the server would return different values. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ETag can supplement for the identity, if the response is the same, the cache is still valid even after changing the targeting properties.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would you use the ETag in the offline case? To me, when we can get an ETag, the cache is not needed anyways.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct |
||
|
|
||
| Providers should therefore treat a context with no `targetingKey` as non-persistable, neither reading nor writing an entry, so `local-cache-first` behaves like `disabled` for that context. A provider holding an entry from an earlier context that had a `targetingKey` should clear it when the `targetingKey` is removed. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like this is the only way to go here but I am worried that this detail could be very confusing to app authors. They might build an app with offline capabilities without knowing that and be confused about this. |
||
|
|
||
| Applications that want persistence for anonymous contexts should supply a cache-key generator. Configuring one asserts that the returned key material identifies the subject, for example a stable device or anonymous ID, and the provider persists using that key. | ||
|
|
||
| Entries are cleared based on the derived cache key, not the `targetingKey` alone. A generator that includes other context properties can change the key while the `targetingKey` is unchanged, leaving a stale entry behind. | ||
|
|
||
| ### Refresh and revalidation | ||
|
|
||
| When connectivity returns or during normal polling, the provider should resume its normal refresh behavior. | ||
|
|
@@ -310,7 +324,8 @@ A single default (local-cache-first) with an explicit per-application opt-out is | |
| - Providers should avoid persisting raw `targetingKey` values when `cacheKeyHash` is sufficient for matching | ||
| - Providers should expose a `cacheMode` option with values `local-cache-first` (default), `network-first`, and `disabled`. `network-first` and `disabled` block `initialize()` on the network request; `local-cache-first` returns from `initialize()` immediately when a persisted entry exists | ||
| - Providers should expose an optional cache-key generator function so applications and wrapping providers can customize the key material (narrowing or broadening the default); the provider always hashes whatever the generator returns | ||
| - Providers should clear or replace persisted entries when the cache key changes, such as on logout or user switch (`targetingKey` change) or when the provider is re-bound to a different `domain` | ||
| - Providers should not read or write a persisted entry for a context with no `targetingKey` unless a cache-key generator is configured; `local-cache-first` behaves like `disabled` for such contexts | ||
| - Providers should clear or replace persisted entries when the derived cache key changes, such as on logout or user switch (a `targetingKey` change, including to or from absent) or when the provider is re-bound to a different `domain`. Compare the key material the generator produces, not the `targetingKey` alone | ||
| - In `local-cache-first` mode, the `initialize()` function should return immediately when a matching cached entry exists, allowing the SDK to emit `PROVIDER_READY` from cache | ||
| - Providers should emit `PROVIDER_CONFIGURATION_CHANGED` when fresh values replace cached values after a background refresh | ||
| - If `onContextChanged()` is called while a background refresh is still in-flight, the provider should cancel or discard the in-flight request. The context-change evaluation supersedes it and should be the authoritative write to the persisted entry | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't find it anymore but I think we discussed this before.
Proposed amendmentis an accepted one after merge isn't it? Should we leave put theproposed?