Skip to content

SSE connection makes redundant probe fetch before opening EventSource #574

@brendanlong

Description

@brendanlong

This is related to #580 and a PR fixing this should target the tanstack-db branch.

Problem

In src/lib/hooks/useRealtimeUpdates.ts:666-714, the createConnection function makes a fetch() request to /api/v1/events to check for a 503 status (Redis down), then immediately cancels the response body and opens a second connection via EventSource to the same URL.

const createConnection = async () => {
  // First request: probe for 503
  const response = await fetch("/api/v1/events", {
    method: "GET",
    credentials: "include",
    headers: { Accept: "text/event-stream" },
  });

  if (response.status === 503) { /* switch to polling */ return; }
  if (!response.ok) { throw new Error(...); }

  // Cancel the first SSE stream we just opened
  response.body?.cancel();

  // Second request: actual EventSource connection
  const eventSource = new EventSource("/api/v1/events", { withCredentials: true });
  // ...
};

This means:

  1. The server opens an SSE connection, subscribes to Redis channels, sends the connection handshake
  2. The client immediately cancels that connection
  3. A second SSE connection is opened, repeating all the server-side setup

Impact

  • Two HTTP requests per connection attempt
  • Server does unnecessary work setting up and tearing down the probe connection
  • Brief window where the server has two open connections for the same user

Possible solutions

  1. Use EventSource directly and handle errors in onerror. EventSource fires onerror for non-200 responses. The challenge is distinguishing "Redis is down (503)" from other errors, since EventSource doesn't expose HTTP status codes.

  2. Use a lightweight health check endpoint (e.g., HEAD /api/v1/events/health or GET /api/v1/events?probe=true) that returns 200/503 without opening an SSE stream, then open EventSource only on success.

  3. Start with EventSource optimistically and switch to polling if it fails to connect after a short timeout or repeated errors. This is simpler and handles the common case (Redis is up) with a single request.

Option 2 or 3 would be cleanest.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions