GenStage serving as event manager

Question 1)

The :reply and :noreply tuples are always related to whoever sent the message. So in the handle_call case, it is whoever called sync_notify. For the demand case, it would be whatever consumer sent the demand. Note though you can’t reply on handle_demand (as you can’t reply on handle_info).

What we are doing in that code sample in handle_call is that, instead of replying immediately, we are storing the client reference and not replying via the tuple. If there is demand, we then reply, otherwise we will wait until the consumer asks for events before replying. In both cases, the client will be waiting until GenStage.reply is called. When we send the event to the consumer, then we effectively reply to the client.

Maybe a simpler example is to use handle_call with GenStage.reply outside of that context. For example, this “classic” handle_call:

def handle_call(:ping, _from, state) do
  {:reply, :pong, state}
end

is equivalent to:

def handle_call(:ping, from, state) do
  GenStage.reply(from, :pong)
  {:noreply, state}
end

The reply technique is used when you can’t reply immediately (it needs to be done through another callback) or even if you want to reply as soon as you get the message because you need to do long running work afterwards.

Question 2)

Currently this is not supported. We may end-up supporting something similar, but it would have to be specific per dispatcher (i.e. some dispatchers would be able to support it but others do not). One possible option is to have multiple producers per key and then you subscribe to the producers that have the keys you care about. However, unless you have too many messages, I wouldn’t worry about it.

3 Likes