A pattern for overcoming non-determinism of Golang select statementPedram HajesmaeeliBlockedUnblockFollowFollowingMay 3In this article I will introduce a pattern that simulates behavior of select statement in go but without the non-determinism.
There are cases when we have a goroutine which takes care of multiple similar tasks.
For example in case of websocket we might have a hub goroutine that exposes a connect channel and a disconnect channel, both of type chan *Connection.
Upon receiving a message from connect channel it keeps a reference to the connection somewhere and upon receiving from disconnect channel, it removes reference to the connection.
In this case even though we are exposing two channels and handling two different types of events, we need to process events in order of their occurrence.
It means that the event which has occurred sooner, should be taken care of sooner as well.
Otherwise we may run into cases when for a specific connection, we handle disconnect event sooner than its corresponding connect event.
First thing that comes to mind for handling such cases is using a select statement inside a for loop.
However there is an issue with that approach: Select statement provides no guarantee of order of execution.
What it means is when multiple channels have some message in them, select statement does not necessarily pick the message that has arrived earlier, first:> If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection.
It is ok for many usecases, however there are times when we need to make sure the message that has come earliest, will be read first.
In those cases we can not use select statements anymore.
Recently I have found a workaround for this overcoming this problem.
Here is the code before applying the pattern.
Note that on each iteration of for loop if connectChan and disconnectChan both have messages, select statement will just read one of them randomly.
When deciding which channel to read from it does not care message of which channel has arrived earlier.
Therefore we might run into the problem described earlier.
Before applying the patternHowever there is a neat way to have that guarantee of order of execution with a small change.
Instead of listening to two different channels, we can expose one command channel, along with a Command interface and get rid of the select statement.
After applying the pattern, our code will look like:After applying the patternNow all events will go through a single channel, therefore they will be processed in the same order as they have occurred.
Beauty of this pattern in my opinion is that there is no switch-case anywhere in it.
Therefore it is quite easy to add new types of commands.
Conforming with open-closed principle.