Does Phoenix PubSub respect the order of messages received

Hi, I have a question regarding the Phoenix PubSub process as it’s used by Channel sockets. Are the sockets that receive broadcast! messages via the PubSub process guaranteed to receive and handle messages sent to the PubSub process (via broadcast!) in the order that the PubSub process received them?

I’m assuming that since there’s only one PubSub process then there’s no opportunity for, say, 2 messages each sent via broadcast! in 2 seperate socket processes, one at time = T1 and the other at T2, to be received by sockets subscribed to the PubSub out of order (say T2 and then T1).

Thanks.

There is no global pubsub process. The VM guarantees message order for any messages sent between two process. So, if process A sends process B, :foo and then :bar, process B is guaranteed to receive them in that order. However if A sends :foo to B and then C sends :bar to B, it’s possible it could see them in any order, depending on how close together those sends happened.

When you broadcast!("some topic", value) you are basically acting as Process A, and you are sending to all processes that are subscribed to that topic. So, if you broadcast :foo and then broadcast :bar, that will arrive in the correct order to everyone.

3 Likes

Thanks @benwilson512. I saw a PubSub.local0 genserver process in the erlang observer process tree and thought maybe it was “sending” messages to the socket processes I saw it was monitoring. But that’s not very elixir-ey. If anything, that PubSub process (or some other entity) would probably keep a registry of socket genserver pids within which broadcast! calls would ultimately call functions.

All that aside, I think I get it. Thanks a lot.

1 Like

Ah yeah so those are mostly for managing the :ets tables that hold onto the pubsub registration information. There are also some processes used for forwarding pubsub messages between nodes in a distributed erlang scenario. There are definitely processes used in Phoenix PubSub internals, I guess I mostly wanted to point out that there isn’t some singular process, or even a singular process per topic that serializes messages in a global way.

1 Like

Gotcha - yeah I was initially asking the question as if there were some PubSub queue process or something like that, and what I’ve gotten from this is that there definitely is not. Thanks.

1 Like

I’ve had a bit of time to collect my thoughts and I’ve got another related question.

  • Say you have socket procs A, B, C, D

  • These procs can both send and receive messages

  • PubSub happens by a socket proc sending another proc a broadcast message, there is no global PubSub “queue”

  • if A sends message :foo to the rest, and D sends :bar immediately afterwards, A will iterate through the list of subscriber procs [A, B, C, D] in order and send its message, and D will do the same.

  • since the list of subscribers is I’m sure always in the same order, I can’t imagine that C, for example, would ever receive D’s message before A’s.

Or would it? The only way that would be possible is if the BEAM scheduler interleaved the execution of A and D’s message sending unevenly, or if A and D were operating on different cores and actual parallelism was happening.

Ultimately what I’m worried about is I want to be sure that the subscribers to topics (and ultimately the users consuming this all in the web browser) all get their events in the same order. I don’t care if A clobbers D, I just want to ensure they all have a consistent state. Imagine 4 users corresponding to the 4 procs mentioned above. If 2 users (proc A’s and proc D’s) each entered a different value in a text box in a browser that was sent to all 4 users via websockets, I don’t care which value they all receive last - i.e. which one “wins” - I just want them all to have the same value. Does Phoenix Channels guarantee that?

it 100% can happen in not the order you expect, particularly if A and D are on different servers.

No, this is a distributed state issue. You either need to bring your own coordinating entity, or use something like a CRDT. This latter thing is actually what Phoenix Presence does, although it is by no means simple.

3 Likes

Thanks @benwilson512. Is the problem exacerbated when A and D are on different servers, or only possible when they’re on different servers?

Exacerbated, but it’s 100% possible even with one server for a variety of reasons.

For one thing, this ^ is a flawed assumption. Subscribers are stored in Registry, which uses a sharded set of :ets tables in :duplicate_bag mode. There is no real order guarantee here at all between any two invocations, plus the list itself can change between those invocations in a way that may effect the order.

Even if the order was a simple list somewhere, the BEAM simply doesn’t provide the guarantee you’re looking for. If the time between A and D messaging C is sufficiently small, then small differences like which scheduler each process is currently on can cause them to arrive out of order. Message order is only guaranteed between one process and another.

3 Likes