DEV Community

Cover image for Real time Dashboard with WebSocket in Elixir
Ahsan Nabi Dar
Ahsan Nabi Dar

Posted on

Real time Dashboard with WebSocket in Elixir

Originally Posted https://darnahsan.medium.com/real-time-dashboard-with-websocket-in-elixir-f4bad1a71ba7 Published On April 17, 2020

What started as an Elixir project over the weekend to support our mobile team with their QA. UltronEx grew into the playground to have introduced many new things to our stack including elixir. The initial design of the project upon which this was built to provide additional support for the QA was stream messages directly into Slack. It soon hit limit on the Slack side with calls being rate limited. This caused thousands of messages not being delivered to Slack for QA to work with. We were getting hundreds of event types rate-limited some over 10000 times since the project. One way would have been to retry but in a stream system you are only queuing messages again to fail and when a message is delayed or out of sequence it loses its value. How bad the rate limit was would be evident from the image below

As a fun way to monitor the system, I started keeping stats of how many messages were processed, how many attachments were downloaded and scanned for matches, how many messages were sent using the service. Keeping in mind it all started as a project to not only provide value but to also assess the ability of Elixir as a robust technology. Along with the slack bot, the API and now a real-time Dashboard built using WebSocket nothing has changed on the resource limit. It still runs on a 1vCPU 2GB RAM instance in the cloud (application container memory limit is set to 350MB only) . As of this writing, it has read over 1.3 million messages from slack, downloaded over 1.2 million attachments and scanned them and has sent close to 1 million messages to slack using its API endpoint from other services.

Stats counterStats counter

The Dashboard is divided up in 3 verticals showing messages as SUCCESS , DANGER and WARNING . It had been long since I did work on the frontend. Last I worked with CSS and JS on the client-side Bootstrap used to be the hip kid on the block. So my first instinct was to Google for Bootstrap and to my surprise, it is active and doing well and keeping jQuery alive. With little Bootstrap styling, the dashboard was looking acceptable.

The performance of the dashboard has been smooth and stable. Internally we have tested it up to 30 users with no noticeable delay on a single box and the application is only allocated 350MB of memory with only 165MB utilised on average. This is a one-way communication where messages are sent to the dashboard. One could argue that Server-Sent Events would have sufficed the use case but as this is a project with expanding scope upgrading it to bidirectional later didn’t seem worth the effort.

The code that handles the message pushes from the server side is compact and readable thanks to Elixir

GitHub logo ahsandar / ultronex

RTM Slack bot written in Elixir

On Github its a mirror from private Gitlab repo

Would like to thank AppSignal for considering my project under their OpenSource initiative and setting up a free account for APM. I hope this helps others at using their product. Its one of the most comprehensive tools available for Elixir monitoring.

UltronEx - Ultron in Elixir

Its my first attempt at writing elixir, the code might not very elixirish. This is a rewrite of a slack bot I did few years ago in ruby. Blog post showcasing the results elixir outperforming ruby by 100%

Image

command(s)
      --> help #list the command list
      --> mute/talk #TODO implement later
      --> xkcd #shows a random xkcd comic
      --> xkcd <comic no> #shows xkcd comic no
      --> gif #shows random gif
      --> gif <category> #show a random gif from the category
      --> quote #shares a quote
      --> forward <term> #sets up msg forwarding for the
defmodule Ultronex.Server.Websocket.SocketHandler do
  use Appsignal.Instrumentation.Decorators

  @behaviour :cowboy_websocket

  def init(request, _state) do
    state = %{registry_key: request.path}
    {:cowboy_websocket, request, state}
  end

  def websocket_init(state) do
    Registry.UltronexApp
    |> Registry.register(state.registry_key, {})

    {:ok, state}
  end

  @decorate transaction()
  def websocket_handle({:text, json}, state) do
    payload = Jason.decode!(json)
    message = payload["data"]["message"]
    websocket_send_msg(message, state)
    {:reply, {:text, message}, state}
  end

  @decorate transaction()
  def websocket_info(info, state) do
    {:reply, {:text, info}, state}
  end

  @decorate transaction()
  def websocket_send_msg(message, state) do
    Registry.UltronexApp
    |> Registry.dispatch(state.registry_key, fn entries ->
      for {pid, _} <- entries do
        if pid != self() do
          Process.send(pid, message, [])
        end
      end
    end)
  end
end
Enter fullscreen mode Exit fullscreen mode

Real-time web apps seem to be the trend now and with Phoenix Live view it could be the next revolution. After how Ruby on Rails modernized the web development it is going to be Elixir and Phoenix.

Oldest comments (0)