Search code examples
elixirphoenix-frameworkbackground-process

Background processes with Elixir and Phoenix


I have a system that creates a Question and inserts this question into the database, I do this by clicking a link that I've set up. This is straightforward Phoenix stuff. Create a controller action setup a link for it and then click the button to fire that action.

This works for now. But the next phase is to have the system create a question without any intervention from the UI. So, that leads me to a new place with Elixir/Phoenix. My new problem is: I need to run this function automatically at x time of day.

Question:

What is the most idiomatic way of implementing a background task in Elixir/Phoenix? I know very little about Genserver or Supervisors but I think I'm ready to start learning about these tools. With all that being said, how would you approach a the problem of moving logic to a background job.

CODE:

  def build_question(conn, _params) do
    case Questions.create_total_points_question(conn) do
      {:ok, _question} ->
        conn
        |> put_flash(:info, "Question created successfully.")
        |> redirect(to: page_path(conn, :index))
      {:error, _msg} ->
        conn
        |> redirect(to: page_path(conn, :index))
    end
  end 

This controller action is triggered from a link. This code needs to be called in the background. Thanks for the help in advance.


Solution

  • You have few options.

    One is to simple run Task.async in your action, but that links process which executes your action with the one you spawned, so task crash will affect process which spawned it and awaits it. BTW, in your particular case I don't think you want this in your action since you don't have anything to work with while awaiting for the result, so it is unnecessary.

    Second option, in case you don't want to await for the result is to use Task.start_link. This is ok, but again as name of start function is, the tasks process is linked to yours, so crashes in any of those two will cause other to crash too.

    Third option, is to use Task.Supervisor, just open your app ex file (probably one which has name as you project and it is in lib folder) and under children = [... list add at the bottom like this

    children = [
        ...
        supervisor(Task.Supervisor,[], [name: MyApp.TaskSupervisor])
    ]
    

    this will start supervisor process in your app with name MyApp.TaskSupervisor which than you can call and tell what code to run and supervise it.

    Now this third option gives you a bit more control in your app since:

    1. You can run task async (in background) without linking process which is instructing supervisor what task should do and task process, instead supervisor will monitor this task
    2. you can still await for the result
    3. you can still link task if you want your process to crash if task crashes
    4. You can event distribute task into other nodes easily.

    You can find more info about this in documentation