SSE Time Limits?

Hi, I’m a little confused about how to implement server-sent events on Vercel. I’ve seen conflicting info as to whether the time limit of 10 seconds for a server request on free plans is applicable to backend endpoints with event-streams that send server-sent events. I am running a longer running task on trigger.dev and using an endpoint to subscribe to that task and send a server-sent event back to the frontend when the task finishes. It works perfectly fine in development, but when deployed to vercel, I just see logs for events that happen in the first few seconds, then don’t see any logs beyond that, and the completion event never gets sent to the front-end. I tried adding a heartbeat/keepalive to see if that helped but it didn’t. Is this because the 10-second time limit is enforced for server-sent event endpoints as well? Below is my code for the SSE endpoint: logging events show up in vercel for the task being queued and when it starts executing, but not when it is completed. I’m also getting a log for the first heartbeat interval, but not beyond that. Any help would be appreciated. Or if there is a way to detect when Vercel is going to end the connection and alert the frontend so it can re-establish it. I’m willing to upgrade to the paid plan, but want to make sure that this is the actual issue first. And still, the 60-second limit won’t be enough for my full task to execute.

Thanks for any help!

import { eventHandler, setHeader } from 'h3'
import { runs } from "@trigger.dev/sdk/v3"

export default eventHandler(async (event) => {
  // Set headers for SSE
  console.log('subscribing to run', event.context.params.id)
  setHeader(event, 'Content-Type', 'text/event-stream')
  setHeader(event, 'Cache-Control', 'no-cache')
  setHeader(event, 'Connection', 'keep-alive')

  const id = event.context.params.id
  
  try {
    // Create a write function to send SSE
    const write = (data) => {
      console.log('Attempting to write SSE data:', data);
      try {
        event.node.res.write(`data: ${JSON.stringify(data)}\n\n`);
        console.log('Successfully wrote SSE data');
      } catch (writeError) {
        console.error('Error writing SSE data:', writeError);
      }
    }

    // Setup heartbeat interval
    const heartbeat = setInterval(() => {
      try {
        console.log('Sending heartbeat');
        event.node.res.write(': keepalive\n\n');
      } catch (error) {
        console.error('Error sending heartbeat:', error);
      }
    }, 5000); // Reduced to 5 seconds for testing
    console.log('Heartbeat interval established');

    // Use the documented subscribeToRun method
    console.log(`Starting subscription to run ${id}`);
    for await (const data of runs.subscribeToRun(id, {
      apiKey: process.env.TRIGGER_API_KEY
    })) {
      console.log('Received run data for ' + id, JSON.stringify(data));
      write({ 
        status: data.status, 
        output: data.output 
      })

      // End connection if job is complete
      if (data.status === 'COMPLETED' || data.status === 'FAILED') {
        clearInterval(heartbeat); // Clear heartbeat interval
        console.log(`Run ${id} finished with status: ${data.status}`);
        event.node.res.end();
        console.log('Connection ended successfully');
        break;
      }
    }

    // Clean up if client disconnects
    event.node.req.on('close', () => {
      clearInterval(heartbeat); // Clear heartbeat interval
      console.log(`Client disconnected from run ${id}`);
    });
  } catch (error) {
    console.error('Error in job stream handler:', error);
    try {
      event.node.res.end(`data: ${JSON.stringify({ error: error.message })}\n\n`);
      console.log('Sent error to client');
    } catch (sendError) {
      console.error('Error sending error to client:', sendError);
    }
  }
})

Hello,

Unless you are implementing Streaming , the Serverless Function timeout will indeed be 10 seconds on Hobby plan: Vercel Functions Limits

I thought I was implementing streaming, by setting the Content-Type to text/event-stream, no? Both anthropic and gemini suggested similar approaches for establishing a streaming connection for SSE with nuxt on vercel. I know AI isn’t always reliable in its responses, but this is what Gemini told me:

No, the event stream itself is not directly limited by Vercel’s 10-second (or 60-second configurable maximum) Hobby plan function execution timeout once the SSE connection is established.

Here’s a breakdown:

  1. Initial Request: The initial HTTP request to set up the SSE connection (the request that your browser makes to /api/sse in the previous example) is subject to the function timeout. This means your function must establish the SSE connection within that time limit.
  2. Persistent Connection: After the SSE connection is successfully established, it becomes a persistent, long-lived connection. The function that initiated the connection doesn’t necessarily need to keep running. Vercel’s infrastructure handles keeping the connection open.
  3. Updates: When your server (or some other process) needs to send an update via the SSE stream, it can “wake up” the function (or trigger another function) to push the data. This new function execution is again subject to the timeout limit, but it only needs to be active long enough to send the update.
  4. Idling and Cold Starts: The SSE connection can still be interrupted due to function idling or cold starts. If the function that’s responsible for sending updates becomes idle, Vercel might scale it down. When a new update needs to be sent, the function has to spin up again (cold start), which introduces latency and might temporarily interrupt the stream.

In summary:

The initial connection is subject to the function timeout. The ongoing SSE stream itself is not directly tied to that timeout. However, sending updates via the SSE stream requires function executions that are again subject to the timeout. The connection can be affected by idling and cold starts.

Therefore, while you won’t have the stream cut off after 10 seconds, it’s not a guarantee of a completely uninterrupted stream due to the potential for cold starts when sending updates. Heartbeats and robust client-side reconnection logic are essential for reliable SSE on Vercel’s Hobby plan. If you need absolutely guaranteed long-lived connections without any interruptions, consider Vercel Edge Functions or a different hosting platform.

I am not sure if Nuxt.js fully compatible with Streaming yet. But you can get started with our Getting Started: Nuxt which will help you to understand concept of Foundations: Streaming

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.