Skip to content

Fix broken WebSocket heartbeat implementation#1

Merged
geoffjay merged 2 commits intomainfrom
fix/websocket-heartbeat-td-fbb644
Feb 21, 2026
Merged

Fix broken WebSocket heartbeat implementation#1
geoffjay merged 2 commits intomainfrom
fix/websocket-heartbeat-td-fbb644

Conversation

@geoffjay
Copy link
Copy Markdown
Owner

Summary

This PR fixes the broken WebSocket heartbeat implementation in nemo-data/src/sources/websocket.rs that was identified in task td-fbb644.

Problem

The original heartbeat implementation had a critical flaw:

  • The heartbeat task would tick a timer and clone the message but never actually send it
  • The WebSocket write handle was moved to the message reading loop, making it inaccessible to the heartbeat task
  • This resulted in a CPU-burning no-op loop that provided no functionality

Here's the problematic code (lines 137-144):

Some(tokio::spawn(async move {
    let mut timer = tokio::time::interval(interval);
    loop {
        timer.tick().await;
        // Note: This won't work as write is moved, simplified for now
        let _ = msg.clone();
    }
}))

Solution

This PR implements a proper channel-based architecture:

  1. Dedicated Write Task: A separate async task owns the WebSocket write handle and receives messages through a channel
  2. Heartbeat Task: Sends heartbeat messages through the channel at configured intervals
  3. Pong Responses: Also sent through the same channel for consistent message handling

Key Benefits:

  • Eliminates the no-op CPU-burning loop
  • Properly sends heartbeat messages as configured
  • Clean separation of concerns (read, write, heartbeat)
  • Maintains support for WebSocket ping/pong protocol
  • Graceful cleanup on disconnection

Changes

File Modified: crates/nemo-data/src/sources/websocket.rs

  • Added tokio::sync::mpsc::channel for coordinating outgoing messages
  • Created dedicated write task that owns the write handle
  • Updated heartbeat task to send messages through the channel
  • Updated ping handler to send pong responses through the channel
  • Added proper cleanup logic to drop channel and abort tasks

Testing

  • ✅ Code compiles successfully
  • ✅ All existing unit tests pass
  • ✅ No new dependencies required

Resolves

Fixes: td-fbb644

Made with Cursor

The WebSocket heartbeat task was broken - it would tick a timer and clone
the message but never actually send it. The write handle was moved to the
read loop, making it inaccessible to the heartbeat task.

This commit introduces a channel-based architecture where:
- A dedicated write task owns the WebSocket write handle
- The heartbeat task sends messages through a channel to the write task
- Pong responses also use the same channel for proper handling

This eliminates the CPU-burning no-op loop and provides a clean separation
of concerns between reading, writing, and heartbeat functionality.

Fixes: td-fbb644
Co-authored-by: Cursor <cursoragent@cursor.com>
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 18, 2026

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

@geoffjay geoffjay merged commit ea03c7e into main Feb 21, 2026
4 of 6 checks passed
@geoffjay geoffjay deleted the fix/websocket-heartbeat-td-fbb644 branch February 22, 2026 21:39
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