Stagger GenServers in `application.ex`

Hi all,

I read Wojtek Mach’s post a couple of weeks ago about writting SDKs with Req (SDKs with Req: Stripe - Dashbit Blog) and the little bonus in the end where he shows how to wire a CLI to a GenServer and have it start with the server in prod blew my mind and I’ve been writing GenServers for all the services I use in dev ever since.

It works fine but there is one little annoyance I haven’t been able to figure out how to circumvent yet; some of these processes need to be ready to go by the time other processes run and, typically they load quickly enough that is not an issue but sometimes one take longer than it should and then I have to restart the server until it loads as it should.

Is there an issue you can start a list of GenServers with Supervisor.start_link where the first in the list signals when the next in the list should start? That’s how I conceptualize it working out anyways but I have a bit of a gap in my understanding of how OTP works so maybe there is a better way to achieve what I want. Any help is welcome!

Thank you

Supervisor guarantees that it will start the children in the order they are defined.

The init callback is synchronous, you can take a look in detail here: GenServer — Elixir v1.17.3 (look at the diagram down the chapter).

You can use the order of definition of children to guarantee that previously defined genserver was already started and is operational. Also, since init is a synchronous call, you can use that callback to define critical things you need for initialization before the process gets called by other ones, but keep in mind that this will delay start of other children.

3 Likes

Everything is started in the order it’s specified so you can also put your own GenServer there that waits for the other thing to start and you should be just fine.

1 Like

Thanks for your answer!

So, the children are being fired in order and the previous one is indeed operational by the time the next one starts; the problem is, the GenServers themselves are operational but not necessarily the thing I’m using the a GenServer to control, if that makes sense.

One example of something I’m using in dev is a Posgres Docker container. Typically, when I start the application the Postgres server is running ok, but every now and then it takes a little longer to become available and everything fails because the lack of database in the project.

From what I understand, right now, init takes care of starting the docker run command and once it’s successful the supevisor goes to the next one in the list. I’m trying to come up with something that would initiate the docker command, wait until psql returns a message saying it’s ready to accept connections to then return. I’m just not sure how to go about doing it.

This could be solved by running pg_isready in your Postgres Docker container, if I am reading you correctly.

2 Likes

The correct way to do this is to write a bash script that detects when the postgres instance is actually running, then start your app, not handling this at the app level. You can also make this easier by setting a wait time until starting the app (like 4-5 seconds).

Please never do this if you can help it, this is k8s level of hackery. PostgreSQL in particular gives you pg_isready that allows you to do zero sleep-ing.

Of course many DBs don’t have such builtin commands so there the choice is zero.

1 Like

Another alternative is not expecting external systems to be up in the first place:

3 Likes

Oh, that was perfect! Thank you!

Just had to send a docker exec with pg_isready to the running container and put this call in a recursive function returning to init and return when I get a positive response from the command. Thank you so much!