Can you use the Redix_Pubsub library with Phoenix?

Hi all, I’m trying to use this library: https://github.com/whatyouhide/redix_pubsub in my phoenix app.

It seems this library is meant for elixir, but I was wondering if we could set this up in phoenix and how? Installing is fine, but where do I call the start_link, subscribe functions to test if I can receive messages?

1 Like

It should be fine if You just add the dependency to the mix file and run mix deps.get.

Then start it like this. You will need to have Redis running.

iex> {:ok, pubsub} = Redix.PubSub.start_link()
{:ok, #PID<0.307.0>}

This is started in the elixir shell. How do we start it in the phoenix app?

I would use

application.ex

Do you mean i should plug the whole code snippet there?

No, just add a line like…

worker(Redix.PubSub, [])

to the children part of the start function.

So I’ve started the redis server and added that worker to the application.ex

What I don’t understand is where my app receives information as the subscriber?

Should I be plugging:

Redix.PubSub.subscribe(pubsub, "my_channel", self())
#=> :ok

# We wait for the subscription confirmation
receive do
   {:redix_pubsub, ^pubsub, :subscribed, %{channel: "my_channel"}} -> :ok
end

Redix.command!(client, ~w(PUBLISH my_channel hello)

receive do
  {:redix_pubsub, ^pubsub, :message, %{channel: "my_channel"} = properties} ->
    properties.payload
end

somewhere?

Your application is just a bunch of processes… Choose a process that cares about the messages sent by Redis and use it to subscribe.

I would not use a receive block. Probably I would use handle_info in a registered GenServer.

Sorry very unfamiliar. I read the documentation and there isn’t a handle_info function either. I shouldn’t be sticking any of this receiver / handle_info code in channels correct?

Let’s take a step back: What are you trying to subscribe to, and what do you want to do when you get a message? What are you trying to accomplish?

All I’m trying to do is get a golang microservice to tell my phoenix app that my data has been processed and saved to the database. So I understand this to be my phoenix app as the subscriber and my golang microservice to be the publisher

Cool. It sounds like you probably need to create a GenServer process that you add to your application.ex supervision tree. Inside that GenServer you’ll subscribe to RedixPubsub, and then can take some kind of action when a message arrives. I highly recommend checking out this guide https://elixir-lang.org/getting-started/mix-otp/genserver.html and the pages after it if you’re unclear on how to configure a GenServer within your application.

So I’ve just created a MyApp.Registry file (AKA my Genserver) which has a handle_info function. This is located in my folder lib/my_app. What do I do in my application.ex?

Registry is an unfortunate name for a GenServer, because You might confuse with Elixir Registry.

Once You have a GenServer, You can use application.ex to start it.

Inside this GenServer, I would use init to subscribe, and handle_info to receive messages from Redis, as mentionned by @benwilson512

Say I just went ahead with the word registry, should I be adding supervisor(MyApp.Registry.start_link([])) to the children array?

No, You should use worker()… supervisor() is for … supervisor.

like this

worker(MyApp.MyGenServer, [])

You don’t need to specify start_link inside.

So do I do this in the init function?

    def init(:ok) do
    names = %{}
    refs = %{}

    {:ok, pubsub} = Redix.PubSub.start_link()
    Redix.PubSub.subscribe(pubsub, "my_channel", self())

    {:ok, {names, refs}}
    end

I would not start Redix.PubSub here, but in application, and before your GenServer starts…

{:ok, pubsub} = Redix.PubSub.start_link()

# instead, put this in application

worker(Redix.PubSub, [])

But the rest seems fine.

Is this how application.ex should look like?

defmodule MyApp.Application do
  use Application

def start(_type, _args) do
import Supervisor.Spec

children = [
  supervisor(MyApp.Repo, []),
  supervisor(MyAppWeb.Endpoint, []),
  worker(Redix.PubSub, []),
]

{:ok, pubsub} = Redix.PubSub.start_link()

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

result
end

def config_change(changed, _new, removed) do
  MyAppWeb.Endpoint.config_change(changed, removed)
  :ok
end
end

What was the result of your previous try? Apart from not being started by top supervisor, it should have been working.