What is GenServer mailbox queue order?

Hey Elixir Community,

I have a question about the mailbox order between GenServers/GenStages

let’s say a GenServer A send {:add, 2} to GenServer B
and then send {:multiply, 4} to GenServer B,
now what order GenServer B will receive the msgs ? (:add then :multiply) or the order is limited to each call/cast scope?

Thanks

The order of the messages in the mailbox is always kept in the order they arrived. So, when process A sends message M1 to process B and then send message M2 to process B, message M1 will be ordered first and M2 second.

For a GenServer the order the messages are processed should also be in the correct order, but if you write your own receive clause you can use selective receive to process the messages in a different order.

3 Likes

perfect, so the order between two genserver/genstage/hybrid(genstage to genserver) is always in order per Gen and not per call/cast scope…

what do you mean by receive clause? I am handling the msgs in handle_info and you said the order is always going to be similar to how they got sent from the sender, so even receive clause going to recv them in same order? or there is a way to manipulate the order?
my hope to recv all the casts between two processors in same order

thanks

If you use the send/receive primitives of the language the receiving process can choose in which order (to some degree) to process the messages in the mail box.

I explained it more here: Message Prioritization inside GenServer - #2 by cmkarlsson

However the GenServer OTP behaviour does not allow selective receive which means the message will be processed in the same order as they are received.

2 Likes

Caveat: the receiving ordering is true only within the same node.

Although GenServer don’t allow for selective receive we can mimic this by using states aka GenStateMachine

1 Like

Thank you this is very helpful, I will stick with the OTP behaviour for now, and will use selective receive when needed in future.

Thank you tty, I never heard of GenStateMachine.

do you think if there is a way to make one GenServer more powerful than other ?
there is Flow hexpackage which deal with enumerable, but how to make flow work with handle_info()?

I’m not sure what you mean by more powerful. In the Erlang world gen_fsm, gen_statem are all implemented with gen_server as a base.

Are you sure? I think message ordering between two processes are intact even between nodes. There is no guarantee that a message will arrive though so there may be gaps in messaging?

I have a GenServer who recv a lot of messages(streams) from active socket, and am hoping to make the GenServer more powerful like to get the priority as 2x or more in the BEAM pipeline, I dont think this is possible.

it make sense, I think it’s only limited between two processors and not the whole queue order(many pids)

Yes I agree my statement might be due to semantics.
See Programming distributed Erlang applications pitfalls and recipes for a discussion on the issue abid from 2007.

Ummmm although possible it isn’t advisable. You can set priorities for BEAM processes but you are also highly advised not to.

Typically we would recv from the active socket and spawn a Task to handle the request, just like a traditional C/C++/Java socket server. BEAM processes are cheap and lightweight that we wouldn’t even use a process pooler in this situation. Just spawn.

Great, the buffer is sequential, so spawning will not be helpful here, I am already sending it to other processor but the genserver doing a little of processing (dispatch the frame header\stream id etc)
do you think spawing processor to send the buffer to other processor is good idea ? or just send it directly from the controlling processor as the cost is going to be similar ?

I would go with the simpler design (ie direct from the controlling processor) and only change it if you can demonstrate it is a bottleneck via profiling.

1 Like