Commit 6695b5f
committed
fix(workspace-client): unwatchGit/unwatchFs can strand a fresh, unretained connection
Found by a review of #6856's cleanup-effect ordering, but pre-existing —
affects fs:watch too, and predates both #6848 and #6856.
getEventBus(hostUrl) unconditionally (re)creates a ConnectionState if none
exists in the shared `connections` map. unwatchGit/unwatchFs/unwatchFsFile
never called maybeCleanupConnection, unlike on()'s and retain()'s cleanups
— so a caller that only ever intends to *release* interest (never establish
it) could, if the connection's last retainer/listener already tore it down
moments earlier, silently mint a brand-new WebSocket connection just to
send an unwatch command, then leave it dangling forever: nothing else would
ever call maybeCleanupConnection for it again.
This is a real, reachable ordering hazard in a multi-effect component like
DashboardSidebarWorkspaceStatusProvider: React runs effect cleanups in
declaration order (not reversed), so a "listener registration" effect's
cleanup releasing a connection's last retain/listener, followed by a
separate "release git-watch interest" effect's cleanup (or body, on a
mid-session workspace removal) calling unwatchGit for the same host, hits
this exactly.
Fix: unwatchGit/unwatchFs/unwatchFsFile now call maybeCleanupConnection
after releasing their own interest, mirroring on()'s and retain()'s
cleanups. Verified with a real WS server (no mocks): before the fix, a
release-only call after the connection's teardown opens a second real
upgrade that never closes; after the fix, maybeCleanupConnection fires
synchronously and fast enough that the stray connection never even dials
out — confirmed via a standalone diagnostic script before writing the
regression test, so the test's "no upgrade" assertion isn't guesswork.
Claude-Session: https://claude.ai/code/session_01NmLFihnhebmL9bbbojGYCR1 parent dff3d45 commit 6695b5f
2 files changed
Lines changed: 49 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
148 | 148 | | |
149 | 149 | | |
150 | 150 | | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
151 | 186 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
480 | 480 | | |
481 | 481 | | |
482 | 482 | | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
483 | 491 | | |
484 | 492 | | |
485 | 493 | | |
| |||
498 | 506 | | |
499 | 507 | | |
500 | 508 | | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
501 | 512 | | |
502 | 513 | | |
503 | 514 | | |
| |||
526 | 537 | | |
527 | 538 | | |
528 | 539 | | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
529 | 543 | | |
530 | 544 | | |
531 | 545 | | |
| |||
0 commit comments