Skip to content

Distributed PubSub using Consistent Hashing and Erlang's Process Group

License

Notifications You must be signed in to change notification settings

svsool/distributed_pubsub

Repository files navigation

Distributed PubSub

This is a proof-of-concept implementation of Distributed PubSub using Distributed Process Group, GenServer, Consistent Hash Ring, Manifold, and Channels. Scalable PubSub layer is an essential component of message delivery in chat applications and beyond.

Ring used to determine Topic's Node, subscribe to Topic's GenServer, and fan out messages to subscribers, subscriber PIDs are grouped by their remote node to reduce number of send/2 calls.

Direct message publishing helps mitigate the Bandwidth Bisection problem, which the default broadcast PubSub Adapter is more prone to, since number of messages grows quadratically with the number of nodes in the cluster when using broadcast.

Topic is roughly equivalent to a Guild mentioned in How Discord Scaled Elixir to 5,000,000 Concurrent Users.

NB Production ready system would involve dynamic topology management, replication, netsplit recovery, stricter delivery semantics, message queueing, overload protection and other things.

Architecture

  +--------------+
  | Message Flow |
  +--------------+


  Client 1 --(Topic 1)-->  WS Server 1  -->  Topic Server 1  --------+
                                                                     |
                                                                     |
  Client 2 <--(Topic 1)--  WS Server 2  <--  Topic 1 (GenServer) ----+


                                ProcessGroup
                         +------------------+
  +------------+ Topic 1 | +--------------+ |           GenServers
  |  Client 1  |---------| | WS Server 1  | |  +-----------------+
  +------------+         | +-------|------+ |  | +-------------+ |
                         |         |        |  | |   Topic 1   |-----+
                         | +-------+------+ |  | +-------------+ |   |
                         | |Topic Server 1|----+ +-------------+ |   |
                         | +--------------+ |  | |   Topic X   | |   |
                         |  |            |  |  | +-------------+ |   |
                         |  |            |  |  +-----------------+   |
                         |  +(Hash Ring) +  |  +-----------------+   |
                         |  |            |  |  | +-------------+ |   |
                         |  |            |  |  | |   Topic X   | |   |
                         | +--------------+ |  | +-------------+ |   |
                         | |Topic Server X|----+ +-------------+ |   |
                         | +--------------+ |  | | Topic X + 1 | |   |
                         |                  |  | +-------------+ |   |
  +------------+ Topic 1 | +--------------+ |  +-----------------+   |
  |  Client 2  |+--------|-- WS Server 2  |+-------------------------|
  +------------+         | +--------------+ |
                         +------------------+

Getting started

# relevant for macos
brew install asdf wxwidgets

# install erlang and elixir
asdf plugin add elixir
asdf install elixir 1.17.1-otp-27
asdf plugin add erlang
asdf install erlang 27.0

# install dependencies
mix deps.get

Run cluster

Application can be started in multiple modes: ws (websocket) and ts (topic server).

# start websocket servers
DPS_PORT=4000 DPS_APP=ws iex --sname dps-a -S mix phx.server
DPS_PORT=4001 DPS_APP=ws iex --sname dps-b -S mix phx.server

# start topic servers
DPS_APP=ts iex --sname dps-ts-a -S mix
DPS_APP=ts iex --sname dps-ts-b -S mix

Ring is static for demonstration purpose, and can be adjusted in config/dev.exs given cluster changes.

Usage

# terminal 1
websocat "ws://127.0.0.1:4000/socket/websocket?vsn=2.0.0"
["1", "1", "topics:matrix", "phx_join", {}]

# terminal 2
websocat "ws://127.0.0.1:4001/socket/websocket?vsn=2.0.0"
["1", "1", "topics:matrix", "phx_join", {}]
["1", "1", "topics:matrix", "publish", ["event", { "message": "red pill or blue pill?"}]]

# => the following message should appear in both terminals
["1",null,"topics:matrix","event",{"message":"red pill or blue pill?"}]

Commands

# tests
mix test

# quality
mix quality

# debugging
:observer.start()

Resources

About

Distributed PubSub using Consistent Hashing and Erlang's Process Group

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages