Running tasks in production with Elixir releases: rpc, eval, and remote

Sooner or later you’ll need to run a one-off or recurring task on a deployed app, and if you deploy with releases the usual answer, a Mix task, isn’t available. In this screencast we’ll walk through what releases give you instead: remote, rpc, and eval, and when to use each one.

The first three parts are plain Elixir releases and apply to any deployment. The last part uses Potions’ scheduled tasks to run the job daily.

3 Likes

Everything was fine and dandy until I encountered this N+1:

def reprice_stale do
  repriced =
    stale_query()
    |> Repo.all()
    |> Enum.map(fn widget ->
      widget
      |> Ecto.Changeset.change(
        price: widget.price |> Decimal.mult(@discount) |> Decimal.round(2),
        price_reduced: true
      )
      |> Repo.update!()
    end)

  IO.puts("Reduced #{length(repriced)} stale widgets")
end

I really, really hope the author doesn’t put such code in production because it’s essentially a single UPDATE in the database. No mapping, no individual updates.

def reprice_stale do
  {repriced_count, _} =
    stale_query()
    |> update([w], 
      set: [
        price: fragment("round(? * ?, 2)", w.price, @discount), 
        price_reduced: true
      ]
    )  
    |> Repo.update_all([])

  IO.puts("Reduced #{length(repriced_count)} stale widgets")
end

P.S. Of course, the above implies the author has a NUMERIC column for the price.

You’re right, and thanks for calling it out. I’ve updated the post and re-recorded that section of the video.

2 Likes