Start a worker on Heroku

I have a Phoenix app on Heroku, working on one Dyno. I added a GenServer doing some background stuff every 15 minutes (checking the DB, sending emails, etc.), and I started it by adding it to my app’s Supervisor (in lib/myapp.ex).

Now I’d like to use more Dynos to scale my moderately successfull app and I’m wondering how to extract the worker in it’s own command.

The heroku Procfile does “mix phoenix.server” to start a web dyno, and I’d need something like a “mix myapp.myworker” to start a worker in the Procfile. This worker should be able to access my Repo, and some functions in the rest of the app, more or less like a Task in lib/task does.

Like I guess many people here I come from a Rails background, already enjoying Phoenix and functional programming with Elixir, but my understanding of OTP is limited for the moment, and I’m not sure how to get this done ?

Thank you

1 Like

I thought Heroku couldn’t network BEAM systems because they operate as distinct units, and that was one big reason for Gigalixer existing (aside it just be built better for the BEAM in general)?

Either way, this is all part of Application start-up and management, look in those docs (Elixir’s and Erlang’s). I’d link but I’m in a hurry at work at the moment. ^.^;

But essentially, you’ll start the nodes having them know about each others names so they auto-connect together, then they can negotiate which applications should run where and why based on your Application descriptions (and manual starting of processes on specific nodes). This is also how you can get fail-over, so if a node fails then applications can migrate to other nodes.

There are a few ways you can do it. Personally, I would approach it this way:

One: Create a custom mix task for your background worker. This is good intro:

I would actually base this task on how phx.server task is structured. Have a look here:

so what it does is two things a) sets some configuration option to force starting web server, b) starts all applications with and that’s it.

Two: In the newly created task, I would set different configuration option. Something like `Application.put_env :my_app, :start_background_stuff, true.

Then, I would use this environment variable in your application callback module (application.ex), and check if the value is true, and only if it is - would start the GenServer I want to be running.

Please note that if you do the above, it will work fine as long as you don’t intend to send messages to this GenServer from your Phoenix workers. It won’t work and will throw you with error that no process exists. This will be because on Heroku, background dyno will start as a separate OS process, in fact in a separate container and most likely on different hardware altogether. Since you just want to run something every 15 minutes that should not be a problem I think.

Which, however, brings me to the other point that you might want to use something like quantum instead . In such case, I would still implement it in a similar way - create a custom mix task, set config variable in it - and only if the variable is set - configure quantum scheduler. Otherwise (on web dynos) no scheduler would be configured.

1 Like

problem is the dynos share env variables, so it’s a bit difficult… and they can’t really talk to each other… can you scale to 2X dyno? or are you already there?

you might be able to use the heroku scheduler and a mix task, but I’m not 100% on how to set that up…

or you can use the $DYNO env var - although it’s experimental and other things…

another although hacky way is to use a 3rd party cron service… I remember doing this in the past with a RoR heroku setup… something like the free plan here and then “ping” an (protected) endpoint that does the background job (of course check in the db for last run etc so it fits your purpose)…

not optimal by any means - but works:/

I think @HenriMorlaye just starts mix phx.server for his web dynos. If that’s the case, then it’s quite easy because in Procfile just should specify different mix task, and the variables used would not be shell environment variables at all.

1 Like

Thank you !
I tried something which seems to be working. I my Procfile I have put:

web: mix phoenix.server
worker: WORKER_APP=true mix phoenix.server

and just changed the MyApp.ex application file to add in the Supervisor the genserver if the env variable “WORKER_APP” exists, and add MyApp.Endpoint if the env variable is empty.

1 Like

That will work too.