broadcaster.broadcast() blocking send lets one stalled subscriber freeze Register/Unregister for everyone
- Dominant language
- Go
- Stars
- 223
- Forks
- 225
- Avg merge
- 7d 1h
- Merged PRs (30d)
- 1
Description
In `utils/broadcast/broadcaster.go`, `broadcast()` sends to each registered channel with a plain blocking `ch <- m`:
```go
func (b *broadcaster) broadcast(m BroadcastMessage) {
for ch := range b.outputs {
ch <- m
}
}
```
`Register`, `Unregister` and `Submit` all funnel through the same single `run()` goroutine (the `select` in `run`). If any one registered subscriber stops draining its channel, `broadcast()` blocks forever on that send, which wedges `run()`, which means every other subscriber stops receiving messages and every subsequent `Register`/`Unregister` call blocks indefinitely too - not just for the stalled subscriber, for the whole broadcaster.
This pattern is inherited from the reference implementation this file's header cites (`github.com/dustin/go-broadcast`) - upstream's `broadcast()` has the identical unprotected send, so it isn't a meshkit-introduced regression, but it's a real gap in an exported, shared-library type that any consumer of `Broadcaster` can hit.
Note on current reach: I checked `meshery/meshery`'s server and its GraphQL subscriptions currently use their own separate channels, not `Broadcaster.Register`, so this isn't actively triggered in that codepath today - but the exported type ships the bug for any current or future caller (adapters, other services) that does call `Register`.
Reproduced on `origin/master` (cf39c57): a test registering a slow (never-drained) and a fast subscriber, submitting one message, and asserting the fast subscriber still gets it and a follow-up Register/Unregister each return within 2s. Fails on current master (fast subscriber times out); passes with a `select`+`default` fix in `broadcast()`.
Will open a PR shortly with the fix + regression test.
Contributor guide
Research direction
Start in utils/broadcast/broadcaster.go, focusing on broadcast() and the run() goroutine that handles Register, Unregister, and Submit. Reproduce the stalled and fast subscriber scenario described in the issue, then add a regression test showing that the fast subscriber and follow-up registration operations still complete; run the Go tests to verify the behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 75/100