Understanding Akka Streams - play-socket.io

Hi All and Happy new Year.

Over the last couple weeks I have been learning about akka and akka streams and am very excited about their potential for various projects that I work on, specifically the non-blocking and stream oriented features of akka and play. One use case that i’ve been looking at is using akka streams for handling web socket based applications. In particular i’ve been looking at the play-socket.io library and its examples.

I’ve been looking at this example on the play-socket.io github repo:

And I cannot figure out how the streams/flows are getting wired up, I’ve studied the akka streams doc in details and understand how Hubs etc work, but cannot follow what is happening in that example.

This is what I can understand:
In Line 94 a flow is created such that each message that enters the flow will be handled in a different way, for instance Chat messages pass right through to “via” part which is the Merge/Broadcast channel for the play-socket.io namespace. Join messages, will spin off the creation of a new flow that specifically deals with Join and Leaves… and is materialized in Line 100.

This is where I get lost, how is this broadcastSource[0] related to anything else, it just seems to get materialized in isolation in the map function.

And even more confusing, what on earth is happening here. Why is the source of the ultimate Merge/Broadcast channel being set to the previously mentioned broadcastSource[0] in the materializer combiner function. Same for mergeSink[0], why is it being set to sink, While the combiner function isn’t returning anything.

I have a lot of experience as a Java developer and have studied that example and akka streams in detail, but this example is making me feel really dumb, I know this is probably a question for the play-socket.io team, but can anyone shed any light light on how this example is working.


If it is any consolation it was not immediately obvious to me either. ;) It’s a bit of quite clever/advanced usage of streams going on there.

BroadcastHub and MergeSinks are for dynamically adding sources and sinks to a flow, in general a flow stays the same after you have materialized (run) it. If you materialize several times it will become separate streams but for a chat you want to create new sinks leading into the chat and new sources coming out of it ad hoc/when a client connects, so that is why those are needed here.

The two single element arrays are there since you cannot close over mutable fields in Java and are there since the in and out to the ad-hoc part of the flow is not there until the user flow has actually been materialized.

The flow returned from createFlow accepts commands which can be to join a room, which will then materialize a new stream section between the user flow and the selected room (that is the dynamic part on L100-113). Other commands, such as ChatMessage and LeaveRoom will go into the BroadcastHub and be routed to all current wired in chat rooms, but is filtered out for other rooms than the actual destination by the fiter on L104.

Note that this is just one way of modelling such a stateful set of streams, you could also with a combination of less advanced streams and actors.

Thanks so much for your detailed reply, its this bit that makes it more clear for me now. It seems like the purpose of L123/L124 is to get a reference to the source/sink of the flow so it can be appended with the roomFlow in L109 and then materialized.

Now I just need to learn a bit of Scala so I can understand the source library, the rabbit hole continues :joy: