What is the right way to communicate between two GenServers?

This is a valid question. The scope of the problem you have described could be solved with the Lobby process using Process.send_after/4 and simply process the “complete” msg value when it is returned via the handle_info/2 callback.

I’m not a fan of this because it seems to tie the two together a bit much.

Now lets say there is a good reason for the CountdownTimer process to exist - maybe because it somehow coordinates a UI display of the countdown timer.

You can take inspiration from send_after. So rather than defining Lobby.countdown_completed/0, you solve the problem in the CountdownTimer API when the countdown is initiated.

CountdownTimer.new_timer(completed_msg, time)

CountdownTimer will implement this as a call so it will get from for free.

  • it can either use send_after itself with a {from, completed_msg} message value
  • or store {from, completed_msg} in the process state for later

So when the time comes CountdownTimer simply uses GenServer.cast(from, completed_msg) to return the requested completed_msg value to the Lobby that requested the timer (which it then has to handle in its own handle_cast/2).

In this particular situation I think it’s fine as the CountdownTimer process really doesn’t care if the Lobby process has died. Furthermore the CountdownTimer could just slap a monitor onto each of its client processes so that it can cleanup their timers as soon as possible.

2 Likes