lauritzsh

lauritzsh

Help with structure of two GenServers in an application

I am working on a simple web application that allows people to join a virtual room. Each room has a video player that synchronises the state of the video to everyone connected and there is a live chat next to it. All communication is done with Phoenix (channels).

One of my requirements is that each room expires after 24 hours of inactivity. The timeout feature of GenServers seems appropriate here.

My question is how should I structure the room component/application?

Current structure

The Dynamic Supervisor will spawn a Supervisor per room (resolved using :global and a name) that supervises two GenServers (Chat.Server and Player.Server).

I have a working prototype with the following structure (inspiration from Dave Thomas):

lib
├── room
│   ├── application.ex
│   ├── chat
│   │   ├── impl.ex
│   │   ├── message.ex
│   │   ├── server.ex
│   │   └── user.ex
│   ├── chat.ex
│   ├── dynamic_supervisor.ex
│   ├── player
│   │   ├── impl.ex
│   │   └── server.ex
│   ├── player.ex
│   └── supervisor.ex
└── room.ex

With this structure lib/room.ex is the public API. All this module does (should) is forwarding calls to the Chat and Player modules. This feels nice: I could easily refactor those modules (Chat and Player) into their own components/applications.

HOWEVER! I have two problems (1) where do I put the GenServer timeout? It is not really the chat itself or the player that expires. It is the room. The room is just a supervisor though, not a GenServer and (2) there is a slight relation between the chat and the player. When all users leave, the player should pause the video. This is currently handled directly in lib/room.ex module and looks like this:

alias Room.{Chat, Player}

###

def leave(name, user_id) do
  users_left = name
  |> Chat.leave(user_id)
  |> Chat.count_users()

  if users_left == 0 do
    Player.pause(name)
  end

  name
end

This logic feels a bit dirty to me and I don’t like it there.

Alternative structure

One alternative is to combine the state of the two Chat and Player GenServers into a single Room GenServer. Something like this:

lib
├── room
│   ├── application.ex
│   ├── chat
│   │   ├── impl.ex
│   │   ├── message.ex
│   │   └── user.ex
│   ├── chat.ex
│   ├── dynamic_supervisor.ex
│   ├── impl.ex # only for `leave`?
│   ├── player
│   │   ├── impl.ex
│   ├── player.ex
│   ├── server.ex
└── room.ex

The implementation of Chat and Player is still unknown to Room. This also solves problem (1) with the timeout and kind of (2) since I won’t have that logic in the public API.

It still feels a bit wrong though: One advantage of current solution is that an error in the chat GenServer doesn’t bring the player GenServer down. Now an error in chat will also take the player down and restart the entire room basically.

I plan to open source this project but I would prefer to have solved this problem (and clean up) first. If it’s much easier to help with the current source code, I can make the repo public.

First Post!

English3000

English3000

A few thoughts:

  • What’s the user flow? The user logs in, and that makes them join both GenServers?

  • In regard to timeouts, your concern is that one could timeout before the other? You could have the one send a message to the other on leave.

Where Next?

Popular in Questions Top

Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
beno
I will often find my self writing things similar to: case some_value do nil -> something() "" -> something() _ -> somethi...
New
tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
Lily
In templates/appointment/index.html.eex: <%= for appointment <- @appointments do %> <tr> <td><%= appoi...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
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
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
dotdotdotPaul
Okay, I’m having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I’m sure I’...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
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

Other popular topics Top

danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29603 241
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
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
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
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
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement