Skip to content

fix: harden ClientGrain.OnDisconnect against stream errors and reentrancy#170

Open
d-barker wants to merge 1 commit into
OrleansContrib:masterfrom
d-barker:fix/client-grain-disconnect-resilience
Open

fix: harden ClientGrain.OnDisconnect against stream errors and reentrancy#170
d-barker wants to merge 1 commit into
OrleansContrib:masterfrom
d-barker:fix/client-grain-disconnect-resilience

Conversation

@d-barker

Copy link
Copy Markdown

Summary

Makes ClientGrain.OnDisconnect resilient so a single stream error can no longer abort the whole disconnect-cleanup sequence.

Two failure modes are addressed:

  • Stale subscription handle. UnsubscribeAsync() on the stored server-disconnected handle could throw Handle is no longer valid. It has been used to unsubscribe or resume. when the subscription had already been torn down on the producer side. This previously aborted cleanup. It is now caught and logged, so the rest of OnDisconnect (publishing the client-disconnect event, clearing state, deactivating) still runs.
  • Unguarded stream publish. The client-disconnection OnNextAsync publish had no error handling; a transient stream/provider error would likewise abort cleanup. It is now wrapped in try/catch and logged.

Additionally, the handle is captured to a local and the field cleared before awaiting UnsubscribeAsync(). Send is marked [ReadOnly] (interleaved), so nulling the field first prevents an interleaved call from observing the same handle and double-unsubscribing it.

Notes

🤖 Generated with Claude Code

…ancy

OnDisconnect could throw and abort disconnect cleanup in two ways:

- UnsubscribeAsync() on the stored server-disconnected handle could throw
  "Handle is no longer valid. It has been used to unsubscribe or resume."
  when the subscription had already been torn down. The exception is now
  caught and logged so the rest of cleanup (publishing the client-disconnect
  event, clearing state, deactivating) still runs.

- The client-disconnection stream publish (OnNextAsync) was unguarded; a
  transient stream/provider error would likewise abort cleanup. It is now
  wrapped in try/catch and logged.

Additionally, the subscription handle is captured to a local and the field
cleared *before* awaiting UnsubscribeAsync(). Send is marked [ReadOnly]
(interleaved), so nulling the field first prevents an interleaved call from
observing the same handle and double-unsubscribing it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant