Skip to content

docs: add ADR 0009 for static-context cache-first local persistence#64

Closed
jonathannorris wants to merge 29 commits intomainfrom
cursor/offline-context-local-storage-4fb5
Closed

docs: add ADR 0009 for static-context cache-first local persistence#64
jonathannorris wants to merge 29 commits intomainfrom
cursor/offline-context-local-storage-4fb5

Conversation

@jonathannorris
Copy link
Copy Markdown
Member

@jonathannorris jonathannorris commented Mar 6, 2026

Summary

  • add ADR 0009 proposing local persistence with cache-first initialization for static-context OFREP providers
  • on startup, providers load cached evaluations immediately so initial flag evaluations never return defaults, then refresh from the network in parallel
  • define cache-hit and cache-miss initialization paths mapped to the OpenFeature provider lifecycle (PROVIDER_READY, PROVIDER_CONFIGURATION_CHANGED)
  • cache key uses hash(targetingKey), no auth token dependency

Motivation

Every major vendor SDK (LaunchDarkly, Statsig, DevCycle, Eppo) uses cache-first initialization by default. Current OFREP static-context providers keep their cache in memory only, losing all state on restart. See vendor mobile SDK caching research for a detailed comparison.

Related

Test plan

  • not applicable; documentation-only change

@cursor
Copy link
Copy Markdown

cursor bot commented Mar 6, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces an Architectural Decision Record (ADR) proposing a standard approach for web and mobile OFREP providers to enhance offline functionality. It aims to improve user experience by enabling the persistence of feature flag evaluations in local storage, ensuring that applications can operate and maintain consistent feature states even with intermittent network connectivity or after restarts.

Highlights

  • Offline Resilience for OFREP Static-Context Providers: Proposes that web and mobile OFREP providers should persist their last successful bulk evaluation in local storage by default. This enables offline application startup and preserves feature state across restarts, addressing intermittent connectivity issues.
  • Persistence Mechanism Details: Specifies that the persisted entry should include the bulk evaluation payload, ETag, metadata for matching the current provider instance (like OFREP endpoint and evaluation context), and the write time. It also outlines the initialization flow for loading from local storage.
  • Consequences and Considerations: Discusses positive impacts like improved resilience and consistent offline behavior, as well as negative aspects such as increased provider complexity, potential for stale data, and security/privacy concerns with persisted data.
Changelog
  • service/adrs/0009-localStorageForStaticContextProviders.md
    • Added a new Architectural Decision Record (ADR) proposing the default use of local storage for static-context OFREP providers to enable offline evaluation caching.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces ADR 0009, which proposes persisting static-context evaluations in local storage for web and mobile providers to improve offline capabilities. The ADR is well-structured and covers the main aspects of the proposal. My review includes a few suggestions to enhance the clarity and robustness of the proposed design, specifically regarding the handling of server errors during initialization, clarifying the purpose of the persisted timestamp, and making the initialization flow more explicit and unambiguous.

Note: Security Review has been skipped due to the limited scope of the PR.

@jonathannorris jonathannorris changed the title Offline context local storage docs: refine ADR 0009 for static-context local persistence Mar 7, 2026
@jonathannorris jonathannorris changed the title docs: refine ADR 0009 for static-context local persistence docs: add ADR 0009 for static-context local persistence Mar 7, 2026
@jonathannorris jonathannorris force-pushed the cursor/offline-context-local-storage-4fb5 branch from e64867d to 9ac24a6 Compare March 7, 2026 12:25
Copy link
Copy Markdown
Member

@lukas-reining lukas-reining left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @jonathannorris, left some feedback.
As you, I think this is a very important addition to the OFREP providers.

I have some thoughts regarding the cache key, which could also influence the whole concept due to the static context possibly also not being available as you said in open question no. 2.

@jonathannorris jonathannorris marked this pull request as ready for review March 13, 2026 02:31
@jonathannorris jonathannorris requested a review from a team as a code owner March 13, 2026 02:31
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@jonathannorris jonathannorris force-pushed the cursor/offline-context-local-storage-4fb5 branch 2 times, most recently from 13ee5b3 to 5dbb057 Compare March 19, 2026 21:09
@jonathannorris jonathannorris changed the title docs: add ADR 0009 for static-context local persistence docs: add ADR 0009 for static-context cache-first local persistence Mar 19, 2026
@jonathannorris
Copy link
Copy Markdown
Member Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces ADR 0009, which proposes a cache-first local persistence strategy for static-context OFREP providers. The ADR is well-structured and detailed, covering the motivation, design, consequences, and implementation considerations. The proposed cache-first initialization aligns with industry best practices and addresses the 'flash-of-defaults' problem effectively. I have a couple of suggestions to improve the clarity and security aspects of the proposal.

