Search code examples
elixirphoenix-frameworkcron-taskelixir-mix

Unable to run cron tasks without phx.server running


I'm developing an app in my desktop computer (Mac OS) for which I created some cron tasks that run every 5 minutes. The code is the following (taken from here):

defmodule MyApp.CronJobs do
  use GenServer

  @shops ["billa","kaufland","lidl"] 

  def start_link do
    GenServer.start_link(__MODULE__, %{})
  end

  def init(state) do
    schedule_work() # Schedule work to be performed at some point
    {:ok, state}
  end

  def handle_info(:work, state) do
    Enum.each(@shops, &monitor_prices/1)
    schedule_work() # Reschedule once more
    {:noreply, state}
  end

  defp monitor_price(shop)
    Mix.Task.run "monitor.#{shop}.all_prices"
  end

  defp schedule_work() do
    Process.send_after(self(), :work, 5 * 60 * 1000)
  end
end

On Supervision tree:

  ...
  children = [
    supervisor(MyApp.CronJobs, [])
    ...
  ]
  opts = [ strategy: :one_for_one, name: MyApp.Supervisor]
  Supervisor.start_link(children, opts)

The problem is this runs with the server, so whenever the computer goes on Sleep mode it stops running.

Is there a way to have the processes running on the background permanently without having the computer all the time in Full Power mode? Even better, Is there a way to have mix Tasks triggered every 5 minutes without running the server?


Solution

  • You can program OSX with a scheduled wakeup and sleep so this could solve your sleeping issue. You can also coordinate a "real" cron job to run either a script in the same window.

    The problem with mix tasks is that they usually run from a project's root folder since they need the context of the project to run. This is not really a "production" solution. You install an archive that will run from any directory, but there is a better way.

    Take a look escripts. You build an escript for a mix project and install it on you system. You can then run the escript from your scheduled cron job.

    I doubt you will need a GenServer in your solution since your example above is not using any persistent state. If you need concurrency, you can simply spawn some processes or use Task to run some concurrent code.

    Depending on the complexity of your full solution, you may want to look at Distillery to package a release for your project, but this may be overkill.