mortenlund

mortenlund

PubSub Broadcast Performance and best practice

Hi everyone!

I am currently making a system that gather data from subsystems and sends each “value” over websocket to a Elixir Phoenix application.
The application then wants to broadcast each of these values to my two other applications in the cluster and this is currently done using PubSub.

The reason that I am asking is that I anticipate alot of traffic here and would really like your input if this is a good/bad way of doing this.

The current payload size for each broadcast message is approx 1020 bytes, using value |> :erlang.term_to_binary() |> :erlang.byte_size() to find the size.
I currently have a way to get this down to about 520 bytes by using a smaller struct.

I send 10.000.000 messages per day at the moment but this will increase.
I send every message on one “common” topic called “values” and also a specific topic called "values:<node_id>

This means I have 20.000.000 messages sent from this node and they are all received on the two other nodes.
And if I understand correctly they are only sent once from node x to node y and then node y sends it to the subscribers on that node?

My other nodes subscribe to either the “common” topic or the node specific ones and they are used to visualize in LiveViews.

The “common” topic is also used to keep a “last value” Cachex store that the views use for the initial render.

I dont see any performance problem with my current load at the moment, but the amount of values will grow alot, at least 5-10 times more.

My questions boils down to:

  1. Is PubSub a viable technology for my use case and something to grow with?
  2. If yes, any better way to broadcast the messages except using a “common” and specific topic to be able to both get “all” values and also filter them where one only cares about node specific ones?
  3. If no, what other technology could be viable? I was thinking Kafka or RabbitMQ or something similar, but that would require new applications/servers just to host them
  4. Would it be better to store the “last value” on the node that receives the websocket messages and instead call this node to get them instead of storing them on each node?

Thank you for the great community! :slight_smile:

Most Liked

RudManusachi

RudManusachi

would also not suffer if something was a bit delayed or if the same message was received more then once.

The concern is not a “delay” or “duplicates” but actually the loss of the messages.

with my current payload size and load I easily get 3.5GB of transferred data from the main node in just 24 hours.

why do you care about that number?

  1. GC takes care of it
  2. large binaries are optimized to not be copied in the same node between processes (I think >64kb on 64bit machine)

Now since you are dealing with LiveView, @lud gave a very valuable point about cutting down the number of messages sent to client.
That part actually can cause your server to get OOMKilled!

Watch this part of Marlus’s talk “Optimizing LiveView for Realtime Applications” in that last section he demonstrates how memory of Phoenix app can grow unbound if client can’t receive messages with the same rate they are published! (and overall the whole talk is a gem)

lud

lud

Can you add the origin node in the message, publish only to chmmon topic and filter locally ?

This will divide your bandwith by like 1.8 at least.

You can also use the compression options of term_to_binary/2

You could write the latest values to a database table and use Postgres pubsub to listen for changes, removing the need for Phoenix PubSub (and maybe Cachex). Or at least just publish a small value on Phoenix pubsub when the database has changed.

Also 10 million messages is a lot for a view. It is 115/s on average (or is it not ? I suck at math). Could you just send the last value every 1 second ?

RudManusachi

RudManusachi

Hi, @mortenlund!
I can’t give you definitive answer, but there are some things come to my mind to consider:

Phoenix.PubSub uses adapters to deliver messages, by default its :pg2. However, it could be Redis, Postgres, or even RabbitMQ or anything else, that can act as a message bus…

Now, it’s worth mentioning that default :pg2 uses distributed erlang… In general most of the available out of box tools in distributed erlang provide with eventual consistency. And they don’t provide with any “at least 1 delivery guarantee”.
If you need some guarantees - you would most likely need to opt in to 3rd party (RabbitMQ is a very strong candidate)

sb8244

sb8244

Author of Real-Time Phoenix

I’ll address just this because the answer is “Yes”.

I built a pusher clone that was pushing payloads between 100 bytes and (too many) MB in size. We pushed easily > 300,000,000 messages per day. I don’t know the aggregated size of the messages. We had ~5 servers so each message was broadcast to all of the other servers.

I never saw performance problems from pushing too much data here. It was almost entirely hands off except for unrelated problems over time.

It’s important to note we pushed using built-in node-node connections. We did not use PubSubRedis connection.

Where Next?

Popular in Questions Top

Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
Werner
Hi, I’m using Ubuntu 18.04 and after updating to OTP-24.0 yesterday i have this warning when I run “mix local.hex”: 14:57:30.512 [warn] ...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
gonzofish
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
Mooodi
Given a string, how can I get access to its character by index? Enum.at("my_string", 2) doesn't work. Or rather, not char, but a substr...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New

Other popular topics Top

lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
Tee
can someone please explain to me how Enum.reduce works with maps
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; someth...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

We're in Beta

About us Mission Statement