jakub-zawislak

jakub-zawislak

Subscribe to Phoenix.PubSub from another application

I’m new to distributed elixir.

I have two clean installations of Phoenix 1.4.2 - apps named A and B. I’m running these with --sname option. I can run command from another application using :rpc.call. Can I subscribe to Phoenix.PubSub from another app?

I have tried:

iex(a@MacBook-Pro)1> Node.connect :"b@MacBook-Pro"
iex(a@MacBook-Pro)2> Phoenix.PubSub.subscribe(B.PubSub, "topic")
# throws argument error - :ets.lookup(B.PubSub, :subscribe)

Should it be somehow wrapped by a GenServer on the B side and used with Node.spawn_link? Is there some easier solution? First I wanted to broadcast messages between two apps installed on same server using websockets but then I thought there should be an easier method.

Most Liked

jakub-zawislak

jakub-zawislak

Maybe someone will be interested in my final implementation of the subscriber module

defmodule B.Worker do
  use GenServer

  @reconnect_interval 1_000

  def start_link(opts) do
    GenServer.start_link(__MODULE__, :ok, opts)
  end

  def init(:ok) do
    Process.send_after(self(), :join_pg2, 0)

    {:ok, %{}}
  end

  def handle_info(:join_pg2, state) do
    node_name = :"a@MacBook-Pro"

    case Node.connect(agent_node) do
      true ->
        if :group_name not in :pg2.which_groups() do
          :global.sync()
          :ok = :pg2.join(:group_name, self())
        end

        Node.monitor(node_name, true)

        {:noreply, state}

      false ->
        Process.send_after(self(), :join_pg2, @reconnect_interval)

        {:noreply, state}
    end
  end

  # from Node.monitor
  def handle_info({:nodedown, _node}, state) do
    Process.send_after(self(), :join_pg2, 0)

    {:noreply, state}
  end

  def handle_info(msg, state) do
    IO.inspect msg

    {:noreply, state}
  end
end

blatyo

blatyo

Conduit Core Team

I’m not super familiar with the implementation details, but I’ll try to describe it conceptually and hopefully that’ll help.

Phoenix.PubSub works, by creating a cluster of different nodes. You need to start a Phoenix.PubSub process on every node in the cluster. You can specify an adapter, which allows Phoenix.PubSub to discover what other nodes are available to connect to. In the case of the redis adapter, each node will connect to redis and messages will be sent through that. In the case of PG2 (the default), Phoenix.PubSub will connect with other Phoenix.PubSub processes on any of the nodes already clustered by the nodes being connected.

So, you’ve correctly connected the nodes. But it looks like you’ve not started a Phoenix.PubSub process. So, when you call Phoenix.PubSub.subscribe/2, the process that would’ve setup stuff locally on the current node hasn’t done so and you’re getting an error about a lookup on an ets table not existing.

I think in the simple iex session you’re just missing something like:

Phoenix.PubSub.PG2.start_link(name: B.PubSub)

jakub-zawislak

jakub-zawislak

I figured it out without the Phoenix.PubSub. I think it’s all I need.

Create pg2 group in A

defmodule A.Application do
  def start(_type, _args) do
    :pg2.create :some_group
    # ...
  end

Connect to group in B

defmodule B.Application do
  def start(_type, _args) do
    children = [
      # ...
      {B.Worker, []},
    ]
    # ...
  end
defmodule B.Worker do
  use GenServer

  def start_link(opts) do
    {:ok, pid} = GenServer.start_link(__MODULE__, :ok, opts)

    true = Node.connect(:"a@MacBook-Pro")
    :ok = :pg2.join(:some_group, pid)

    {:ok, pid}
  end
end

Send message in A

iex(a@MacBook-Pro)1> [pid] = :pg2.get_members :some_group
iex(a@MacBook-Pro)2> send(pid, :hello) 

Handle messages from A in B

defmodule B.Worker do
  def handle_info(msg, state) do
    IO.inspect msg

    {:noreply, state}
  end
end

Last Post!

lkuty

lkuty

I invite you to have a look at Distributed Phoenix Chat with PubSub PG2 adapter.

Where Next?

Popular in Questions Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New

Other popular topics Top

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40042 209
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

We're in Beta

About us Mission Statement