Problem
OFREP static-context providers currently poll on a fixed timer by default (JS: 30s, Swift: 30s, Kotlin: 5min). This has two issues:
-
Sticky bucketing — polling causes flag values to change mid-session, which breaks the expectation that a user sees consistent behavior throughout a session. Most platforms expect bucketing to remain sticky for a user's whole session.
-
Resource efficiency — timer-based polling is wasteful for static-context providers where the evaluation context rarely changes during a session. This is especially problematic on mobile where it impacts battery life and data usage.
This is out of step with how vendor SDKs handle client-side refresh. DevCycle, LaunchDarkly, Statsig, and Eppo do not use timer-based polling as the default client-side refresh mechanism:
| Vendor |
Default Mechanism |
Timer Polling? |
| DevCycle |
Init + context change + foreground + SSE push |
No |
| LaunchDarkly |
SSE streaming foreground, 60min poll background only |
No (streams) |
| Statsig |
Init + user change only |
No |
| Eppo |
Init only, polling opt-in |
No (off by default) |
| ConfigCat |
Auto polling (60s) |
Yes |
| Unleash |
Polling (30s) |
Yes |
Proposed Solution
Replace default timer-based polling with event-driven refresh for static-context providers. Providers should re-fetch bulk evaluations on:
- Initialization (already defined in ADR-0009)
- Context change (already handled via
onContextChange)
- App foreground / page visibility — re-fetch when the app returns from background or the page becomes visible
Timer-based polling should remain available as an opt-in for applications that want it, but default to disabled.
Further details on foreground detection, debouncing, and interaction with SSE (see #62) will follow in an ADR PR.
Problem
OFREP static-context providers currently poll on a fixed timer by default (JS: 30s, Swift: 30s, Kotlin: 5min). This has two issues:
Sticky bucketing — polling causes flag values to change mid-session, which breaks the expectation that a user sees consistent behavior throughout a session. Most platforms expect bucketing to remain sticky for a user's whole session.
Resource efficiency — timer-based polling is wasteful for static-context providers where the evaluation context rarely changes during a session. This is especially problematic on mobile where it impacts battery life and data usage.
This is out of step with how vendor SDKs handle client-side refresh. DevCycle, LaunchDarkly, Statsig, and Eppo do not use timer-based polling as the default client-side refresh mechanism:
Proposed Solution
Replace default timer-based polling with event-driven refresh for static-context providers. Providers should re-fetch bulk evaluations on:
onContextChange)Timer-based polling should remain available as an opt-in for applications that want it, but default to disabled.
Further details on foreground detection, debouncing, and interaction with SSE (see #62) will follow in an ADR PR.