dagucloud / dagucloud/dagu

bug: SSE stream closes when a DAG details payload exceeds write_buffer_size

Closed
#2,765 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
4k
Forks
332
Avg merge
19h 26m
Merged PRs (30d)
133

Description

## Describe the bug

The multiplexed SSE stream closes when a single DAG-details payload exceeds `sse.write_buffer_size`.

This makes the Dagu UI stale: the backend persists the new run state correctly, but the browser no longer receives live updates. A manual Refresh works because it performs a REST request.

The issue is independent of sub-DAG execution, approvals, or reverse-proxy buffering. Those merely exposed the problem in this case.

## To Reproduce

1. Start Dagu `v2.16.4` with the default configuration.
2. Use a DAG whose details endpoint response is larger than the default SSE write buffer (`65,536` bytes).
3. Subscribe to its generic DAG SSE topic:

```text
dag:
```

4. Observe that the stream receives its initial `control` event but no `message` containing the DAG details. Subsequent updates on the same multiplexed stream do not reach the UI.
5. Restart Dagu with a larger buffer:

```bash
DAGU_SSE_WRITE_BUFFER_SIZE=131072 dagu start-all
```

6. Subscribe again. The DAG-details `message` and subsequent live updates are delivered.

## Expected behavior

A large DAG-details response should not make the entire multiplexed SSE connection unusable.

At minimum, the server should not silently close the session in a way that prevents unrelated run-specific updates from reaching the browser.

## Actual behavior

With the default `sse.write_buffer_size`:

| Workflow | `GET /api/v1/dags/` response | Result |
|---|---:|---|
| Small reproduction workflow | `5,136` bytes | SSE updates work |
| Large real workflow | `67,328` bytes | SSE stream closes; UI stays stale |

The default buffer is `65,536` bytes. The SSE message also adds a small wrapper around the payload, so:

```text
5,136 + SSE wrapper < 65,536 -> stream remains usable
67,328 + SSE wrapper > 65,536 -> stream is closed
```

In `v2.16.4`, [`streamSession.enqueueMessage()`](https://github.com/dagucloud/dagu/blob/v2.16.4/internal/service/frontend/sse/multiplex.go) calculates the message size and closes the session when it exceeds `writeBufferSize`:

```go
size := len(data) + 64
if size > s.writeBufferSize {
s.closed = true
return false
}
```

The generic `dag:` topic uses the full DAG-details response, including the raw specification and related metadata. Since the stream is multiplexed, closing it also prevents `dagrun` and `subdagrun` updates from reaching the UI.

## Environment

- Dagu: `v2.16.4`
- OS: Debian GNU/Linux 12, x86_64
- Dagu server: `127.0.0.1:8080`
- Authentication: built-in auth
- Hosted behind a reverse proxy and a sub-path
- The same behavior is reproduced directly by comparing payload sizes; proxy behavior is not required to explain it.

## DAG configuration

The workflow content is not relevant to the root cause. The deciding factor is the size of the response returned by:

```text
GET /api/v1/dags/
```

Any DAG definition large enough to make this response exceed `sse.write_buffer_size` can trigger the problem.

## Screenshots

Not included. The issue is demonstrated by the response-size measurements and by the SSE stream behavior before and after increasing the buffer.

## Additional context

A working configuration workaround is:

```bash
DAGU_SSE_WRITE_BUFFER_SIZE=131072 dagu start-all
```

or:

```yaml
sse:
write_buffer_size: 131072
```

This was tested successfully with the `67,328`-byte workflow.

Possible fixes:

- avoid sending the complete DAG-details document over the generic live topic;
- split or reduce large SSE payloads;
- handle an oversized topic payload without closing the complete multiplexed session;
- document `DAGU_SSE_WRITE_BUFFER_SIZE` and its practical sizing requirements.

No JWT, HAR archive, or screenshots are included.

Contributor guide

Open the contributing guide

Research direction

Start in internal/service/frontend/sse/multiplex.go, especially streamSession.enqueueMessage(), and reproduce with a DAG-details payload larger than sse.write_buffer_size. Trace how an oversized message affects the multiplexed session; done means large DAG details no longer make the session unusable and unrelated dagrun or subdagrun updates continue reaching the UI.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.