Skip to content

feat(ofrep): Disable polling by default#1510

Open
marcozabel wants to merge 3 commits intoopen-feature:mainfrom
open-feature-forking:feat/disable-polling-by-default
Open

feat(ofrep): Disable polling by default#1510
marcozabel wants to merge 3 commits intoopen-feature:mainfrom
open-feature-forking:feat/disable-polling-by-default

Conversation

@marcozabel
Copy link
Copy Markdown
Contributor

This PR

Disabled the default polling interval to make way for SSE.

Related Issues

Part of #1497

Notes

Follow-up Tasks

How to test

- Change DEFAULT_POLL_INTERVAL from 30000ms to 0 (disabled by default)
- Update OFREPWebProviderOptions documentation to reflect polling is now opt-in
- Polling must be explicitly enabled by setting pollInterval to a positive number
- This is a breaking change, appropriate for sub-v1 OFREP providers

Signed-off-by: marcozabel <marco.zabel@dynatrace.com>
- Change DEFAULT_POLL_INTERVAL from 30000ms to 0 (polling disabled by default)
- Add visibility change detection to re-fetch flags when page/app becomes visible
- Add _visibilityChangeHandler property and _onVisibilityChange() method
- Register/unregister visibility change listener in initialize/onClose
- Re-fetches respect Retry-After rate limit headers
- Emits ConfigurationChanged or Stale events based on fetch results

Signed-off-by: marcozabel <marco.zabel@dynatrace.com>
@marcozabel marcozabel force-pushed the feat/disable-polling-by-default branch from 5dacf91 to 7ccc33c Compare April 9, 2026 09:51
@marcozabel marcozabel marked this pull request as ready for review April 9, 2026 09:51
@marcozabel marcozabel requested review from a team as code owners April 9, 2026 09:51
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 a breaking change by disabling polling by default (setting pollInterval to 0) and adds a new feature that automatically re-fetches flags when the page or application becomes visible. Feedback suggests refactoring the flag-fetching logic into a shared helper method to reduce duplication and implementing a concurrency lock or flag in the visibility change handler to prevent redundant, overlapping network requests during rapid state transitions.

Comment on lines +337 to +356
private async _onVisibilityChange() {
// Only re-fetch when the page becomes visible, not when it's hidden
if (typeof document !== 'undefined' && document.visibilityState === 'visible') {
try {
const now = new Date();
if (this._retryPollingAfter !== undefined && this._retryPollingAfter > now) {
return;
}
const res = await this._fetchFlags(this._context);
if (res.status === BulkEvaluationStatus.SUCCESS_WITH_CHANGES) {
this.events?.emit(ClientProviderEvents.ConfigurationChanged, {
message: 'Flags updated after visibility change',
flagsChanged: res.flags,
});
}
} catch (error) {
this.events?.emit(ClientProviderEvents.Stale, { message: `Error while fetching after visibility change: ${error}` });
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The logic for fetching flags and emitting events is duplicated between startPolling and _onVisibilityChange. Consider extracting this into a private helper method (e.g., _refreshFlags(reason: string)) to improve maintainability and ensure consistent error handling across different background update triggers.

Comment on lines +337 to +356
private async _onVisibilityChange() {
// Only re-fetch when the page becomes visible, not when it's hidden
if (typeof document !== 'undefined' && document.visibilityState === 'visible') {
try {
const now = new Date();
if (this._retryPollingAfter !== undefined && this._retryPollingAfter > now) {
return;
}
const res = await this._fetchFlags(this._context);
if (res.status === BulkEvaluationStatus.SUCCESS_WITH_CHANGES) {
this.events?.emit(ClientProviderEvents.ConfigurationChanged, {
message: 'Flags updated after visibility change',
flagsChanged: res.flags,
});
}
} catch (error) {
this.events?.emit(ClientProviderEvents.Stale, { message: `Error while fetching after visibility change: ${error}` });
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The _onVisibilityChange handler is an async function used as an event listener. Since visibilitychange can fire multiple times in quick succession (e.g., rapid tab switching), and _fetchFlags involves an asynchronous network request, there is a risk of multiple concurrent fetches. While the use of ETags mitigates server load, it's generally better to prevent overlapping requests by using a simple 'isFetching' flag or a concurrency lock to avoid race conditions and redundant network activity.

- Extract duplicated fetch+emit logic from startPolling and _onVisibilityChange
  into a shared _refreshFlags(reason) method
- Add _isFetching flag to prevent overlapping concurrent requests
  from rapid visibility changes or polling
- Addresses PR review feedback

Signed-off-by: marcozabel <marco.zabel@dynatrace.com>
* Default: 30000
* If a negative number or 0 is provided, the provider will not poll the OFREP API.
* This is the default behavior. Polling is available as an opt-in configuration.
* Default: 0 (disabled)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

does the issue specify changing the polling behaviour?

Copy link
Copy Markdown
Contributor Author

@marcozabel marcozabel Apr 15, 2026

Choose a reason for hiding this comment

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

  1. On SSE connection failure, fall back to polling if polling is enabled; otherwise continue SSE reconnection attempts

This reads to me like it is not enabled by default. Did I misread that @toddbaert ?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

my mistake, it is part of this new adr that hasn't been merged yet open-feature/protocol#69

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.

2 participants