Skip to content

fix(#1138): fix SSE EventSource leak and polling race condition#1174

Closed
ionfwsrijan wants to merge 2 commits into
utksh1:mainfrom
ionfwsrijan:fix/issue-1138-sse-polling-race
Closed

fix(#1138): fix SSE EventSource leak and polling race condition#1174
ionfwsrijan wants to merge 2 commits into
utksh1:mainfrom
ionfwsrijan:fix/issue-1138-sse-polling-race

Conversation

@ionfwsrijan

Copy link
Copy Markdown
Contributor

Problem

  1. SSE EventSource leak: On reconnect, the es.onerror handler could fire
    for the old EventSource after a new connection was established, triggering
    spurious reconnection loops and duplicate EventSources.
  2. Polling race: setInterval(loadTasks, 5000) in Scans.tsx doesn't await
    the async loadTasks(). If a request takes longer than 5s, overlapping
    requests pile up, potentially corrupting state.

Solution

  • Added a versionRef (generation counter) to useTaskSubscription.ts that
    increments on every connectSSE() call and every cleanupAll()
  • All SSE callbacks (status, phase, output, onerror, onopen) now check
    versionRef.current === expectedVersion before acting — stale callbacks from
    previous connections are silently ignored
  • In Scans.tsx, replaced setInterval with chained setTimeout:
    scheduleNextPoll() awaits loadTasks() before scheduling the next tick
  • Renamed intervalRefpollingTimerRef for clarity

Files changed

frontend/src/hooks/useTaskSubscription.ts, frontend/src/pages/Scans.tsx

Closes #1138

- Add version/generation counter in useTaskSubscription to prevent stale
  EventSource callbacks from triggering after reconnection
- Check versionRef.current against expected version in all SSE event handlers,
  onerror, and onopen to ignore callbacks from previous connections
- Replace setInterval in Scans.tsx with chained setTimeout that awaits
  loadTasks() before scheduling the next poll
- Use AbortController signal to gate polling continuation
- Rename intervalRef to pollingTimerRef for clarity
…eout

The chained setTimeout pattern calls poll() once immediately on start,
unlike setInterval which waits for the first interval. Update the test
assertions to reflect the correct call counts.
@ionfwsrijan

Copy link
Copy Markdown
Contributor Author

@utksh1 Please review this

@utksh1 utksh1 added type:bug Bug fix work category bonus label level:beginner 20 pts difficulty label for small beginner-friendly PRs labels Jun 23, 2026
@utksh1

utksh1 commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Closing this because it now conflicts with main after recent merges. Please rebase/update the branch and open a fresh PR if the change is still needed.

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

Labels

gssoc:invalid Admin validation: invalid for GSSoC scoring level:beginner 20 pts difficulty label for small beginner-friendly PRs type:bug Bug fix work category bonus label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] SSE Connection Leak in useTaskSubscription Combined with Polling Race in Scans Page

2 participants