Skip to content

Add configurable default dispatcher#208

Closed
twinn wants to merge 1 commit intophoenixframework:mainfrom
twinn:feature/default-dispatcher
Closed

Add configurable default dispatcher#208
twinn wants to merge 1 commit intophoenixframework:mainfrom
twinn:feature/default-dispatcher

Conversation

@twinn
Copy link
Copy Markdown
Contributor

@twinn twinn commented Apr 15, 2026

Summary

Adds a :dispatcher option to child_spec/start_link that sets the default dispatcher module for all broadcast functions. Defaults to Phoenix.PubSub (preserving existing behavior). Can be overridden per-call.

{Phoenix.PubSub, name: :my_pubsub, dispatcher: MyApp.Dispatcher}

Motivation

The custom dispatching feature is powerful, but in practice it's difficult to use as a default for an application. The current options are:

  1. Pass the dispatcher explicitly on every broadcast call
  2. Create a wrapper module that injects the dispatcher

Both add boilerplate for what is conceptually a per-PubSub configuration. The wrapper approach is particularly fragile. Any code that calls Phoenix.PubSub.broadcast/3 directly instead of going through the wrapper bypasses the custom dispatcher entirely. In a growing codebase with multiple contributors, this is easy to miss.

The use case I ran into was filtering broadcasts in test environments, scoping delivery so that concurrent async tests sharing a topic don't leak messages to each other. A configurable default makes this a one-line change at startup rather than a module-level abstraction that every callsite must remember to use.

Trade-offs

The 3-arity broadcast functions (without an explicit dispatcher) now call Registry.meta/2 to look up the configured default. The 4-arity versions also call Registry.meta/2 for the adapter, so the 3-arity path makes two lookups total (one to get the default, one when delegating to the 4-arity head). Registry.meta reads from :persistent_term, so this is a nanosecond-level cost. I don't believe it's meaningful, but wanted to call it out.

Changes

  • Phoenix.PubSub.Supervisor accepts :dispatcher option, stores it in Registry meta
  • All broadcast functions have explicit 3-arity heads that read the default from meta, and 4-arity heads that accept an explicit dispatcher
  • 6 new tests covering default dispatcher, per-call override, and all broadcast variants
  • Fully backwards compatible

Thank you for Phoenix.PubSub. It's a great foundation and the dispatcher extension point was well designed. This just makes it a little easier to reach.

@twinn twinn force-pushed the feature/default-dispatcher branch 2 times, most recently from 116f859 to 5395b6b Compare April 15, 2026 14:15
Adds a :dispatcher option to child_spec/start_link that sets the
default dispatcher module for all broadcast functions. Defaults to
Phoenix.PubSub (preserving existing behavior). Can be overridden
per-call by passing a dispatcher to broadcast/4 and friends.

    {Phoenix.PubSub, name: :my_pubsub, dispatcher: MyApp.Dispatcher}
@twinn twinn force-pushed the feature/default-dispatcher branch from 5395b6b to 541436a Compare April 15, 2026 14:17
@josevalim
Copy link
Copy Markdown
Member

This adds additional lookups on every invocation, which we want to avoid. Our suggestion is to wrap the pubsub API in another module that enforces your dispatcher. Thanks!

@josevalim josevalim closed this Apr 15, 2026
@twinn
Copy link
Copy Markdown
Contributor Author

twinn commented Apr 15, 2026

@josevalim Understood, thanks for the quick feedback. I'll stick with the wrapper module approach and Credo rules to ensure we don't call Phoenix.PubSub directly. Too bad the meta pubsub isn't a map, could have been a single backward-compatible lookup.

@josevalim
Copy link
Copy Markdown
Member

Wait, I believe the meta is private, so we can change it? Do you think that could work?

@twinn
Copy link
Copy Markdown
Contributor Author

twinn commented Apr 15, 2026

@josevalim yeah as long as the value in Registry.meta(pubsub, :pubsub) is considered private we can support it with one lookup. Updated the branch to reflect that strategy. The meta :pubsub tuple is extended from {adapter, name} to {adapter, name, dispatcher}. Diff is here if you'd like to take a look: twinn/phoenix_pubsub@main...feature/default-dispatcher.

@josevalim
Copy link
Copy Markdown
Member

That looks good to me! Please send a new PR!

@josevalim
Copy link
Copy Markdown
Member

Apparently I cannot reopen this one :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants