New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sync channel messaging with new push API #737
Conversation
@@ -137,14 +137,18 @@ defmodule Phoenix.Channel do | |||
:ignore | | |||
{:error, reason :: term, Socket.t} | |||
|
|||
defcallback leave(msg :: map, Socket.t) :: {:ok, Socket.t} | |||
defcallback leave(msg :: map, Socket.t) :: :ok | {:error, reason :: term} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What was our conclusion regarding leave
? I think we were discussing to rename it to terminate, weren't we? I don't remember the rationale anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I remember. Leave is not always called... only if we trap exits. So I would definitely go with terminate as we have the same definition in there too.
Beautiful. I love the join example! ❤️ 💚 💙 💛 💜 |
this.bindings = [] | ||
this.afterHooks = [] | ||
this.recHooks = {} | ||
this.joinPush = new Push(this, "phx_join", this.message) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we move this into a constant? All of these strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+2
Dig the usage of ES6 here. You might consider adding semicolons to make this more reliable for minifiers or for people who concatenate all of their vendor javascript code in to a single file (points at self). Not really strongly opinionated on this point though. |
Thanks! Babel inserts semicolons in the compiled priv/static/phoenix.js, so not an issue for minifiers. |
Perfect! Then no worries! |
This is outstanding work. The examples are very clear 👍
|
I considered this because before I had
I will bump the docs to better explain why. Your guess is correct. If the pubsub server is down, you likely want to crash, but there are cases where you may only want to optimistically broadcast but messages being dropped aren't mission critical. |
Knowing this, I think it makes sense to return |
I can see it going both ways, but I agree with @paulcsmith here since there is the explicit reply and noreply I wouldn't expect I think there might be value in making two methods, |
👍 to being explicit. The implicit return would make your code suddenly How would push_and_reply help though? Wouldn't you need to pass the Should we also change broadcast and push to just return :ok? José Valimwww.plataformatec.com.br |
That's a good point, unless you pass both it wouldn't make much sense. In that case I'd lean towards being explicit each time. |
I think this argument seals it. Explicit > implicit here.
👍 |
We do have the same failure with the tuple but it is much more unlikely to Generally speaking, moving code around will cause breakage, the goal is José Valimwww.plataformatec.com.br |
Oops, sorry I deleted my comment. I saw that Chris already made a decision so I deleted it. You have a great point. Less breakage is good :) |
That's ok! I am stuck on mobile, so I get to see everything :P José Valimwww.plataformatec.com.br |
@@ -157,7 +158,7 @@ defmodule Phoenix.ChannelTest do | |||
end | |||
|
|||
test "#broadcast_from and #broadcast_from! raises error when msg isn't a Map" do | |||
socket = Socket.put_topic(new_socket, "top:subtop") | |||
socket = new_socket |> put_in([:joined], true) |> Socket.put_topic("top:subtop") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new_socket |> put_in([:joined], true)
is used fairly often. Maybe extract a function called new_joined_socket
, or joined_socket
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. This whole test file needs cleaned up :)
// Return the next message ref, accounting for overflows | ||
makeRef(){ | ||
let newRef = this.ref + 1 | ||
if(newRef === this.ref){ this.ref = 0 } else { this.ref = newRef } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expand this for clarity?
Beautiful work @chrismccord! Can't wait to use it. |
Teardown/cleanup should be handled by trapping exits and handling in terminate/2
We've overhauled the channel API to allow "synchronous" messaging, and I really love the results. By synchronous, I mean being able to reply to an incoming event directly, while ensuring messaging ordering for the same incoming events. This not only lets you do proper request/response messaging where necessary, but it also fixes issues we have in our <= 0.10 apis where joins were not synchronous and messages could be dropped if you fired them before you were fully joined. With these changes, we have a few high-level concepts which make up channels:
The flows looks like this:
push("ev1")
-> serverhandle_in("ev1")
->server push("ev2")
-> clienton("ev2")
push("ev1")
-> serverhandle_in("ev1")
->server broadcast("ev2")
-> N subscribershandle_out("ev2")
-> N subscriberspush("ev2") -> N clients
on("ev2")`push("ev1")
-> serverhandle_in("ev")
-> server{:reply, :ok, ...}
-> clientreceive("ok", ...)
Now let's see some cli/server code:
Note that
{:reply, {:ok, resp}, socket}
on the server, triggers.receive("ok", resp => {})
on the client. The "status" of the reply can be anything, ie{:reply, {:queued, resp}, socket}
on the server, triggers.receive("queued", resp => { })
on the client.Also note that client joining, push, and receiving replies all have the same semantics and API now, which is quite nice.
All feedback appreciated before I merged this upstream. Thanks!