Greetings,
I am using the documentation to set up a simple working example for phoenix.presence, and I am using the fetch override to call the database and get user details as described here: Phoenix.Presence — Phoenix v1.6.2
Here are my scripts so far:
/assets/js/socket.js (client side)
import {Socket, Presence} from "phoenix"
let socket = new Socket("/socket", {params: {token: window.token, foo: window.bar}})
socket.connect()
let channel = socket.channel("room:lobby", {})
let presence = new Presence(channel)
function renderOnlineUsers(presence) {
let response = ""
presence.list((id, stuff) => {
console.log(id+' '+JSON.stringify(stuff))
response += '<br>${id}</br>'
})
document.querySelector("#UserList").innerHTML = response
}
presence.onSync(() => renderOnlineUsers(presence))
export default socket
/lib/exchat_web/channels/presence.ex
defmodule ExchatWeb.Presence do
alias ExchatWeb.Accounts
use Phoenix.Presence, otp_app: :exchat,
pubsub_server: Exchat.PubSub
def fetch(_topic, presences) do
IO.inspect(presences)
users = presences |> Map.keys() |> Accounts.get_users_map()
for {key, %{metas: metas}} <- presences, into: %{} do
{key, %{metas: metas, user: users[key]}}
end
end
end
This is pretty much the basic examples found in the documentation. Here is an example of what happens when someone joins:
Let’s say these user ids are already in the chat room:
[299, 1999, 18100, 18, 1]
When a new user id “2000” joins, I’m seeing this in the Phoenix logs:
[info] CONNECTED TO ExchatWeb.UserSocket in 1ms
"join room"
"handle after join"
"run fetch - call database"
"run fetch - call database"
[debug] QUERY OK source="members" db=0.9ms queue=0.8ms idle=302.2ms
SELECT m0.`id`, m0.`username`, m0.`gender`, m0.`status`, i1.`path` FROM `members` AS m0 LEFT OUTER JOIN `images` AS i1 ON i1.`id` = m0.`mainimageid` WHERE (m0.`id` IN (?,?,?,?,?,?)) [299, 2000, 1999, 18100, 18, 1]
"run fetch - call database"
[debug] QUERY OK source="members" db=1.1ms queue=3.1ms idle=0.0ms
SELECT m0.`id`, m0.`username`, m0.`gender`, m0.`status`, i1.`path` FROM `members` AS m0 LEFT OUTER JOIN `images` AS i1 ON i1.`id` = m0.`mainimageid` WHERE (m0.`id` IN (?)) [2000]
"run fetch - call database"
"empty query: ids to find = []"
[debug] QUERY OK source="members" db=1.1ms queue=3.1ms idle=0.0ms
SELECT m0.`id`, m0.`username`, m0.`gender`, m0.`status`, i1.`path` FROM `members` AS m0 LEFT OUTER JOIN `images` AS i1 ON i1.`id` = m0.`mainimageid` WHERE (false)
[debug] QUERY OK source="members" db=0.6ms queue=45.3ms idle=0.0ms
SELECT m0.`id`, m0.`username`, m0.`gender`, m0.`status`, i1.`path` FROM `members` AS m0 LEFT OUTER JOIN `images` AS i1 ON i1.`id` = m0.`mainimageid` WHERE (m0.`id` IN (?,?,?,?,?,?)) [299, 2000, 1999, 18100, 18, 1]
In addition, if that same user leaves the chat room, two uneccessary calls are done:
"run fetch - call database"
"empty query"
[debug] QUERY OK source="members" db=1.1ms queue=3.1ms idle=0.0ms
SELECT m0.`id`, m0.`username`, m0.`gender`, m0.`status`, i1.`path` FROM `members` AS m0 LEFT OUTER JOIN `images` AS i1 ON i1.`id` = m0.`mainimageid` WHERE (false)
"run fetch - call database"
[debug] QUERY OK source="members" db=0.5ms queue=0.7ms idle=1553.2ms
SELECT m0.`id`, m0.`username`, m0.`gender`, m0.`status`, i1.`path` FROM `members` AS m0 LEFT OUTER JOIN `images` AS i1 ON i1.`id` = m0.`mainimageid` WHERE (m0.`id` IN (?)) [2000]
When someone joins, such as user id “2000”, shouldn’t there just be two calls - one call to grab the details just that one user to display for the existing users, and one call to grab all the user details to display to the new user? For example:
SELECT m0.`id`, m0.`username`, m0.`gender`, m0.`status`, i1.`path` FROM `members` AS m0 LEFT OUTER JOIN `images` AS i1 ON i1.`id` = m0.`mainimageid` WHERE (m0.`id` IN (?)) [2000] # display this to existing users
SELECT m0.`id`, m0.`username`, m0.`gender`, m0.`status`, i1.`path` FROM `members` AS m0 LEFT OUTER JOIN `images` AS i1 ON i1.`id` = m0.`mainimageid` WHERE (m0.`id` IN (?)) [299, 2000, 1999, 18100, 18, 1] # display this to the new user - or simply only just use this query for both the existing and new user, send it to everyone?
and when a user leaves, shouldn’t it just remove that user from the presence instead of trying to run another query to fetch the details for the user that is leaving?
I’m using the simplest example from the documentation, so I’m not really sure how to fix/optimize this for minimal database calling. I’d greatly appreciate some advice.
Thanks