### Start Phoenix.PubSub in Supervision Tree Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md Ensure Phoenix.PubSub is started in your application's supervision tree. This example shows how to include it alongside Oban. ```elixir defmodule MyApp.Application do use Application def start(_type, _args) do children = [ {Phoenix.PubSub, name: MyApp.PubSub}, {Oban, oban_config()}, ] Supervisor.start_link(children, strategy: :one_for_one) end defp oban_config do Application.get_env(:my_app, Oban) end end ``` -------------------------------- ### Oban.Notifiers.Phoenix listen/2 Channel Conversion Examples Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/oban-notifier-behavior.md Illustrates how different inputs for channels are handled and converted to strings for subscription. ```elixir listen(:signal) # Converted to "signal" listen(["signal"]) # Already a string listen([:signal, :insert]) # Both converted ``` -------------------------------- ### Configure Phoenix.PubSub with PG Adapter Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Example of configuring Phoenix.PubSub using the PG adapter. This is an optional configuration and uses defaults if not specified. ```elixir # In config/config.exs or config/runtime.exs # PubSub configuration (optional, uses defaults if not specified) config :my_app, MyApp.PubSub, adapter: Phoenix.PubSub.PG, # ... adapter-specific options ``` -------------------------------- ### Oban.Notifiers.Phoenix.start_link/1 Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/oban-notifier-behavior.md Starts the notifier as a supervised GenServer process with specified options. ```APIDOC ## Oban.Notifiers.Phoenix.start_link/1 ### Description Starts the notifier as a supervised GenServer process. It initializes the process state and registers it under a given name. ### Parameters - `opts` - Keyword list of options: - `opts[:name]` - Custom process name (default: `Oban.Notifiers.Phoenix`). - `opts[:pubsub]` - Phoenix.PubSub instance name (required). - `opts[:conf]` - Oban configuration (provided by Oban framework). ### Returns - `{:ok, pid}` - Process successfully started. - `{:error, {:already_started, pid}}` - Name already registered. - `{:error, term()}` - Other startup failures. ### State Initialization 1. Extracts the `:name` option or uses the default module name. 2. Creates a `%Oban.Notifiers.Phoenix{}` struct from the remaining options. 3. Starts a GenServer with this struct as the initial state. 4. Registers the process under the provided name. ``` -------------------------------- ### Multi-Node Notification Flow Example Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md Demonstrates how to set up two nodes, listen for notifications on one, and send a notification from the other, with the first node receiving it. ```elixir # Node A Node.start(:"node_a@localhost") Application.start(:my_app) Oban.Notifier.listen(:insert) # Node B Node.start(:"node_b@localhost") Application.start(:my_app) Oban.Notifier.notify(:insert, [%{job_id: 123}]) # Node A receives notification receive do {:notification, :insert, %{"job_id" => 123}} -> IO.puts("Received from Node B!") end ``` -------------------------------- ### Successful Process Start Return Type Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/types.md The {:ok, pid} tuple is returned by `start_link/1` upon successful creation of the notifier process. ```elixir {:ok, pid :: pid()} ``` -------------------------------- ### Minimal Oban Phoenix Setup Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Basic configuration for Oban with Phoenix PubSub. Ensure Oban and your PubSub module are configured. ```elixir # config/config.exs config :my_app, Oban, notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub}, repo: MyApp.Repo, queues: [default: 10] ``` -------------------------------- ### start_link/1 Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/api-reference-oban-notifiers-phoenix.md Starts the notifier as a supervised GenServer and integrates it with Oban's notification system. It requires configuration options including the Phoenix.PubSub instance name and Oban's configuration. ```APIDOC ## Function: start_link/1 Starts the notifier as a supervised GenServer and integrates it with Oban's notification system. ### Function Signature ```elixir @impl Notifier def start_link(opts) ``` ### Parameters - **opts** (Keyword.t()) - Required - Configuration options - **opts[:name]** (atom()) - Optional - The registered name for the GenServer process, defaults to `Oban.Notifiers.Phoenix` - **opts[:pubsub]** (atom()) - Required - The name of the Phoenix.PubSub instance to use - **opts[:conf]** (map()) - Required - The Oban configuration structure containing at minimum the `name` field ### Return Value `{:ok, pid}` tuple where `pid` is the process identifier of the started GenServer. ### Errors - Raises `ArgumentError` if required `:pubsub` option is missing - Returns `{:error, {:already_started, pid}}` if the process name is already registered ### Example ```elixir # Configure Oban to use the Phoenix notifier config :my_app, Oban, notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub}, repo: MyApp.Repo # Or start manually in supervision tree {Oban, [ notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub}, repo: MyApp.Repo ]} ``` ``` -------------------------------- ### Oban.Notifiers.Phoenix start_link/1 Implementation Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/oban-notifier-behavior.md Starts the notifier as a supervised GenServer process, handling process naming and state initialization. ```elixir @impl Notifier def start_link(opts) do {name, opts} = Keyword.pop(opts, :name, __MODULE__) GenServer.start_link(__MODULE__, struct!(__MODULE__, opts), name: name) end ``` -------------------------------- ### Internal Struct Instantiation Example Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/types.md Illustrates how the Oban.Notifiers.Phoenix struct is internally instantiated during the `start_link/1` process. This is not intended for direct user instantiation. ```elixir %Oban.Notifiers.Phoenix{} |> struct!(opts) ``` -------------------------------- ### Setup Isolated Oban and PubSub Instances for Testing Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/behavior-and-design.md Configure a unique Phoenix.PubSub instance and an Oban instance with the Phoenix notifier and isolated peer for testing. Ensure TestRepo is configured. ```elixir setup do start_supervised!({Phoenix.PubSub, name: unique_name()}) start_supervised!({ Oban, [ notifier: {Oban.Notifiers.Phoenix, pubsub: unique_name()}, peer: Oban.Peers.Isolated, repo: TestRepo ] ]}) end ``` -------------------------------- ### ExUnit Test Setup for Oban Notifiers Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Configure ExUnit and Ecto Sandbox for testing. Sets up a test PubSub adapter for Oban Notifiers. ```elixir # test/test_helper.exs ExUnit.start() Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, :manual) # Configure test PubSub Application.put_env(:my_app, :test_pubsub, MyApp.TestPubSub) ``` -------------------------------- ### Oban Notifier GenServer init/1 Callback Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/oban-notifier-behavior.md Initializes the Oban Notifier GenServer by storing its state in the Oban.Registry. This is called by GenServer when starting. ```elixir @impl GenServer def init(state) do put_state(state) {:ok, state} end ``` -------------------------------- ### Configure Phoenix.PubSub with Redis Adapter Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Example of configuring Phoenix.PubSub using the Redis adapter. This configuration includes options for connecting to a Redis instance. ```elixir # Or for Redis adapter: config :my_app, MyApp.PubSub, adapter: Phoenix.PubSub.Redis, redis_opts: [ host: System.get_env("REDIS_HOST") || "localhost", port: String.to_integer(System.get_env("REDIS_PORT") || "6379") ] ``` -------------------------------- ### Example of a Received Notification Message Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/types.md An example of a concrete notification message received through Phoenix.PubSub, demonstrating the structure with a specific channel and payload. ```elixir {:notification, :signal, %{"action" => "insert", "oid" => 123, "queue" => "default"}} ``` -------------------------------- ### Start PubSub Before Oban Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md Ensure Phoenix.PubSub is started before Oban in your application's supervision tree to avoid 'no notifier running' errors. ```elixir children = [ {Phoenix.PubSub, name: MyApp.PubSub}, # Start first {Oban, oban_config()}, ] ``` -------------------------------- ### Install Oban Notifiers Phoenix Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md Add the `phoenix_pubsub_redis` and `oban_notifiers_phoenix` dependencies to your project's `deps`. ```elixir defp deps do [ {:phoenix_pubsub_redis, "~> 2.1"}, {:oban_notifiers_phoenix, "~> 0.2"}, ] end ``` -------------------------------- ### Supervision Tree Setup with Phoenix.PubSub Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Include Phoenix.PubSub in your application's supervision tree before or alongside Oban. Ensure the `name` option for Phoenix.PubSub matches the one used in the Oban notifier configuration. ```elixir defmodule MyApp.Application do use Application @impl true def start(_type, _args) do children = [ # Start Phoenix.PubSub with a specific name {Phoenix.PubSub, name: MyApp.PubSub}, # Start Oban with the notifier configured {Oban, oban_config()}, # ... other children ] opts = [strategy: :one_for_one, name: MyApp.Supervisor] Supervisor.start_link(children, opts) end defp oban_config do Application.get_env(:my_app, Oban) end end ``` -------------------------------- ### Multiple Oban Instances Configuration Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Example of setting up multiple independent Oban instances within the application's supervision tree. Each instance can have its own queues and notifier configurations. ```elixir # Supervision tree children = [ {Phoenix.PubSub, name: MyApp.JobQueue}, {Phoenix.PubSub, name: MyApp.Notifications}, {Oban, [ name: ObanJobQueue, notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.JobQueue}, repo: MyApp.Repo, queues: [default: 10] ]}, {Oban, [ name: ObanNotifications, notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.Notifications, name: NotifierTwo}, repo: MyApp.Repo, queues: [notifications: 5] ]} ] ``` -------------------------------- ### Listen for Job Insertions Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md Start listening for new job insertions using `Oban.Notifier.listen(:insert)`. Handle incoming notifications in a separate process. ```elixir # Start listening for new jobs :ok = Oban.Notifier.listen(:insert) # In a separate process (e.g., a channel handler): def handle_info({:notification, :insert, {oid, count}}, state) do IO.puts("Job #{oid} inserted, #{count} in queue") {:noreply, state} end ``` -------------------------------- ### Configure Oban Notifiers Phoenix in Supervision Tree Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/START-HERE.md Configure Oban to use the Phoenix notifier by specifying the `pubsub` name and your application's `repo`. After setup, you can start listening for notifications and broadcasting them. ```elixir # In supervision tree {Phoenix.PubSub, name: MyApp.PubSub}, {Oban, [ notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub}, repo: MyApp.Repo ]} # Then use: :ok = Oban.Notifier.listen(:insert) :ok = Oban.Notifier.notify(:signal, [%{data: "value"}]) receive do {:notification, :insert, payload} -> handle(payload) end ``` -------------------------------- ### Listen for Job Insertions with Oban Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/behavior-and-design.md Register a listener for job insertion events in Oban. Ensure the PubSub adapter is started and accessible via the provided name. ```elixir :ok = Oban.Notifier.listen(:insert) # Receive messages receive do {:notification, :insert, {oid, count}} -> ... end ``` -------------------------------- ### Oban Configuration with Phoenix Notifier Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/api-reference-oban-notifiers-phoenix.md Configure Oban to use the Phoenix notifier by specifying the pubsub name. This example shows the configuration in `config/runtime.exs` or `config.exs` and how to add Oban to the application's supervision tree. ```elixir # config/runtime.exs or config.exs config :my_app, Oban, engine: Oban.Engines.Basic, queues: [default: 10, priority: 5], plugins: [Oban.Plugins.Pruner], notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub}, repo: MyApp.Repo # And in your application supervision tree: defmodule MyApp.Application do use Application def start(_type, _args) do children = [ {Phoenix.PubSub, name: MyApp.PubSub}, {Oban, oban_config()}, # ... other children ] opts = [strategy: :one_for_one, name: MyApp.Supervisor] Supervisor.start_link(children, opts) end defp oban_config do Application.get_env(:my_app, Oban) end end ``` -------------------------------- ### Real-Time Updates via WebSocket with Phoenix Channel Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md This example demonstrates setting up a Phoenix endpoint and a channel to receive and broadcast real-time job updates. It listens for insert notifications and pushes them to connected clients. ```elixir defmodule MyApp.Endpoint.UserSocket do use Phoenix.Socket channel "updates:*", MyApp.UpdateChannel end defmodule MyApp.UpdateChannel do use Phoenix.Channel def join("updates:" <> user_id, _payload, socket) do Oban.Notifier.listen(:insert) {:ok, assign(socket, :user_id, user_id)} end def handle_info({:notification, :insert, payload}, socket) do push(socket, "new_job", payload) {:noreply, socket} end end ``` -------------------------------- ### Test Notification Handler Processing Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md Starts a notification handler process and sends a notification to verify that the handler receives and processes it correctly. ```elixir defmodule MyApp.NotificationHandlerTest do use ExUnit.Case setup do # Setup as above :ok end test "handler receives and processes notifications" do # Start your handler process {:ok, _pid} = MyApp.NotificationHandler.start_link([]) # Send notification :ok = Oban.Notifier.notify(:insert, [%{job_id: 123}]) # Allow time for async processing Process.sleep(100) # Assert state changed or side effect occurred assert MyApp.NotificationHandler.get_state()[:last_job_id] == 123 end end ``` -------------------------------- ### Batching High-Frequency Notifications Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md For high-frequency events, batching notifications can distribute load over time. Instead of notifying for each job individually, group multiple jobs into a single notification. This example shows the conceptual difference between notifying for each job versus batching. ```elixir # Instead of notifying for each job: # notify(:insert, [job1, job2, job3, ...]) # One notification with multiple jobs # Distribute over time: # notify(:insert, [job1]) # notify(:insert, [job2]) # notify(:insert, [job3]) ``` -------------------------------- ### Configure Oban and PubSub in runtime.exs Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Set up Oban with the Phoenix notifier and configure the PubSub adapter and queues by reading environment variables. Includes helper functions for parsing queue definitions. ```elixir # config/runtime.exs config :my_app, Oban, notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub}, repo: MyApp.Repo, engine: Oban.Engines.Basic, queues: parse_queues(System.get_env("OBAN_QUEUES")) config :my_app, MyApp.PubSub, adapter: parse_pubsub_adapter(System.get_env("PUBSUB_ADAPTER", "pg")) defp parse_pubsub_adapter("redis"), do: Phoenix.PubSub.Redis defp parse_pubsub_adapter(_), do: Phoenix.PubSub.PG defp parse_queues(nil), do: [default: 10] defp parse_queues(str) str |> String.split(",") |> Enum.map(&parse_queue_definition/1) end defp parse_queue_definition(queue_def) case String.split(queue_def, ":") do [queue, count] -> {String.to_atom(queue), String.to_integer(count)} [queue] -> {String.to_atom(queue), 10} end end ``` -------------------------------- ### Monitor Oban Broadcast Calls Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/oban-notifier-behavior.md Add debugging to broadcast calls to observe when notifications occur. This example sends a signal with a debug message. ```elixir # Add debugging to broadcast :ok = Oban.Notifier.notify(:signal, [%{debug: "message"}]) # Should print or log when notification occurs ``` -------------------------------- ### Development Oban with PG Adapter Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Configuration for local development using the default PG (Process Groups) adapter for PubSub. Includes Oban Pruner plugin and Basic engine. ```elixir # config/dev.exs config :my_app, Oban, notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub}, repo: MyApp.Repo, queues: [default: 10], plugins: [Oban.Plugins.Pruner], engine: Oban.Engines.Basic config :my_app, MyApp.PubSub, adapter: Phoenix.PubSub.PG ``` -------------------------------- ### Phoenix Notifier Options Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/types.md The keyword list passed to `start_link/1` when configuring the Phoenix notifier. `pubsub` is required and must match the registered Phoenix.PubSub instance name. ```elixir [ pubsub: atom(), name: atom() ] ``` -------------------------------- ### Configure Phoenix.PubSub Instance Name Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/README.md Define the name for your application's Phoenix.PubSub instance in your supervision tree. ```elixir def start(_type, _args) do children = [ {Phoenix.PubSub, name: MyApp.PubSub}, ... ``` -------------------------------- ### Run Local CI Checks Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/README.md Execute `mix test.ci` to run formatting checks, dependency checks, linting with Credo, and all tests. ```bash mix test.ci ``` -------------------------------- ### Configure Redis PubSub Adapter Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Configure the Redis adapter for distributed notifications across multiple nodes or when persistence is desired. Requires the `:phoenix_pubsub_redis` dependency. ```elixir config :my_app, MyApp.PubSub, adapter: Phoenix.PubSub.Redis, redis_opts: [ host: "localhost", port: 6379 ] ``` ```elixir defp deps do [ {:phoenix_pubsub_redis, "~> 2.0"}, {:oban_notifiers_phoenix, "~> 0.2"} ] end ``` -------------------------------- ### Configure Oban Notifier with Phoenix.PubSub Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/README.md Configure Oban to use the Phoenix Notifier by specifying the `pubsub` instance name and the `repo`. This can be done in your application's supervision tree or in `config.exs`. ```elixir # In application supervision tree {Phoenix.PubSub, name: MyApp.PubSub}, {Oban, [ notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub}, repo: MyApp.Repo, queues: [default: 10] ]} ``` ```elixir # Or in config config :my_app, Oban, notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub}, repo: MyApp.Repo, queues: [default: 10] ``` -------------------------------- ### Handle Registry Lookup Failures in Oban Notifiers Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/behavior-and-design.md This code handles cases where a notifier process is not found in the registry, typically occurring before Oban starts or after a notifier crashes. Oban's supervision tree will automatically restart the notifier. ```elixir nil -> {:error, RuntimeError.exception("no notifier running as #{inspect(name)}")} ``` -------------------------------- ### Configure PG PubSub Adapter Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Use the default PG adapter for notifications within a single node or clustered deployments on the same node. ```elixir config :my_app, MyApp.PubSub, adapter: Phoenix.PubSub.PG ``` -------------------------------- ### Efficient Registry Lookup Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/behavior-and-design.md Demonstrates efficient, non-blocking state retrieval using direct registry lookup, avoiding GenServer call serialization. ```elixir # Efficient — direct registry lookup Registry.lookup(Oban.Registry, {state.conf.name, Oban.Notifier}) ``` ```elixir # Avoided — would serialize concurrent calls GenServer.call(server, :get_state) ``` -------------------------------- ### Channel String Conversion in Oban Notifiers Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/behavior-and-design.md Illustrates how channel names are consistently converted to strings, losing the distinction between atoms and strings. Use this to understand how channel subscriptions are managed. ```elixir listen(:signal) # Stored as "signal" listen("signal") # Same as above ``` -------------------------------- ### Verify PubSub is Running Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/oban-notifier-behavior.md Check if the application's PubSub process is registered. This is a prerequisite for the Oban notifier to function correctly. ```elixir # Verify PubSub is running Process.whereis(MyApp.PubSub) ``` -------------------------------- ### Production Oban with Redis PubSub Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Configuration for a distributed deployment using Redis as the PubSub backend. Adjust Redis connection details as needed. ```elixir # config/runtime.exs config :my_app, Oban, notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub}, repo: MyApp.Repo, queues: [default: 10, priority: 5, mailers: 2] config :my_app, MyApp.PubSub, adapter: Phoenix.PubSub.Redis, redis_opts: [ host: System.get_env("REDIS_HOST"), port: String.to_integer(System.get_env("REDIS_PORT") || "6379"), password: System.get_env("REDIS_PASSWORD"), ssl: String.to_existing_atom(System.get_env("REDIS_SSL") || "false") ] ``` -------------------------------- ### Multiple Oban Instances with Phoenix Notifiers Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/types.md Advanced configuration for running multiple Oban instances, each with its own Phoenix.PubSub and notifier GenServer. Each instance requires a unique `pubsub` and `name` for its notifier. ```elixir children = [ {Phoenix.PubSub, name: MyApp.PubSub1}, {Phoenix.PubSub, name: MyApp.PubSub2}, {Oban, [ notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub1, name: ObanInstance1}, # ... other config ]}, {Oban, [ notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub2, name: ObanInstance2}, # ... other config ]} ] ``` -------------------------------- ### Add Oban.Notifiers.Phoenix Dependency Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/README.md Add the oban_notifiers_phoenix package to your project's dependencies in `mix.exs`. ```elixir defp deps do [ {:oban_notifiers_phoenix, "~> 0.1"}, ... ] end ``` -------------------------------- ### Notification-Driven State Synchronization with GenServer Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md This GenServer listens for job insert and signal notifications to synchronize internal state. It demonstrates how to update state based on notification payloads and handle custom signals. ```elixir defmodule MyApp.JobTracker do use GenServer def start_link(_opts) do GenServer.start_link(__MODULE__, %{}, name: __MODULE__) end def init(state) do Oban.Notifier.listen([:insert, :signal]) {:ok, state} end def handle_info({:notification, :insert, {oid, count}}, state) do # Update internal state when jobs are added new_state = Map.put(state, :pending_count, count) {:noreply, new_state} end def handle_info({:notification, :signal, payload}, state) do # Handle custom signals IO.inspect(payload, label: "Signal received") {:noreply, state} end def get_pending_count do GenServer.call(__MODULE__, :count) end def handle_call(:count, _from, state) do {:reply, Map.get(state, :pending_count, 0), state} end end ``` -------------------------------- ### Add Oban Notifiers Phoenix to Dependencies Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/START-HERE.md Add the oban_notifiers_phoenix, oban, and phoenix_pubsub dependencies to your project's `deps.ex` file. ```elixir defp deps do [ {:oban, "~> 2.17"}, {:phoenix_pubsub, "~> 2.0"}, {:oban_notifiers_phoenix, "~> 0.2"}, ] end ``` -------------------------------- ### Validate Oban Configuration Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Use this module to validate Oban configuration at application startup, ensuring the notifier and its options are correctly set. It checks for the presence of Oban configuration and specifically for the 'pubsub' option when using Oban.Notifiers.Phoenix. ```elixir defmodule MyApp.ConfigValidator do def validate! do case Application.get_env(:my_app, Oban) do nil -> raise "Oban not configured" config -> case Keyword.fetch(config, :notifier) do {:ok, {Oban.Notifiers.Phoenix, opts}} -> unless Keyword.has_key?(opts, :pubsub) do raise "Oban.Notifiers.Phoenix requires pubsub option" end _ -> :ok end end end end ``` ```elixir # In your Application.start/2: def start(_type, _args) do MyApp.ConfigValidator.validate!() # ... rest of startup end ``` -------------------------------- ### listen/2 Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/api-reference-oban-notifiers-phoenix.md Subscribes to one or more notification channels using Phoenix.PubSub. It takes the notifier server identifier and the channel(s) to listen on. ```APIDOC ## Function: listen/2 Subscribes to one or more notification channels using Phoenix.PubSub. ### Function Signature ```elixir @impl Notifier def listen(server, channels) ``` ### Parameters - **server** (atom() | pid()) - Required - The notifier server name or process identifier - **channels** (list(atom() | String.t()) | atom() | String.t()) - Required - Single channel or list of channels to subscribe to ### Return Value `:ok` on successful subscription ### Errors - Returns `{:error, RuntimeError}` with message "no notifier running as ..." if the notifier is not running ### Example ```elixir # Listen to a single channel :ok = Notifier.listen(:signal) # Listen to multiple channels :ok = Notifier.listen([:signal, :insert, :gossip]) # Channels are automatically converted to strings for PubSub ``` ``` -------------------------------- ### Listen to Multiple Channels Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md Subscribe to multiple notification channels simultaneously by passing a list of channel names to `Oban.Notifier.listen/1`. Handle different channels using a case statement. ```elixir # Listen to multiple channels at once :ok = Oban.Notifier.listen([:insert, :signal, :gossip]) # Handle different channels def handle_info({:notification, channel, payload}, state) do case channel do :insert -> handle_insert(payload) :signal -> handle_signal(payload) :gossip -> handle_gossip(payload) end {:noreply, state} end ``` -------------------------------- ### Configure Oban Phoenix Notifier Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/oban-notifier-behavior.md Configure Oban to use the Phoenix Notifier, which integrates with Phoenix.PubSub. Specify the PubSub adapter to use. ```elixir config :oban, Oban, notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub} ``` -------------------------------- ### Per-Test Oban Configuration with Phoenix Notifier Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/configuration.md Sets up an isolated PubSub instance and Oban with the Phoenix notifier for each test. Ensures test isolation by using unique PubSub names and an isolated peer engine. ```elixir defmodule MyApp.ObanTest do use ExUnit.Case, async: true setup do # Create an isolated PubSub instance for this test start_supervised!({Phoenix.PubSub, name: unique_pubsub_name()}) # Start Oban with test configuration opts = [ notifier: {Oban.Notifiers.Phoenix, pubsub: unique_pubsub_name()}, repo: MyApp.Repo, peer: Oban.Peers.Isolated, engine: Oban.Engines.Basic ] start_supervised!({Oban, opts}) :ok end defp unique_pubsub_name do Module.concat(MyApp.PubSub, System.unique_integer([:positive])) end test "notifications are received" do assert :ok = Oban.Notifier.listen(:signal) assert :ok = Oban.Notifier.notify(:signal, [%{test: "data"}]) assert_receive {:notification, :signal, _} end end ``` -------------------------------- ### Subscribe to a Channel with Metadata Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/behavior-and-design.md Subscribe a process to a specific channel using Phoenix.PubSub. The `metadata: __MODULE__` option ensures that messages are routed to the Oban Notifier's dispatch function. ```elixir PubSub.subscribe(pubsub, to_string(channel), metadata: __MODULE__) ``` -------------------------------- ### Configure Oban Notifiers with Phoenix PubSub Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md Configure Oban to use Phoenix PubSub for notifications and specify your Redis connection options and Oban repository. ```elixir # config/runtime.exs config :my_app, MyApp.PubSub, adapter: Phoenix.PubSub.Redis, redis_opts: [ host: System.get_env("REDIS_HOST", "localhost"), port: String.to_integer(System.get_env("REDIS_PORT", "6379")), password: System.get_env("REDIS_PASSWORD") ] config :my_app, Oban, notifier: {Oban.Notifiers.Phoenix, pubsub: MyApp.PubSub}, repo: MyApp.Repo, queues: [default: 10] ``` -------------------------------- ### Listen for and Broadcast Job Notifications Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/README.md Use `Oban.Notifier.listen/1` to subscribe to job insertion events and `Oban.Notifier.notify/2` to broadcast custom signals. Received messages are pattern-matched in a `receive` block. ```elixir # Listen for job insertions :ok = Oban.Notifier.listen(:insert) # Broadcast a notification :ok = Oban.Notifier.notify(:signal, [%{action: "test"}]) # Receive message receive do {:notification, :insert, {oid, count}} -> handle_job_insert(oid, count) {:notification, :signal, payload} -> handle_signal(payload) end ``` -------------------------------- ### Oban Notification Test Structure Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md Sets up an isolated Phoenix PubSub and Oban instance for testing notification broadcasting and handling. ```elixir defmodule MyApp.NotificationTest do use ExUnit.Case, async: true setup do # Create isolated PubSub for this test pubsub_name = :"test_pubsub_#{System.unique_integer()}" start_supervised!({Phoenix.PubSub, name: pubsub_name}) # Start Oban with the test PubSub start_supervised!({Oban, [ notifier: {Oban.Notifiers.Phoenix, pubsub: pubsub_name}, repo: MyApp.Repo, peer: Oban.Peers.Isolated, engine: Oban.Engines.Basic ]}) {:ok, pubsub_name: pubsub_name} end test "broadcasts notifications to subscribers" do assert :ok = Oban.Notifier.listen(:signal) assert :ok = Oban.Notifier.notify(:signal, [%{action: "test"}]) assert_receive {:notification, :signal, %{"action" => "test"}} end test "handles multiple channels" do assert :ok = Oban.Notifier.listen([:insert, :signal]) assert :ok = Oban.Notifier.notify(:insert, [%{count: 5}]) assert_receive {:notification, :insert, %{"count" => 5}} end test "unsubscription prevents message receipt" do assert :ok = Oban.Notifier.listen(:signal) assert :ok = Oban.Notifier.unlisten(:signal) assert :ok = Oban.Notifier.notify(:signal, [%{data: "test"}]) refute_received {:notification, :signal, _} end test "complex types are serialized correctly" do Oban.Notifier.listen(:signal) Oban.Notifier.notify(:signal, [%{ date: ~D[2021-08-09], keyword: [a: 1, b: 2], tuple: {1, 2} }]) assert_receive {:notification, :signal, payload} assert payload["date"] == "2021-08-09" assert payload["keyword"] == [["a", 1], ["b", 2]] assert payload["tuple"] == [1, 2] end end ``` -------------------------------- ### Broadcast Message via Phoenix.PubSub Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/behavior-and-design.md Use this to broadcast a message through Phoenix.PubSub. Ensure the PubSub instance, channel, and payload are correctly formatted. The metadata tag `__MODULE__` is used for dispatching. ```elixir PubSub.broadcast( pubsub, to_string(channel), # "signal", "insert", etc. {conf.name, channel, payload}, # Message format __MODULE__ # Metadata/dispatcher identifier ) ``` -------------------------------- ### Lookup GenServer State in Registry Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/types.md The notifier's state is stored in Oban.Registry. This shows the internal pattern used for lookup, which returns the PID and state or nil. ```elixir # Internal pattern used in the notifier Registry.lookup(Oban.Registry, {conf.name, Oban.Notifier}) # Returns: {pid, state} or nil ``` -------------------------------- ### Batch Notification Processing with GenServer and Timers Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/usage-guide.md This GenServer accumulates notifications and processes them in batches after a short delay. It uses `Process.send_after` to trigger batch processing and resets the timer on each new notification. ```elixir defmodule MyApp.BatchNotificationHandler do use GenServer def start_link(opts) do GenServer.start_link(__MODULE__, opts, name: __MODULE__) end def init(_opts) do Oban.Notifier.listen(:insert) {:ok, %{notifications: [], timer: nil}} end def handle_info({:notification, :insert, data}, state) do # Accumulate notifications state = update_in(state.notifications, &[data | &1]) # Cancel existing timer if state.timer, do: Process.cancel_timer(state.timer) # Set new timer to process batch after 1 second timer = Process.send_after(self(), :process_batch, 1000) {:noreply, %{state | timer: timer}} end def handle_info(:process_batch, state) do # Process all accumulated notifications IO.inspect(state.notifications, label: "Processing batch") {:noreply, %{state | notifications: [], timer: nil}} end end ``` -------------------------------- ### Oban.Notifiers.Phoenix child_spec/1 Implementation Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/oban-notifier-behavior.md Implements the child_spec callback by delegating to GenServer's default implementation for standard supervision. ```elixir @doc false def child_spec(opts), do: super(opts) ``` -------------------------------- ### Successful Operation Return Type Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/types.md The :ok atom is returned by `listen/2`, `unlisten/2`, and `notify/3` functions upon successful completion. ```elixir :ok ``` -------------------------------- ### Backward Compatibility Dispatcher for Oban Notifier (Pre-0.2.0) Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/oban-notifier-behavior.md This clause handles messages from pre-0.2.0 notifier versions, which used a full config struct, ensuring graceful upgrade compatibility. ```elixir def dispatch(entries, _from, {conf, channel, payload}) do dispatch(entries, :none, {conf.name, conf.node, channel, payload}) end ``` -------------------------------- ### Listen to Notification Channels with Oban.Notifiers.Phoenix Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/api-reference-oban-notifiers-phoenix.md Subscribe to one or more notification channels using the `listen/2` function. Channels can be atoms or strings, and they are automatically converted to strings for Phoenix.PubSub. Ensure the notifier is running before attempting to listen. ```elixir :ok = Notifier.listen(:signal) ``` ```elixir :ok = Notifier.listen([:signal, :insert, :gossip]) ``` -------------------------------- ### Oban.Notifier Behavior Callbacks Source: https://github.com/oban-bg/oban_notifiers_phoenix/blob/main/_autodocs/oban-notifier-behavior.md Defines the required callback functions for any Oban.Notifier implementation. ```elixir @callback child_spec(opts :: Keyword.t()) :: Supervisor.child_spec() @callback start_link(opts :: Keyword.t()) :: GenServer.on_start() @callback listen(server :: atom() | pid(), channels :: list() | atom()) :: :ok | {:error, term()} @callback unlisten(server :: atom() | pid(), channels :: list() | atom()) :: :ok | {:error, term()} @callback notify(server :: atom() | pid(), channel :: atom(), payload :: list()) :: :ok | {:error, term()} ```