Search code examples
erlangelixircowboyplug

Simple Elixir / Plug Processes Issue - PID not sticking around


I'm just starting out in Elixir and wanted to build a very simple API with Plug. I used this guide to get a very simple API up and running.


Basically, the problem I'm facing is that the process that I registered as :qs doesn't seem to be found (and errors out), whenever I use the send/2 function in queue_service.ex. What I'm trying to achieve is a process that sticks around so that I can have state being maintained across requests.


In my router.ex file, I have:

defmodule SecondQueue.Router do
  use Plug.Router

  alias SecondQueue.Plug.VerifyParams
  alias SecondQueue.QueueService

  plug Plug.Parsers, parsers: [:urlencoded, :multipart]
  plug :match
  plug :dispatch


  get "/receive-message" do
    # gather query parameters from connection
    queue = Map.get(conn.params, "queue")
    message = Map.get(conn.params, "message")

    # handle the params
    QueueService.handle_incoming(queue, message)

    send_resp(conn, 201, "Created")
  end
end


Then inside queue_service.ex, I initiate the queues process, register it to an atom of :qs, and then want to be able to get at that process later via a function that a request calls. I have:

defmodule SecondQueue.QueueService do
  alias SecondQueue.QueueStore
  use Agent

  {:ok, agent_pid} = QueueStore.start_queues_link()
  Process.register(agent_pid, :qs)

  def handle_incoming(queue, message) do
    queue_atom = String.to_atom(queue)
    send(:qs, {:put, queue_atom, "success"})
  end
end

And then finally, in queue_store.ex, I actually define the process that I want to store the state, and run a loop so that it stays alive, and ready to receive messages. I have:

defmodule SecondQueue.QueueStore do
  def start_queues_link() do
    Task.start_link(fn -> queues_loop(%{}) end)
  end

  defp queues_loop(map) do
    receive do
      {:get, key, caller} ->
        send caller, Map.get(map, key)
        queues_loop(map)
      {:put, key, value} ->
        IO.puts("i am here")
        queues_loop(Map.put(map, key, value))
    end
  end
end

Update:
Github repo: https://github.com/qarthandgi/test-second-queue


Solution

  • Elixir is a compiled language. The code below gets executed during compilation stage; no process is being started in runtime.

    defmodule SecondQueue.QueueService do
      ...
      {:ok, agent_pid} = QueueStore.start_queues_link()
      Process.register(agent_pid, :qs)
      ...
    end
    

    Instead, you need to put this code into a function and explicitly call this function to start QueueStore (directly or by plugging it into your app’s supervision tree.)