Phoenix.Presence
Phoenix.Presence behaviour
Provides Presence tracking to processes and channels.
This behaviour provides presence features such as fetching presences for a given topic, as well as handling diffs of join and leave events as they occur in real-time. Using this module defines a supervisor and allows the calling module to implement the Phoenix.Tracker
behaviour which starts a tracker process to handle presence information.
Example Usage
Start by defining a presence module within your application which uses Phoenix.Presence
and provide the :otp_app
which holds your configuration, as well as the :pubsub_server
.
defmodule MyApp.Presence do
use Phoenix.Presence, otp_app: :my_app,
pubsub_server: MyApp.PubSub
end
The :pubsub_server
must point to an existing pubsub server running in your application, which is included by default as MyApp.PubSub
for new applications.
Next, add the new supervisor to your supervision tree in lib/my_app.ex
:
children = [
...
supervisor(MyApp.Presence, []),
]
Once added, presences can be tracked in your channel after joining:
defmodule MyApp.MyChannel do
use MyApp.Web, :channel
alias MyApp.Presence
def join("some:topic", _params, socket) do
send(self, :after_join)
{:ok, assign(socket, :user_id, ...)}
end
def handle_info(:after_join, socket) do
push socket, "presence_state", Presence.list(socket)
{:ok, _} = Presence.track(socket, socket.assigns.user_id, %{
online_at: inspect(System.system_time(:seconds))
})
{:noreply, socket}
end
end
In the example above, Presence.track
is used to register this channel’s process as a presence for the socket’s user ID, with a map of metadata. Next, the current presence information for the socket’s topic is pushed to the client as a "presence_state"
event.
Finally, a diff of presence join and leave events will be sent to the client as they happen in real-time with the “presence_diff” event. The diff structure will be a map of :joins
and :leaves
of the form:
%{joins: %{"123" => %{metas: [%{status: "away", phx_ref: ...}]},
leaves: %{"456" => %{metas: [%{status: "online", phx_ref: ...}]},
See Phoenix.Presence.list/2
for more information on the presence datastructure.
Fetching Presence Information
Presence metadata should be minimized and used to store small, ephemeral state, such as a user’s “online” or “away” status. More detailed information, such as user details that need to be fetched from the database, can be achieved by overriding the fetch/2
function. The fetch/2
callback is triggered when using list/1
and serves as a mechanism to fetch presence information a single time, before broadcasting the information to all channel subscribers. This prevents N query problems and gives you a single place to group isolated data fetching to extend presence metadata. The function must return a map of data matching the outlined Presence datastructure, including the :metas
key, but can extend the map of information to include any additional information. For example:
def fetch(_topic, entries) do
query =
from u in User,
where: u.id in ^Map.keys(entries),
select: {u.id, u}
users = query |> Repo.all |> Enum.into(%{})
for {key, %{metas: metas}} <- entries, into: %{} do
{key, %{metas: metas, user: users[key]}}
end
end
The function above fetches all users from the database who have registered presences for the given topic. The fetched information is then extended with a :user
key of the user’s information, while maintaining the required :metas
field from the original presence data.
Summary
Types
Functions
- list(module, topic)
-
Returns presences for a topic
Callbacks
- fetch(topic, presences)
- handle_diff(%{}, state)
- init(arg0)
- list(topic)
- start_link(arg0)
- track(arg0, key, meta)
- track(pid, topic, key, meta)
- untrack(arg0, key)
- untrack(pid, topic, key)
- update(arg0, key, meta)
- update(pid, topic, key, meta)
Types
Functions
list(module, topic)
Returns presences for a topic.
Presence datastructure
The presence information is returned as map with presences grouped by key, cast as a string, and accumulated metadata, with the following form:
%{key => %{metas: [%{phx_ref: ..., ...}, ...]}}
For example, imagine a user with id 123
online from two different devices, as well as a user with id 456
online from just one device. The following presence information might be returned:
%{"123" => %{metas: [%{status: "away", phx_ref: ...},
%{status: "online", phx_ref: ...}]},
"456" => %{metas: [%{status: "online", phx_ref: ...}]}}
The keys of the map will usually point to a resource ID. The value will contain a map with a :metas
key containing a list of metadata for each resource. Additionally, every metadata entry will contain a :phx_ref
key which can be used to uniquely identify metadata for a given key. In the event that the metadata was previously updated, a :phx_ref_prev
key will be present containing the previous :phx_ref
value.
Callbacks
fetch(topic, presences)
Specs
fetch(topic, presences) :: presences
handle_diff(%{}, state)
Specs
handle_diff(%{optional(topic) => {joins :: presences, leaves :: presences}}, state :: term) :: {:ok, state :: term}
init(arg0)
Specs
init(Keyword.t) ::
{:ok, pid} |
{:error, reason :: term}
list(topic)
Specs
list(topic) :: presences
start_link(arg0)
Specs
start_link(Keyword.t) :: {:ok, pid} | {:error, reason :: term} :: :ignore
track(arg0, key, meta)
Specs
track(Phoenix.Socket.t, key :: String.t, meta :: map) ::
{:ok, binary} |
{:error, reason :: term}
track(pid, topic, key, meta)
Specs
track(pid, topic, key :: String.t, meta :: map) ::
{:ok, binary} |
{:error, reason :: term}
untrack(arg0, key)
Specs
untrack(Phoenix.Socket.t, key :: String.t) :: :ok
untrack(pid, topic, key)
Specs
untrack(pid, topic, key :: String.t) :: :ok
update(arg0, key, meta)
Specs
update(Phoenix.Socket.t, key :: String.t, meta :: map) ::
{:ok, binary} |
{:error, reason :: term}
update(pid, topic, key, meta)
Specs
update(pid, topic, key :: String.t, meta :: map) ::
{:ok, binary} |
{:error, reason :: term}
© 2014 Chris McCord
Licensed under the MIT License.
https://hexdocs.pm/phoenix/Phoenix.Presence.html