1
votes

I've got an elixir (Phoenix) application that retrieves analytic data for users at regular intervals.

I've got an AnalyticSupervisor to supervise AnalyticWorkers.

defmodule AnalyticsSupervisor do
  use Supervisor

  def start_link do
    Supervisor.start_link(__MODULE__, [], name: :analytics_supervisor)
  end

  def init(_) do
    children = [
      worker(AnalyticsWorker, [])
    ]

    supervise(children, strategy: :simple_one_for_one)
  end

  def start_worker(user) do
    Supervisor.start_child(:analytics_supervisor, [user])
  end
end

The AnalyticWorker is is using gproc as a process registry to create a dynamic process name and queue an analytic call.

defmodule AnalyticsWorker do
  use GenServer

  def start_link(user) do
    GenServer.start_link(__MODULE__, user, name: via_tuple(user))
  end

  defp via_tuple(user) do
    {:via, :gproc, {:n, :l, {:analytics_worker, user.id}}}
  end

  def init(user) do
    queue_analytic_call(user)
    {:ok, user}
  end

  def queue_analytic_call(user) do
    :timer.apply_after(50000, __MODULE__, :call_anlaytics, [user])
  end
end

The AnaylticSupervisor is started when my Phoenix application is started, and will queue up analytic calls for any existing users.

The problem is, if I start off another iex session, this will also start the supervisor and workers - I'll end up with multiple processes for the same job.

I thought that using gproc as a process registry, this would be prevented - but each iex session is creating it's own process registry.

  • How to I ensure that the processes are unique, regardless of whether they're being queued by an iex session or my Phoneix application?

  • Is there a better way of achieving unique scheduled jobs than what I'm doing?

1

1 Answers

1
votes

By running the “another iex instance” you are basically creating the new erlang node, that knows nothing about other nodes running around.

One option to ensure that the processes are unique, regardless of whether they're being queued by an iex session or my Phoneix application would be to attach the subsequently started iex to the running one. Assuming, that the original process was started with:

#                           ⇓⇓⇓⇓⇓⇓⇓⇓⇓
MIX_ENV=prod elixir --sname analytics -S mix run --no-halt

to attach iex to it use:

#       put resolvable host name here ⇓⇓⇓⇓⇓⇓⇓⇓⇓
iex --sname console --remsh analytics@localhost

Since the second question is a) the second question in the same OP and b) highly opinion based, I would suggest you compare what you have to already existing implementations (the common approach would be to use Redis or like to ensure uniqueness):

Also, Task.Supervisor might suffice your requirements for zero cost.