Skip to content

fix(sse): cancel active streams on user logout#6073

Merged
clubanderson merged 1 commit intomainfrom
fix/6029-sse-cancellation
Apr 10, 2026
Merged

fix(sse): cancel active streams on user logout#6073
clubanderson merged 1 commit intomainfrom
fix/6029-sse-cancellation

Conversation

@clubanderson
Copy link
Copy Markdown
Collaborator

Fixes #6029. Recreated from #6033 after base branch auto-deletion closed the original.

Problem

SSE streaming endpoints (streamClusters in pkg/api/handlers/sse.go) run inside SetBodyStreamWriter callbacks that block until either the client disconnects or sseOverallDeadline (~30s) fires. When a user logged out, any in-flight SSE streams kept emitting cluster_data events until that deadline — the token was revoked but the already-established stream had no active cancellation path.

Fix

Mirrors the exec-session cancellation pattern added in #6031 for /ws/exec sessions (#6024).

  • New per-user SSE session registry in sse.go:
    • sseSessions map[uuid.UUID]map[int64]context.CancelFunc guarded by a plain sync.Mutex
    • registerSSESession / unregisterSSESession / CancelUserSSEStreams
    • Monotonic session-id generator so multiple concurrent streams for the same user are tracked independently
  • streamClusters captures the authenticated user ID before entering the deferred SetBodyStreamWriter callback, registers the stream context's cancel func, and unregisters on normal stream end.
  • auth.Logout calls CancelUserSSEStreams(claims.UserID) immediately after the existing CancelUserExecSessions call.
  • streamDemoSSE intentionally unchanged — demo streams are instant, no cancellation path needed.

Tests

New pkg/api/handlers/sse_test.go:

  • TestCancelUserSSEStreams_CancelsRegisteredContexts
  • TestCancelUserSSEStreams_OtherUserUnaffected
  • TestUnregisterSSESession_RemovesEntry
  • TestCancelUserSSEStreams_NoSessions

Verification

  • go build ./..., go vet ./..., go test ./pkg/api/handlers/..., cd web && npm run build — all clean.

Copilot AI review requested due to automatic review settings April 10, 2026 12:42
@kubestellar-prow kubestellar-prow Bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Apr 10, 2026
@kubestellar-prow
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign clubanderson for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@github-actions
Copy link
Copy Markdown
Contributor

👋 Hey @clubanderson — thanks for opening this PR!

🤖 This project is developed exclusively using AI coding assistants.

Please do not attempt to code anything for this project manually.
All contributions should be authored using an AI coding tool such as:

This ensures consistency in code style, architecture patterns, test coverage,
and commit quality across the entire codebase.


This is an automated message.

@kubestellar-prow kubestellar-prow Bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Apr 10, 2026
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 10, 2026

Deploy Preview for kubestellarconsole ready!

Name Link
🔨 Latest commit 93d7919
🔍 Latest deploy log https://app.netlify.com/projects/kubestellarconsole/deploys/69d8fee0d99f3c0008ad1df8
😎 Deploy Preview https://deploy-preview-6073.console-deploy-preview.kubestellar.io
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR ensures server-side SSE streams are actively terminated when a user logs out, preventing continued event delivery after JWT revocation and aligning SSE behavior with the existing exec-session cancellation lifecycle.

Changes:

  • Added a per-user SSE session registry in sse.go and exposed CancelUserSSEStreams(userID) to cancel all active streams for a user.
  • Registered/unregistered SSE stream cancel funcs in streamClusters so logout can promptly tear down in-flight streams.
  • Updated AuthHandler.Logout to cancel SSE streams after revoking the token, and added unit tests covering the registry lifecycle and cancellation behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
pkg/api/handlers/sse.go Adds per-user SSE stream cancellation registry and registers stream cancel funcs from streamClusters.
pkg/api/handlers/sse_test.go Adds unit tests validating cancellation, isolation across users, and unregister behavior.
pkg/api/handlers/auth.go Cancels active SSE streams for the logging-out user via CancelUserSSEStreams.

Comment thread pkg/api/handlers/sse.go
Comment on lines 240 to 245
// Create a cancellable context with the overall deadline so that all
// spawned goroutines are cancelled when the client disconnects or the
// deadline expires. Previously context.Background() was used, which
// caused goroutine leaks on client disconnect (see #3291).
streamCtx, streamCancel := context.WithTimeout(context.Background(), sseOverallDeadline)
defer streamCancel()
@clubanderson clubanderson force-pushed the fix/6029-sse-cancellation branch from ccc5dda to 6c93085 Compare April 10, 2026 13:29
Fixes #6029. SSE streaming endpoints (streamClusters in sse.go) ran
inside SetBodyStreamWriter callbacks that blocked for up to
sseOverallDeadline (~30s), so when a user logged out any in-flight
SSE streams continued to emit cluster_data events until the deadline
fired or the client disconnected.

Mirrors the exec session cancellation pattern added in #6031 for
/ws/exec sessions (#6024):

- Add a per-user SSE session registry in sse.go with
  registerSSESession / unregisterSSESession / CancelUserSSEStreams,
  guarded by a plain sync.Mutex.
- In streamClusters, capture the authenticated user ID before the
  deferred SetBodyStreamWriter callback runs (the fiber.Ctx may be
  reused by then) and register the stream context's cancel func in
  the registry, with a deferred unregister for normal stream end.
- In auth.Logout, after CancelUserExecSessions, call
  CancelUserSSEStreams(claims.UserID) so logout tears down any live
  SSE streams the user had open.
- Add sse_test.go with the four lifecycle tests mirroring
  exec_test.go: CancelsRegisteredContexts, OtherUserUnaffected,
  UnregisterSSESession_RemovesEntry, and NoSessions (no panic on
  empty map).

streamDemoSSE is left unchanged — demo streams are instant, they
do not block, so they need no cancellation path.

Signed-off-by: Andrew Anderson <[email protected]>
@clubanderson clubanderson force-pushed the fix/6029-sse-cancellation branch from 6c93085 to 93d7919 Compare April 10, 2026 13:45
@clubanderson clubanderson merged commit c1fcb46 into main Apr 10, 2026
28 of 30 checks passed
@kubestellar-prow kubestellar-prow Bot deleted the fix/6029-sse-cancellation branch April 10, 2026 13:50
@github-actions
Copy link
Copy Markdown
Contributor

Thank you for your contribution! Your PR has been merged.

Check out what's new:

Stay connected: Slack #kubestellar-dev | Multi-Cluster Survey

@github-actions
Copy link
Copy Markdown
Contributor

❌ Post-Merge Verification: failed

Commit: c1fcb46ddedfba050db8334bcd88f13347ca1fa3
Specs run: smoke.spec.ts
Report: https://github.com/kubestellar/console/actions/runs/24246256715

@github-actions
Copy link
Copy Markdown
Contributor

Post-merge build verification passed

Both Go and frontend builds compiled successfully against merge commit c1fcb46ddedfba050db8334bcd88f13347ca1fa3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dco-signoff: yes Indicates the PR's author has signed the DCO. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: sse connections remain active after logout

2 participants