defmodule BCUtils.PubSubManager do @pubsub_config [ adapter: Phoenix.PubSub.PG2, pool_size: 1 ] @moduledoc """ Manages Phoenix.PubSub startup to avoid conflicts when multiple applications in the same VM try to start the same PubSub instance. This module provides utilities to: - Check if a PubSub instance is already running - Conditionally start PubSub only if needed - Provide graceful fallbacks for shared PubSub instances - Health check existing PubSub instances ## Usage In your supervision tree: children = [ BCUtils.PubSubManager.maybe_child_spec(:my_pubsub), # other children... ] |> Enum.filter(& &1) # Remove nil entries ## Dependencies This module requires `phoenix_pubsub` to be available at runtime. Add it to your `mix.exs`: {:phoenix_pubsub, "~> 2.1"} """ @doc """ Returns a child spec for Phoenix.PubSub if not already started, otherwise nil. This is the recommended way to use this module in supervision trees. ## Examples # Will start PubSub if not running BCUtils.PubSubManager.maybe_child_spec(:my_pubsub) # Will return nil if already running BCUtils.PubSubManager.maybe_child_spec(:my_pubsub) # Usage in supervision tree children = [ BCUtils.PubSubManager.maybe_child_spec(:ex_esdb_pubsub), # other children... ] |> Enum.filter(& &1) # Remove nil entries ## Parameters - `pubsub_name` - The atom name for the PubSub instance, or nil - `opts` - Additional options to pass to Phoenix.PubSub (default: []) ## Returns - `{Phoenix.PubSub, keyword()}` - Child spec if PubSub needs to be started - `nil` - If PubSub is already running or pubsub_name is nil """ @spec maybe_child_spec(atom()) :: {module(), keyword()} | nil def maybe_child_spec(nil), do: nil def maybe_child_spec(pubsub_name) when is_atom(pubsub_name) do if Process.whereis(pubsub_name) do nil else config = Keyword.merge(@pubsub_config, name: pubsub_name) {Phoenix.PubSub, config} end end end