Commit b704932
fix(realtime): cancel pending reconnect on disconnect after a dropped socket (#1477)
## What
Fixes three related problems in `RealtimeClient` around a socket that
dropped unexpectedly:
1. `disconnect()` now always cancels a pending reconnect, so
disconnecting after the socket has already dropped no longer silently
reopens the connection.
2. Automatic reconnects keep their exponential backoff instead of
resetting to the shortest delay on every attempt.
3. A manual `connect()` after an unexpected drop now actually reconnects
instead of being a no-op.
## Why
When the socket drops unexpectedly, `_onConnClose` sets `connState =
closed` and schedules a reconnect through
`reconnectTimer.scheduleTimeout()`. `conn` is not nulled on a drop, so a
later `disconnect()` still enters the `conn != null` branch, but
`oldState == closed` makes `shouldCloseSink` false, so the close block
that cancelled the timer was skipped. `conn` was nulled and the method
returned, yet the armed backoff timer still fired, ran `_reconnect()`
and reopened a connection the user explicitly closed.
Cancelling the timer on every `disconnect()` fixes that, but it cannot
use `reconnectTimer.reset()`: the reconnect path is `_reconnect ->
disconnect -> connect`, so `reset()` would zero the retry counter on
every attempt and defeat exponential backoff. During an outage the
client would hammer an unreachable server at the shortest interval
instead of backing off. A new `RetryTimer.cancel()` cancels the
scheduled timer without touching the retry counter, so a user disconnect
still stops a pending reconnect while automatic reconnects keep backing
off. The counter is still zeroed on a successful open through `reset()`
in `_onConnOpen`.
Finally, because `conn` is intentionally kept non-null after a drop so
`conn.closeCode` and `conn.closeReason` stay readable, the `if (conn !=
null) return` guard in `connect()` treated the dead channel as a live
connection and turned a manual `connect()` into a no-op. `connect()` now
only bails out when the connection is live or in progress. When `conn`
is set but `connState == closed`, it tears the stale connection down
through `disconnect()` first, which cancels the old stream subscription,
and then opens a fresh one.
## Not a breaking change
The public `conn` field is still left non-null after an unexpected drop,
so consumers reading `conn.closeCode` or `conn.closeReason` are
unaffected. `connState` transitions are unchanged: a socket that was
already `closed` still ends up `closed`, and an open socket still ends
up `disconnected` after `disconnect()`. `RetryTimer.reset()` keeps its
existing behavior and now delegates its cancel step to the new
`cancel()` method.
## Tests
Added three tests to the `disconnect` group:
- `cancels a pending reconnect after an unexpected drop`: drop the
socket so a reconnect is scheduled, then `disconnect()` and assert the
transport is never invoked again.
- `reconnects on a manual connect() after an unexpected drop`: drop the
socket, call `connect()` manually, and assert a fresh connection is
opened and `connState` becomes `open`.
- `grows the reconnect backoff across failed attempts`: fail the first
few connection attempts and assert the retry counter passed to
`reconnectAfterMs` grows as `1, 2, 3` instead of being reset to `1` on
every reconnect.
Each test fails without its corresponding fix.
---------
Co-authored-by: Lukas Klingsbo <lukas.klingsbo@gmail.com>1 parent ac579c6 commit b704932
3 files changed
Lines changed: 170 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
213 | 213 | | |
214 | 214 | | |
215 | 215 | | |
216 | | - | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
217 | 220 | | |
218 | 221 | | |
219 | 222 | | |
| |||
300 | 303 | | |
301 | 304 | | |
302 | 305 | | |
303 | | - | |
304 | 306 | | |
305 | 307 | | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
306 | 315 | | |
307 | 316 | | |
308 | 317 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
3 | 5 | | |
4 | 6 | | |
5 | 7 | | |
| |||
34 | 36 | | |
35 | 37 | | |
36 | 38 | | |
37 | | - | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
38 | 47 | | |
39 | 48 | | |
40 | 49 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
322 | 322 | | |
323 | 323 | | |
324 | 324 | | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
325 | 474 | | |
326 | 475 | | |
327 | 476 | | |
| |||
0 commit comments