| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
Co-Authored-By: rlamb@launchdarkly.com <4955475+kinyoklion@users.noreply.github.com>
Co-Authored-By: rlamb@launchdarkly.com <4955475+kinyoklion@users.noreply.github.com>
Co-Authored-By: rlamb@launchdarkly.com <4955475+kinyoklion@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Sorry, something went wrong.
Co-Authored-By: rlamb@launchdarkly.com <4955475+kinyoklion@users.noreply.github.com>
| */ | ||
| public Provider(String sdkKey) { | ||
| this(sdkKey, new LDConfig.Builder().build()); | ||
| this(new LDClient(sdkKey, withWrapper(new LDConfig.Builder().build())), Duration.ZERO); |
There was a problem hiding this comment.
I think Duration.Zero meaning wait indefinitely is not consistent with Duration.Zero passed to other SDKs startup/init/wait APIs. I think Duration.Zero is usually interpreted as don't wait at all.
Sorry, something went wrong.
There was a problem hiding this comment.
Zero here came from OFP 4.3.4.2 — "If the configured start wait time is zero, the provider MUST NOT apply an initialization timeout" — with the rationale that zero means the application does not want to block on initialization and leaves how long to wait up to the caller.
Worth separating the two layers, because I think my javadoc is what actually reads wrong:
So the semantics follow the spec, but "waits indefinitely" is a misleading way to describe it, and if zero reads as "don't wait" to you it will read that way to users. Options, happy to take direction:
I'd lean towards 1 plus better docs, since the spec is cross-SDK, but you know the intent behind the wording better than I do. Which do you prefer?
Sorry, something went wrong.
There was a problem hiding this comment.
So, rephrasing it, we are saying:
This method will return immediately when the timeout is 0.
The time for the open feature initialized event itself is unbounded. It will be emitted when the SDK initializes or fails to initialize. If a timeout is provided, then an event will be emitted when the timeout lapses?
Or is it distinct from that?
Sorry, something went wrong.
There was a problem hiding this comment.
Close, with one correction on the first line — it's the constructor, not initialize, that returns immediately.
With Duration.ZERO:
With a positive duration, both layers get it: the SDK constructor blocks up to that long, and initialize additionally stops waiting when it lapses, sets provider state to ERROR, and throws — the OpenFeature SDK wraps that in a GeneralError and emits PROVIDER_ERROR. So yes, an event on timeout, but as a failure rather than a separate timeout signal.
One consequence worth a decision: the status listener stays registered after that throw, so if the data source becomes valid later, the provider still emits PROVIDER_READY even though initialization already failed. I think that's desirable — it's how the SDK recovers on its own — but it does mean the timeout bounds initialization, not the provider's lifetime. Say the word if you'd rather a lapsed timeout be terminal.
Sorry, something went wrong.
There was a problem hiding this comment.
Following up on this after 095f4ea, since the behavior I described has changed: a positive duration is now spent once rather than twice. The SDK constructor blocks for up to startWait, and initialize then reports whatever the outcome already is instead of starting its own timed wait — so a two second start wait can no longer add up to four seconds of waiting. Zero still means no provider-applied timeout, per OFP 4.3.4.2, and initialize waits until the data source is valid or permanently fails.
The consequence I flagged above is unchanged: the status listener stays registered after a failed initialization, so a data source that becomes valid later still emits PROVIDER_READY. Still happy to make a lapsed start wait terminal instead if that's what you'd prefer.
Sorry, something went wrong.
Co-Authored-By: rlamb@launchdarkly.com <4955475+kinyoklion@users.noreply.github.com>
Co-Authored-By: rlamb@launchdarkly.com <4955475+kinyoklion@users.noreply.github.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 70082f7. Configure here.
Sorry, something went wrong.
…rt wait Co-Authored-By: rlamb@launchdarkly.com <4955475+kinyoklion@users.noreply.github.com>
| Back | FazBrowse Home | New Git URL |
initialize blocked on CompletableFuture.get() with no timeout, so a provider whose data source never became valid and never permanently failed would wait forever.
Closes #58.
- Adds Provider(String sdkKey, LDConfig config, Duration startWait), which applies the duration to the SDK through LDConfig.startWait
- That duration bounds the whole of initialization once: the constructor blocks for up to that long, and initialize then reports whatever the outcome already is instead of waiting a second time
- If the client did not become ready in time, the provider goes to ERROR and initialize throws, distinct from the existing initialization-failure path
- A zero duration means no provider-applied timeout, per OFP 4.3.4.2: the constructor does not block and initialize waits until the data source is valid or permanently failed
- The existing constructors are unchanged in behavior: they leave the caller's LDConfig.startWait untouched, so a caller who configured startWait themselves is not overridden
- Parameter naming follows LDConfig.startWait rather than inventing a cross-provider name
Implementation detailsRequirements
Related issues
#58
Describe the solution you've provided
LDClient only exposes one- and two-argument constructors and LDConfig has no start-wait getter, so the three-argument constructor applies the duration through LDConfig.Builder.startWait(...) and keeps its own copy. Because the SDK constructor has already consumed that budget by the time initialize runs, initialize reads the completed state with getNow(false) rather than waiting again — a single wait, not two back-to-back ones.
The lack of a start-wait getter is also why the two-argument constructor stays indefinite rather than adopting a default: it cannot discover a start wait the caller configured, so defaulting would silently override it.
Describe alternatives you've considered
Making the existing constructors default to the SDK's five second start wait would have given every caller a timeout without an API change, but it overrides a startWait the caller set on their own LDConfig — which the README tells them to do — so it was rejected as a silent behavior change.
Having initialize run its own timed wait on top of the SDK's was the first implementation; it meant a caller passing a two second start wait could wait four seconds in total, so the provider now reuses the wait the SDK already performed.
Additional context
Testing: ./gradlew test checkstyleMain javadoc — all tests, checkstyle and javadoc pass. Lifecycle tests cover a positive start wait failing without waiting again against a data source that never becomes ready, a client that becomes ready during the start wait, a zero start wait waiting indefinitely, and the two-argument constructor preserving a configured start wait.
Link to Devin session: https://app.devin.ai/sessions/38a6eaf69fcf41109e136a1d0fe5e899
Open in Devin Desktop: https://app.devin.ai/desktop/session/38a6eaf69fcf41109e136a1d0fe5e899?variant=devin
Requested by: @kinyoklion
Note
Overview
Adds Provider(String, LDConfig, Duration) so callers can cap how long LaunchDarkly client startup blocks: the duration is applied via LDConfig.startWait, the constructor consumes that wait once, and initialize finishes from the current data-source state instead of blocking again.
When startWait is positive and the client never becomes ready in time, initialize sets ERROR and throws with a distinct timeout message (separate from permanent init failure). Duration.ZERO keeps indefinite waiting in initialize; the existing one- and two-argument constructors are documented to leave config startWait alone and still wait indefinitely at the provider layer. Wrapper injection is refactored through withWrapper, and lifecycle tests cover timeout-without-double-wait, success during wait, failure after wait, zero-duration indefinite wait, and preserving config start wait on the two-arg constructor.
Reviewed by Cursor Bugbot for commit 8862fe7. Bugbot is set up for automated code reviews on this repo. Configure here.