soju manages two types of connections:
On startup, soju will iterate over the list of networks stored in the database and try to open an upstream connection for each network.
In order to correctly send history to each downstream client, soju maintains for each upstream channel a single-producer multiple-consumer ring buffer. The network's upstream connection produces messages and multiple downstream connections consume these messages. Each downstream client may have a different cursor in the history: for instance a client may be 10 messages late while another has consumed all pending messages.
Each type of connection has two dedicated goroutines: the first one reads incoming messages, the second one writes outgoing messages.
Each user has a dedicated goroutine responsible for dispatching all messages. It communicates via channels with the per-connection reader and writer goroutines. This allows to keep the dispatching logic simple (by avoiding any race condition or inconsistent state) and to rate-limit each user.
The user dispatcher goroutine receives from the
user.events channel. Upstream
and downstream message handlers are called from this goroutine, thus they can
safely access both upstream and downstream state.