Commit 373eac5
fix: Do not emit a redundant ready event during initialization (#63)
Stops the provider from emitting a second `PROVIDER_READY` event during
initialization, and makes
`LifeCycleTest.itCanHandleClientThatIsNotInitializedImmediately` wait
for the event instead of racing it.
- The provider emitted ready from its data source status listener while
`initialize` was still running, and the OpenFeature SDK emits its own
ready event when initialization succeeds — consumers got two.
- The test only passed because it asserted the counter before the second
event was dispatched; adding a wait made it fail `expected: <1> but was:
<2>` 25/25 times, which is how the double emit surfaced.
- Ready events for later recoveries (stale/error back to valid) are
unaffected — only the emit that happens while `initialize` is in flight
is suppressed.
- Under CPU load, `LifeCycleTest` now passes 25/25; before this branch
it failed 9/25 with `expected: <1> but was: <0>`.
<details>
<summary>Implementation details</summary>
**Requirements**
- [x] I have added test coverage for new or changed functionality
- [x] I have followed the repository's [pull request submission
guidelines](../blob/main/CONTRIBUTING.md#submitting-pull-requests)
- [x] I have validated my changes against all supported platform
versions
**Mechanism**
`initialize` sets an `initializing` flag for the duration of the call.
In the `VALID` branch of `handleDataSourceStatus` the flag is read
*before* completing the initialization future, so the initialize thread
cannot clear it between the read and the emit:
```java
if (emit) {
boolean duringInitialization = initializing;
completer.complete(true);
if (!duringInitialization) {
emitProviderReady(...);
}
}
```
`setProviderAndWait` also returns before API-level handlers have run,
since the OpenFeature SDK dispatches events on its own executor; the
test now waits on a `CompletableFuture` (1s timeout) and then asserts
the count after a short grace period so a duplicate emit is still
caught.
**Alternatives rejected**
Dropping the exact-count assertion from the test — it hides exactly the
duplicate-emit bug this change fixes.
**Testing**
`./gradlew test --tests '*LifeCycleTest*' --rerun-tasks` repeated 25x
with six busy loops pinning the CPUs, and the full suite once: all
green. On `main` the same loop failed 9/25 (and 5/20 in an earlier
round). CI on this branch was also re-triggered 11 times with empty
commits, all green, before the provider change.
**Related**
Found while investigating #32 (debug output for a flaky
`itEmitsReadyEvents`); that flake did not reproduce in ~75 runs and its
debug prints are superseded by #59, so #32 looks closable.
</details>
Link to Devin session:
https://app.devin.ai/sessions/3c9b094de4f84a0f82ba266098e823e9
Open in Devin Desktop:
https://app.devin.ai/desktop/session/3c9b094de4f84a0f82ba266098e823e9?variant=devin
Requested by: @kinyoklion
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Overview**
> Fixes **double `PROVIDER_READY` events** when the LaunchDarkly data
source becomes valid while `initialize` is still running. The
OpenFeature SDK already emits ready on successful initialization; the
provider was also calling `emitProviderReady` from the data-source
status listener, so listeners could see two events.
>
> An **`initializing` flag** wraps the wait in `initialize` (cleared in
`finally`). On transition to `VALID`, the provider still completes
initialization and updates state, but **skips `emitProviderReady` while
`initializing` is true**. Recoveries after init (e.g. stale/error back
to valid) still emit ready as before.
>
> **Tests:** `itCanHandleClientThatIsNotInitializedImmediately` now
waits for the ready event and rechecks the count after a short delay to
catch races. A new case uses a controllable data source to assert that
**failed init followed by `VALID` still produces exactly one ready
event**.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f8a6052. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>1 parent 1f35bc8 commit 373eac5
2 files changed
Lines changed: 102 additions & 6 deletions
File tree
- src
- main/java/com/launchdarkly/openfeature/serverprovider
- test/java/com/launchdarkly/openfeature/serverprovider
Lines changed: 26 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
| 54 | + | |
| 55 | + | |
54 | 56 | | |
55 | 57 | | |
56 | 58 | | |
| |||
152 | 154 | | |
153 | 155 | | |
154 | 156 | | |
| 157 | + | |
155 | 158 | | |
156 | 159 | | |
157 | 160 | | |
| |||
164 | 167 | | |
165 | 168 | | |
166 | 169 | | |
| 170 | + | |
167 | 171 | | |
168 | 172 | | |
169 | 173 | | |
170 | | - | |
171 | | - | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
172 | 181 | | |
173 | 182 | | |
174 | 183 | | |
| |||
189 | 198 | | |
190 | 199 | | |
191 | 200 | | |
| 201 | + | |
192 | 202 | | |
193 | 203 | | |
194 | 204 | | |
195 | 205 | | |
196 | 206 | | |
197 | | - | |
198 | | - | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
199 | 211 | | |
200 | 212 | | |
201 | 213 | | |
202 | | - | |
| 214 | + | |
203 | 215 | | |
204 | | - | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
205 | 219 | | |
206 | 220 | | |
207 | 221 | | |
| |||
218 | 232 | | |
219 | 233 | | |
220 | 234 | | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
221 | 241 | | |
222 | 242 | | |
223 | 243 | | |
| |||
Lines changed: 76 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
107 | 130 | | |
108 | 131 | | |
109 | 132 | | |
| |||
219 | 242 | | |
220 | 243 | | |
221 | 244 | | |
| 245 | + | |
222 | 246 | | |
223 | 247 | | |
224 | 248 | | |
| 249 | + | |
225 | 250 | | |
226 | 251 | | |
227 | 252 | | |
228 | 253 | | |
229 | 254 | | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
230 | 258 | | |
231 | 259 | | |
232 | 260 | | |
| |||
260 | 288 | | |
261 | 289 | | |
262 | 290 | | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
263 | 339 | | |
264 | 340 | | |
265 | 341 | | |
| |||
0 commit comments