Skip to content

Commit 3a09787

Browse files
authored
feat: custom dispatcher for Presence handle_diff broadcast (#6500)
1 parent 163e3fc commit 3a09787

File tree

2 files changed

+78
-10
lines changed

2 files changed

+78
-10
lines changed

lib/phoenix/presence.ex

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,21 @@ defmodule Phoenix.Presence do
7979
8080
See `c:list/1` for more information on the presence data structure.
8181
82+
## Custom dispatcher
83+
84+
It's possible to customize the dispatcher module used to broadcast.
85+
By default, `Phoenix.Channel.Server` is used, which is the same dispatcher
86+
used by channels. To customize the dispatcher, pass the `:dispatcher` option
87+
when using `Phoenix.Presence`:
88+
89+
use Phoenix.Presence,
90+
otp_app: :my_app,
91+
pubsub_server: MyApp.PubSub,
92+
dispatcher: MyApp.CustomDispatcher
93+
94+
See `m:Phoenix.PubSub#module-custom-dispatching` for more information on
95+
custom dispatchers.
96+
8297
## Fetching Presence Information
8398
8499
Presence metadata should be minimized and used to store small,
@@ -417,9 +432,11 @@ defmodule Phoenix.Presence do
417432
pubsub_server =
418433
opts[:pubsub_server] || raise "use Phoenix.Presence expects :pubsub_server to be given"
419434

435+
dispatcher = opts[:dispatcher] || Phoenix.Channel.Server
436+
420437
Phoenix.Tracker.start_link(
421438
__MODULE__,
422-
{module, task_supervisor, pubsub_server},
439+
{module, task_supervisor, pubsub_server, dispatcher},
423440
opts
424441
)
425442
end
@@ -455,15 +472,16 @@ defmodule Phoenix.Presence do
455472
end
456473

457474
@doc false
458-
def init({module, task_supervisor, pubsub_server}) do
475+
def init({module, task_supervisor, pubsub_server, dispatcher}) do
459476
state = %{
460477
module: module,
461478
task_supervisor: task_supervisor,
462479
pubsub_server: pubsub_server,
463480
topics: %{},
464481
tasks: :queue.new(),
465482
current_task: nil,
466-
client_state: nil
483+
client_state: nil,
484+
dispatcher: dispatcher
467485
}
468486

469487
client_state =
@@ -507,12 +525,13 @@ defmodule Phoenix.Presence do
507525
Task.shutdown(task)
508526

509527
Enum.each(computed_diffs, fn {topic, presence_diff} ->
510-
Phoenix.Channel.Server.local_broadcast(
511-
state.pubsub_server,
512-
topic,
513-
"presence_diff",
514-
presence_diff
515-
)
528+
broadcast = %Phoenix.Socket.Broadcast{
529+
topic: topic,
530+
event: "presence_diff",
531+
payload: presence_diff
532+
}
533+
534+
Phoenix.PubSub.local_broadcast(state.pubsub_server, topic, broadcast, state.dispatcher)
516535
end)
517536

518537
new_state =

test/phoenix/presence_test.exs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ defmodule Phoenix.PresenceTest do
3434
Phoenix.Presence.init({
3535
__MODULE__,
3636
__MODULE__.TaskSupervisor,
37-
PresPub
37+
PresPub,
38+
Phoenix.Channel.Server
3839
})
3940
end
4041

@@ -43,13 +44,27 @@ defmodule Phoenix.PresenceTest do
4344
end
4445
end
4546

47+
defmodule CustomDispatcher do
48+
def dispatch(entries, from, message) do
49+
for {pid, _} <- entries, pid != from, do: send(pid, {:custom_dispatcher, message})
50+
51+
:ok
52+
end
53+
end
54+
55+
defmodule CustomDispatcherPresence do
56+
use Phoenix.Presence, otp_app: :phoenix, dispatcher: CustomDispatcher
57+
end
58+
4659
Application.put_env(:phoenix, MyPresence, pubsub_server: PresPub)
4760
Application.put_env(:phoenix, MetasPresence, pubsub_server: PresPub)
61+
Application.put_env(:phoenix, CustomDispatcherPresence, pubsub_server: PresPub)
4862

4963
setup_all do
5064
start_supervised!({Phoenix.PubSub, name: PresPub, pool_size: 1})
5165
start_supervised!(MyPresence)
5266
start_supervised!(MetasPresence)
67+
start_supervised!(CustomDispatcherPresence)
5368
{:ok, pubsub: PresPub}
5469
end
5570

@@ -174,6 +189,40 @@ defmodule Phoenix.PresenceTest do
174189
assert MyPresence.list(topic) == %{}
175190
end
176191

192+
test "handle_diff with custom dispatcher", %{topic: topic} = config do
193+
pid = spawn(fn -> :timer.sleep(:infinity) end)
194+
Phoenix.PubSub.subscribe(config.pubsub, topic)
195+
start_supervised!({DefaultPresence, pubsub_server: config.pubsub})
196+
CustomDispatcherPresence.track(pid, topic, "u1", %{name: "u1"})
197+
198+
assert_receive {:custom_dispatcher,
199+
%Broadcast{
200+
topic: ^topic,
201+
event: "presence_diff",
202+
payload: %{
203+
joins: %{"u1" => %{metas: [%{name: "u1", phx_ref: u1_ref}]}},
204+
leaves: %{}
205+
}
206+
}}
207+
208+
assert %{"u1" => %{metas: [%{name: "u1", phx_ref: ^u1_ref}]}} =
209+
CustomDispatcherPresence.list(topic)
210+
211+
Process.exit(pid, :kill)
212+
213+
assert_receive {:custom_dispatcher,
214+
%Broadcast{
215+
topic: ^topic,
216+
event: "presence_diff",
217+
payload: %{
218+
joins: %{},
219+
leaves: %{"u1" => %{metas: [%{name: "u1", phx_ref: ^u1_ref}]}}
220+
}
221+
}}
222+
223+
assert CustomDispatcherPresence.list(topic) == %{}
224+
end
225+
177226
test "untrack with pid", %{topic: topic} = config do
178227
Phoenix.PubSub.subscribe(config.pubsub, config.topic)
179228
MyPresence.track(self(), config.topic, "u1", %{})

0 commit comments

Comments
 (0)