Skip to content

Conversation

@tanmaysharma2001
Copy link
Contributor

🔍 Description

This PR fixes a memory leak in RealtimeClient when using the worker: true option. Web Workers were being created during connection but never properly cleaned up during disconnection, leading to memory accumulation over time.

What changed?

  1. Added _terminateWorker() private method

    • Terminates the Web Worker when called
    • Clears the workerRef reference for garbage collection
    • Includes logging for debugging
  2. Updated _teardownConnection() method

    • Now calls _terminateWorker() during connection teardown
    • Ensures workers are properly cleaned up on disconnect
  3. Added test coverage

    • New test verifies workers are terminated on disconnect
    • Ensures workerRef is cleared after termination

Why was this change needed?

When using RealtimeClient with worker: true, each disconnect/reconnect cycle would create a new Web Worker without terminating the previous one. This caused:

  • Memory accumulation with orphaned workers
  • Workers continuing to send heartbeat messages even when disconnected
  • Potential performance degradation over time with multiple reconnections

The fix ensures proper worker lifecycle management by terminating workers during connection teardown.

Closes #1902

📸 Screenshots/Examples

Before: Workers were created but never terminated

// _teardownConnection() - line 628-649 (before fix)
private _teardownConnection(): void {
  // ... cleanup code
  this._clearAllTimers()
  this.channels.forEach((channel) => channel.teardown())
  // Worker never terminated - memory leak!
}

**After**: Workers are properly cleaned up
```typescript
// _teardownConnection() - line 628-650 (after fix)
private _teardownConnection(): void {
  // ... cleanup code
  this._clearAllTimers()
  this._terminateWorker()  // Worker properly terminated
  this.channels.forEach((channel) => channel.teardown())
}

🔄 Breaking changes

  • [☑️] This PR contains no breaking changes

📋 Checklist

  • [☑️] I have read the Contributing Guidelines
  • [☑️] My PR title follows the conventional commit format: <type>(<scope>): <description>
  • [☑️] I have run npx nx format to ensure consistent code formatting
  • [☑️] I have added tests for new functionality (if applicable)
  • [☑️] I have updated documentation (if applicable)

📝 Additional notes

Web Workers were created in _startWorkerHeartbeat() but never terminated
in _teardownConnection(), causing memory accumulation with each
disconnect/reconnect cycle.

This fix adds a _terminateWorker() private method that properly terminates
the Web Worker and clears the reference, then calls it during connection
teardown.

Fixes supabase#1902
@tanmaysharma2001 tanmaysharma2001 requested review from a team as code owners December 3, 2025 01:01
@coveralls
Copy link

coveralls commented Dec 3, 2025

Coverage Status

coverage: 95.367% (+14.2%) from 81.183%
when pulling 0949fc0 on tanmaysharma2001:fix/realtime-worker-memory-leak
into 8d1e0c5 on supabase:master.

@tanmaysharma2001
Copy link
Contributor Author

Hi @filipecabaco, I wanted to ask regarding the Label Issues and PRs check. I am not completely sure why is it failing. Is it something i need to fix from my side? Please let me know. Thank you 🙏

@mandarini
Copy link
Contributor

@tanmaysharma2001 don't worry about this. I think sometimes it fails when you do not accept edits from maintainers, but don't worry about it. maybe i will remove this workflow in the future, if it fails consistently.

Copy link
Contributor

@mandarini mandarini left a comment

Choose a reason for hiding this comment

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

Great fix! This correctly addresses the memory leak by terminating workers on disconnect.

One related issue: the error handler in _startWorkerHeartbeat() has the same problem. If a worker errors out, workerRef isn't cleared, so on reconnect no new worker gets created. Could you update the error handler to use your new method?

  this.workerRef.onerror = (error) => {
    this.log('worker', 'worker error', (error as ErrorEvent).message)
    this._terminateWorker()
  }

@mandarini mandarini self-assigned this Dec 3, 2025
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.

Web Worker memory leak on disconnect

3 participants