11defmodule Phoenix.Tracker do
22 @ moduledoc ~S"""
3- Provides distributed Presence tracking to processes.
4-
5- The `Tracker` API is used as a facade for a pool of `Phoenix.Tracker.Shard`s.
6- The responsibility of which calls go to which `Shard` is determined based on
7- the topic, on which a given function is called.
3+ Provides distributed presence tracking to processes.
84
95 Tracker shards use a heartbeat protocol and CRDT to replicate presence
106 information across a cluster in an eventually consistent, conflict-free
117 manner. Under this design, there is no single source of truth or global
12- process. Each node runs a pool of `Phoenix.Tracker.Shard`s and node-local
13- changes are replicated across the cluster and handled locally as a diff of
14- changes.
15-
16- * `tracker` - The name of the tracker handler module implementing the
17- `Phoenix.Tracker` behaviour
18- * `tracker_opts` - The list of options to pass to the tracker handler
19- * `pool_opts` - The list of options used to construct the shard pool
20-
21- ## Required `pool_opts`:
22-
23- * `:name` - The name of the server, such as: `MyApp.Tracker`
24- This will also form the common prefix for all shard names
25- * `:pubsub_server` - The name of the PubSub server, such as: `MyApp.PubSub`
26-
27- ## Optional `pool_opts`:
28-
29- * `:broadcast_period` - The interval in milliseconds to send delta broadcasts
30- across the cluster. Default `1500`
31- * `:max_silent_periods` - The max integer of broadcast periods for which no
32- delta broadcasts have been sent. Default `10` (15s heartbeat)
33- * `:down_period` - The interval in milliseconds to flag a replica
34- as temporarily down. Default `broadcast_period * max_silent_periods * 2`
35- (30s down detection). Note: This must be at least 2x the `broadcast_period`.
36- * `:permdown_period` - The interval in milliseconds to flag a replica
37- as permanently down, and discard its state.
38- Note: This must be at least greater than the `down_period`.
39- Default `1_200_000` (20 minutes)
40- * `:clock_sample_periods` - The numbers of heartbeat windows to sample
41- remote clocks before collapsing and requesting transfer. Default `2`
42- * `:max_delta_sizes` - The list of delta generation sizes to keep before
43- falling back to sending entire state. Defaults `[100, 1000, 10_000]`.
44- * `:log_level` - The log level to log events, defaults `:debug` and can be
45- disabled with `false`
46- * `:pool_size` - The number of tracker shards to launch. Default `1`
8+ process. Each node runs a pool of trackers and node-local changes are
9+ replicated across the cluster and handled locally as a diff of changes.
4710
4811 ## Implementing a Tracker
4912
@@ -87,8 +50,8 @@ defmodule Phoenix.Tracker do
8750 end
8851 end
8952
90- Trackers must implement `start_link/1`, `init/1`, and `handle_diff/2`.
91- The `init/1` callback allows the tracker to manage its own state when
53+ Trackers must implement `start_link/1`, `c: init/1`, and `c: handle_diff/2`.
54+ The `c: init/1` callback allows the tracker to manage its own state when
9255 running within the `Phoenix.Tracker` server. The `handle_diff` callback
9356 is invoked with a diff of presence join and leave events, grouped by
9457 topic. As replicas heartbeat and replicate data, the local tracker state is
@@ -255,6 +218,7 @@ defmodule Phoenix.Tracker do
255218 iex> Phoenix.Tracker.get_by_key(MyTracker, "lobby", "user1")
256219 [{#PID<0.88.0>, %{name: "User 1"}, {#PID<0.89.0>, %{name: "User 1"}]
257220 """
221+ @ spec get_by_key ( tracker , topic , term ) :: [ presence ]
258222 def get_by_key ( tracker_name , topic , key ) do
259223 tracker_name
260224 |> Shard . name_for_topic ( topic , pool_size ( tracker_name ) )
@@ -275,13 +239,46 @@ defmodule Phoenix.Tracker do
275239 Supervisor . stop ( tracker_name )
276240 end
277241
278- def start_link ( tracker , tracker_opts , pool_opts ) do
242+ @ doc """
243+ Starts a tracker pool.
244+
245+ * `tracker` - The tracker module implementing the `Phoenix.Tracker` behaviour
246+ * `tracker_arg` - The argument to pass to the tracker handler `c:init/1`
247+ * `pool_opts` - The list of options used to construct the shard pool
248+
249+ ## Required `pool_opts`:
250+
251+ * `:name` - The name of the server, such as: `MyApp.Tracker`
252+ This will also form the common prefix for all shard names
253+ * `:pubsub_server` - The name of the PubSub server, such as: `MyApp.PubSub`
254+
255+ ## Optional `pool_opts`:
256+
257+ * `:broadcast_period` - The interval in milliseconds to send delta broadcasts
258+ across the cluster. Default `1500`
259+ * `:max_silent_periods` - The max integer of broadcast periods for which no
260+ delta broadcasts have been sent. Default `10` (15s heartbeat)
261+ * `:down_period` - The interval in milliseconds to flag a replica
262+ as temporarily down. Default `broadcast_period * max_silent_periods * 2`
263+ (30s down detection). Note: This must be at least 2x the `broadcast_period`.
264+ * `:permdown_period` - The interval in milliseconds to flag a replica
265+ as permanently down, and discard its state.
266+ Note: This must be at least greater than the `down_period`.
267+ Default `1_200_000` (20 minutes)
268+ * `:clock_sample_periods` - The numbers of heartbeat windows to sample
269+ remote clocks before collapsing and requesting transfer. Default `2`
270+ * `:max_delta_sizes` - The list of delta generation sizes to keep before
271+ falling back to sending entire state. Defaults `[100, 1000, 10_000]`.
272+ * `:log_level` - The log level to log events, defaults `:debug` and can be
273+ disabled with `false`
274+ * `:pool_size` - The number of tracker shards to launch. Default `1`
275+ """
276+ def start_link ( tracker , tracker_arg , pool_opts ) do
279277 name = Keyword . fetch! ( pool_opts , :name )
280- Supervisor . start_link ( __MODULE__ ,
281- [ tracker , tracker_opts , pool_opts , name ] ,
282- name: name )
278+ Supervisor . start_link ( __MODULE__ , [ tracker , tracker_arg , pool_opts , name ] , name: name )
283279 end
284280
281+ @ doc false
285282 def init ( [ tracker , tracker_opts , opts , name ] ) do
286283 pool_size = Keyword . get ( opts , :pool_size , 1 )
287284 ^ name = :ets . new ( name , [ :set , :named_table , read_concurrency: true ] )
0 commit comments