@jonathannorris
Copy link
Copy Markdown
Member Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces ADR-0009, which proposes a cache-first initialization strategy for static-context providers using local storage persistence. It also updates the OpenAPI specification to support real-time change notifications via Server-Sent Events (SSE). Feedback indicates that the OpenAPI version 3.2.0 is non-standard and breaks compatibility, the itemSchema keyword in the webhook definition is invalid, and the ADR's proposed cache key needs namespacing and a specific hashing algorithm to prevent collisions. Additionally, the redundancy of new query parameters alongside standard HTTP cache headers should be addressed.

@jonathannorris
Copy link
Copy Markdown
Member Author

@lukas-reining / @thomaspoignant take a look at this ADR when you get a chance

jonathannorris and others added 23 commits April 13, 2026 13:59
Document an explicit provider option for turning off persisted local storage.

Signed-off-by: Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Drop authToken from cache key derivation and replace sha256 with generic hash(), per reviewer feedback.

Signed-off-by: Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
…tion

Specify CACHED as the evaluation reason when serving from persisted storage. Remove fallback scope open question since the decision section already addresses it.

Signed-off-by: Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Use must not for auth/config error fallback to prevent masking real problems.

Signed-off-by: Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Local storage availability is a platform constraint, not a consequence of the proposal.

Signed-off-by: Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Specify that flag values are stored in plaintext and accessible to same-origin code or compromised devices.

Signed-off-by: Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
The specific storage key and record model are implementation details.

Signed-off-by: Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Remove redundant implementation notes that overlap with the decision section. Simplify mermaid diagram initialize call to use context.

Signed-off-by: Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Replace fallback-on-failure with cache-first initialization pattern aligned with vendor SDKs (LaunchDarkly, Statsig, DevCycle, Eppo). Provider loads from persisted cache immediately on startup, refreshes from network in background, and emits PROVIDER_CONFIGURATION_CHANGED when fresh values arrive.

Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Fix PROVIDER_FATAL to PROVIDER_ERROR with fatal error code per spec. Add rationale for READY vs STALE on cache-hit startup. Clarify cache key tradeoff (targetingKey vs full context). Note existing provider implementations will need lifecycle refactors.

Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
On the cache-hit path, if the background refresh fails with 401/403/400, the provider continues serving cached values for the current session but clears the persisted entry. This ensures the next cold start uses the cache-miss path, making auth errors immediately visible.

Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
- Bump OpenAPI version from 3.1.0 to 3.2.0 for text/event-stream support
- Bump OFREP version from 0.2.0 to 0.3.0
- Add flagConfigEtag and flagConfigLastModified query parameters to both eval endpoints
- Add eventStreams field to bulkEvaluationSuccess and serverEvaluationSuccess responses
- Add eventStream schema with mutually exclusive url/endpoint fields
- Add eventStreamEndpoint schema for structured origin + requestUri
- Add sseEvent and sseEventData schemas for event stream payloads
- Add webhook documenting text/event-stream content type (OAS 3.2.0)
- Disable oas3-schema spectral rule until 3.2.0 support lands (stoplightio/spectral#2910)

Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
…rt behavior

Providers should cancel in-flight background refresh when onContextChanged() is called. Document that cache-first only applies after the first successful evaluation is persisted.

Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
…e URL"

This reverts commit b69eafd.

Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
… for prefixing the cache key (#74)

Signed-off-by: Jason Salaber <jason.salaber@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
@jonathannorris jonathannorris force-pushed the cursor/offline-context-local-storage-4fb5 branch from 1aaba80 to ad025d9 Compare April 13, 2026 18:00
Revert .spectral.yaml and service/openapi.yaml to main. These ADR-0008 changes belong in PR #67.

Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
@jonathannorris jonathannorris deleted the cursor/offline-context-local-storage-4fb5 branch April 13, 2026 18:13
@jonathannorris jonathannorris restored the cursor/offline-context-local-storage-4fb5 branch April 13, 2026 18:13
@jonathannorris jonathannorris deleted the cursor/offline-context-local-storage-4fb5 branch April 13, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OFREP Static-Context Provider Local Persistence

6 participants