Joseph Doherty 282a0e70ad fix(GWC-34): fail an attach that races session disposal instead of hanging it
EnsureDistributorCreated read a null _eventDistributor as "not created yet",
which is true before MarkReady and false after DisposeAsync nulled it. An
attach that passed its readiness gate while the session was still Ready and
then lost the CPU across the whole close + dispose therefore built a
REPLACEMENT distributor: startNow came back false because the pump-start flag
is a one-way latch, so the subscriber registered on a distributor whose pump
never starts and whose channel is never completed, and EventStreamService
blocked in WaitToReadAsync until the client's own deadline — no error, no log,
no metric, the backlog gauge reporting 0 forever. This is the disposal-side
twin of the premature-attach poison GWC-27 closed.

DisposeAsync now sets _eventDistributorDisposed under _syncRoot in the same
section that nulls the distributor, and EnsureDistributorCreated throws
SessionManagerException(SessionNotReady) when it is set. The attach paths'
existing catch/rollback releases the subscriber slot, so a second attempt
still reports SessionNotReady rather than EventSubscriberAlreadyActive.

StartDashboardMirror is the one caller that is not an attach: it abandons the
mirror (releasing its loop token source) instead of propagating, so MarkReady
stays total. Losing that race previously left a started orphan distributor
behind, so this path strictly improves too.

Test: AttachAfterDisposeThrowsSessionNotReadyInsteadOfRegisteringOnANeverPumped-
Distributor covers all three attach entry points plus the rollback, and fails
on the pre-fix code. docs/Sessions.md records the disposal end of the
one-distributor-per-session rule.
2026-08-20 09:25:21 -04:00
S
Description
No description provided
49 MiB
Languages
C# 48.4%
Java 40.1%
Python 3.2%
Rust 3%
Go 2.6%
Other 2.7%