Skip to content

Commit 00c9d20

Browse files
authored
Add unsubscribe function that matches on metadata (#206)
This allows multiple subscriptions to the same topic and later the ability to remove a specific subscription instead of "all" subscriptions for the topic.
1 parent 6ff3cd4 commit 00c9d20

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

lib/phoenix/pubsub.ex

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,28 @@ defmodule Phoenix.PubSub do
216216
Registry.unregister(pubsub, topic)
217217
end
218218

219+
@doc """
220+
Unsubscribes the caller from the PubSub adapter's topic taking the metadata into consideration.
221+
222+
Unlike `unsubscribe/2`, this function matches on the metadata provided as an option when subscribed.
223+
This is useful when you have multiple subscriptions for the same topic with different metadata.
224+
225+
## Example
226+
227+
iex> PubSub.subscribe_match(:my_pubsub, "users:123", metadata: :fast)
228+
:ok
229+
iex> PubSub.subscribe_match(:my_pubsub, "users:123", metadata: :slow)
230+
:ok
231+
iex> PubSub.unsubscribe_match(:my_pubsub, "users:123", :fast)
232+
:ok
233+
# Only the :fast subscription is removed, :slow remains active
234+
235+
"""
236+
@spec unsubscribe_match(t, topic, term) :: :ok
237+
def unsubscribe_match(pubsub, topic, metadata) when is_atom(pubsub) and is_binary(topic) do
238+
Registry.unregister_match(pubsub, topic, metadata)
239+
end
240+
219241
@doc """
220242
Broadcasts message on given topic across the whole cluster.
221243

test/shared/pubsub_test.exs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,39 @@ defmodule Phoenix.PubSubTest do
7272
assert subscribers(config, config.topic) |> length == 0
7373
end
7474

75+
@tag pool_size: size
76+
test "pool #{size}: subscribe and unsubscribe with metadata", config do
77+
pid = spawn_pid()
78+
pid2 = spawn_pid()
79+
assert subscribers(config, config.topic) |> length == 0
80+
81+
# Subscribe with different metadata variants
82+
assert rpc(pid, fn ->
83+
PubSub.subscribe(config.pubsub, config.topic, metadata: :custom)
84+
end)
85+
86+
assert rpc(pid, fn ->
87+
PubSub.subscribe(config.pubsub, config.topic, metadata: :other)
88+
end)
89+
90+
assert rpc(pid2, fn -> PubSub.subscribe(config.pubsub, config.topic) end)
91+
92+
# Verify all subscriptions exist
93+
assert length(subscribers(config, config.topic)) == 3
94+
assert {pid, :custom} in subscribers(config, config.topic)
95+
assert {pid, :other} in subscribers(config, config.topic)
96+
assert {pid2, nil} in subscribers(config, config.topic)
97+
98+
# Unsubscribe only the :custom metadata subscription
99+
assert rpc(pid, fn -> PubSub.unsubscribe_match(config.pubsub, config.topic, :custom) end)
100+
101+
# Verify only :custom was removed, others remain
102+
assert length(subscribers(config, config.topic)) == 2
103+
assert {pid, :other} in subscribers(config, config.topic)
104+
assert {pid2, nil} in subscribers(config, config.topic)
105+
refute {pid, :custom} in subscribers(config, config.topic)
106+
end
107+
75108
@tag pool_size: size
76109
test "pool #{size}: broadcast/3 and broadcast!/3 publishes message to each subscriber",
77110
config do

0 commit comments

Comments
 (0)