2020-03-27 18:49:55 +00:00
|
|
|
# soju architecture
|
|
|
|
|
|
|
|
soju manages two types of connections:
|
|
|
|
|
|
|
|
- Upstream connections: soju maintains persistent connections to
|
|
|
|
user-configured IRC servers
|
|
|
|
- Downstream connections: soju accepts connections from IRC clients
|
|
|
|
|
|
|
|
On startup, soju will iterate over the list of networks stored in the database
|
|
|
|
and try to open an upstream connection for each network.
|
|
|
|
|
|
|
|
## Ring buffer
|
|
|
|
|
|
|
|
In order to correctly send history to each downstream client, soju maintains
|
2020-04-17 16:29:49 +00:00
|
|
|
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.
|
2020-03-27 18:49:55 +00:00
|
|
|
|
|
|
|
## Goroutines
|
|
|
|
|
|
|
|
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.
|