dsaghliani

dsaghliani

Is it cheaper to broadcast a small amount of data (over a Phoenix Channel) frequently or to gather it and broadcast the total fewer times?

I built a simple (and unauthoritative) game server that’s remarkably similar to a chat server. It’s a message bus that receives updates from users connected to a certain topic in a (Phoenix) Channel regarding their position, rotation, etc.

Each socket passes the update as a message to a process named UpdateDispatcher. The process runs a function, dispatch(), in an infinite loop. Every @tickrate times a second, it gathers all messages in its inbox, organizes them into a Map, and calls Endpoint.broadcast!/3 to send it to the users.

# ------------------------
# Module: UpdateDispatcher

def start_link() do
  pid = spawn_link(&dispatch/0)
  true = Process.register(pid, __MODULE__)
  {:ok, pid}
end

def dispatch() do
  {micros_elapsed, _} =
    :timer.tc(fn ->
      messages = receive_messages()

      if messages != %{} do
        Endpoint.broadcast!("world:lobby", "world_updates", messages)
      end
    end)

  max(sleep_duration() - micros_elapsed / 1000, 0)
  |> round()
  |> Process.sleep()

  dispatch()
end

defp sleep_duration(), do: (1000 / @tickrate) |> round()

# --------------------
# Module: WorldChannel

@impl true
def handle_in("user_update", %{"user_id" => user_id, "data" => data}, socket) do
  send(UpdateDispatcher, {:user_update, user_id, data})
  {:noreply, socket}
end

Currently, @tickrate is 10, so the dispatcher sends updates every 100 ms. The users send in theirs at the same rate, though they may not be synced up.

I built it that way out of fear of quadratic time complexity, since every user would otherwise broadcast their updates to all other users. That may work for a chat app, I thought, but my updates will be coming in way more frequently, so it won’t work for me. The idea came from this answer to a question I asked on Reddit.

However, is that really true? I made the assumption without knowing much at all about Elixir and Erlang. My method should, theoretically, decrease it to linear time, but it still sends roughly the same amount of data on the wire, so does it make any difference, or is the Erlang VM perfectly capable of handling the load?

Did I even need to bother, or could I simplify the application by ridding it of UpdateDispatcher and using Phoenix.Channel.broadcast_from!/3 inside the handle_in("user_update", ...)?

How could I even benchmark it? My goal is to reduce the server costs.

Most Liked

al2o3cr

al2o3cr

Some thoughts in no particular order:


On overload:

PubSub.broadcast! ultimately uses Registry.dispatch and send to deliver the message to each local recipient, semi-synchronously (“semi” because all the messages have to be sent before returning, but not necessarily handled).

send is an inexpensive operation, but it isn’t free - so there’s a hard upper limit on how many clients a single-process UpdateDispatcher can support on one node. If it takes more than 100ms to do all the sends, then the whole system will slip farther and farther behind.


On lag:

Buffering messages for (up to) 100ms means that a player of the game will see an effective “lag” 50ms higher than their connection’s latency on average. That would be frustrating for a fast-moving game like an FPS.

On the other hand, collecting all the messages every 100ms means that a laggy client might not get an update in during the window at all, while clients whose clocks are running fast might send two updates. Every listener of user_update is going to need to grapple with missing or extra values in data.

One way to deal with that situation is to collect responses differently, if the game’s semantics support it:

  • accumulate updates for either 100ms or until every user has checked in
  • “collapse” the updates by combining multiple updates from a single user and filling in “same as last time” for users that didn’t report during the interval

This would allow listeners to user_update to get consistent input.

Where Next?

Popular in Questions Top

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
New
logicmason
Hi there, I'm working through my first release with elixir/phoenix. I've built a release with distillery and found that it crashes when I...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

Other popular topics Top

Tee
can someone please explain to me how Enum.reduce works with maps
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 45766 226
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

We're in Beta

About us Mission Statement