Skip to content

feat: add WithDisablePolling and WithSynchronousFetchOnInitialisation options - #268

Closed
sukesh2000 wants to merge 2 commits into
Unleash:v5from
sukesh2000:feat/v5-disable-polling-synchronous-fetch
Closed

feat: add WithDisablePolling and WithSynchronousFetchOnInitialisation options#268
sukesh2000 wants to merge 2 commits into
Unleash:v5from
sukesh2000:feat/v5-disable-polling-synchronous-fetch

Conversation

@sukesh2000

Copy link
Copy Markdown

About the changes

Adds two new config options available in other Unleash SDKs (Node, Java) but not yet present in the Go v5 SDK:

  • WithDisablePolling(true) — stops the client from polling the Unleash server after the initial startup. The client serves whatever state was loaded from backup storage or fetched on initialisation. Intended for serverless/lambda environments, air-gapped deployments, and CI setups where toggle state only changes on redeploy.

  • WithSynchronousFetchOnInitialisation(true) — makes NewClient block until the first fetch completes, so toggles are available immediately on return without calling WaitForReady(). Subsequent polling (if enabled) continues as normal in the background.

The two options compose:

disablePolling synchronousFetch behaviour
false false unchanged — goroutine fetches once then polls (default)
false true NewClient blocks on first fetch, goroutine skips the leading fetch, polls normally
true false ready fires immediately from backup/empty state, no polling goroutine
true true NewClient blocks on first fetch, ready fires, no polling goroutine

In all four cases, if the initial fetch fails the client falls back to whatever is in backup storage rather than returning an error, and ready still fires so the application is not blocked.

Closes #185

Important files

  • config.go — the two new With* option funcs and their doc comments, start here for the public API shape
  • repository.go — core logic: the four-quadrant behaviour matrix, the skip-leading-fetch guard in sync() that prevents a double-fetch when synchronousFetch already succeeded, and guarded Close() for the no-goroutine path
  • client_test.go — client-level integration tests for all four flag combinations plus the streaming interaction

Discussion points

WithSynchronousFetchOnInitialisation has no effect in streaming mode.

In v5, the synchronous fetch path is guarded by !repo.isStreaming, so streaming mode is unaffected. Streaming starts the sync goroutine as normal.

disablePolling=true is overridden by streaming.

When isStreaming=true, the disablePolling flag is ignored — the sync goroutine always starts for streaming. Close() correctly uses the blocking shutdown path when streaming is active.

Close() with disablePolling=true (non-streaming) does not start or wait on a goroutine.

r.close, r.closed, and r.refreshTicker are only allocated when the goroutine starts. Close() is guarded with nil checks and skips the blocking <-r.closed wait, so there is no deadlock.

Failover from sync fetch failure retries immediately.

When synchronousFetch=true and the initial fetch fails, the polling goroutine's leading-fetch condition is !r.options.synchronousFetch || !r.isReady. This ensures the goroutine retries immediately rather than waiting a full refreshInterval before the client becomes ready.

@sukesh2000

Copy link
Copy Markdown
Author

Closing this as well. See the discussion in #267. Thanks.

@sukesh2000 sukesh2000 closed this Jun 26, 2026
@github-project-automation github-project-automation Bot moved this from New to Done in Issues and PRs Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants