This week I faced in my current Xamarin project an odd timing issue related with observable collections and the .NET reactive extension.
In this post, I wanted to document the debugging process and what I learned along the way about types of observables and subjects which essentially revolves around using the proper
I have a Xamarin mobile application that needs to join a channel in a Phoenix based backend service.
When the mobile application joins the channel it receives immediately notifications about already connected users then it is supposed to render them on a map view.
The issue was that these notification where ignored and existing users where not getting rendered.
After some debugging on both backend and frontend sides, I figured out that the issue was caused by the fact that on-join notifications where delivered fast and before letting the UI layer a chance to subscribe its event listeners(here the event is the reception of the notification).
Consider the following diagram:
The diagram depicts the classes involved in the issue, here we have three classes involved:
BackendGateway class with its
Connect method establishes a web-socket connection with the backend, joins the specific topic and registers message reception handlers as you can see this in the following simplified snippet:
This method returns and
IObservable<Message> and as you can see the class uses the
Subject class has a
IObservable implementation, the message reception handler converts the received payload to an internal representation and then pushes it to the
_observable subject via
ActiveStore class, when started via its
Start method, uses the
BackendGateway class to connect to the backend, it gets the
IObservable<Message> after calling
BackendGateway.Connect to which it subscribes an incoming message event handler:
You can see that this handler performs some processing before pushing the messages to another down stream
Subject which is returned by
ActiveStore.Start to its callers.
MainActivity class creates an
ActiveStore instance and gets an
IObservable<Message> by starting it via the
Start method whereupon it registers a listener that will handle drawing actual users on the UI:
Here it turned out that in the time window spanning between the
BackendGateway instance effectively joining the channel and
MainActivity subscribing its
Message reception handler is large enough to cuase the
MainActivity to miss the initial notifications.
This situation has been caused by the fact that all the
IObservables in this chain where inadvertently implemented has Hot Observables.
Hot vs cold observable
In the Reactive extension, there two kinds of observable collections:
- cold observables that starts publishing values only when an Observer is subscribed
- hot observables that starts publishing values usually when created even without any Observer subscription
In our previous example, the
BackendGateway starts pushing events in the
Subject that it return as soon as the connection is established with the backend, late subscribers will simply miss early notifications which in our case is unacceptable.
We can clearly see that the
IObservable<Message>s returned respectively by
ActiveStore.Start have been implemented (inadvertently) as hot observables while they should have been implemented as cold observables.
Implementing all of these observables seems a bit challenging for me, but fortunately there is a simpler alternative.
Fortunately, thanks to an existing subject implementation we don’t have to make these observable cold as we are going to see in the next section.
replay subjects, know your subject types
The reactive extension defines multiple subject types, among them the
ReplaySubject keeps track of received messages and replays them in the order they arrived in to every new subscriber, simply using the
ReplaySubject instead of the regular
Subject allowed us to correct the issue in the previously described code.
Late subscribers can now get all the notifications delivered since the establishment of the connection with the backend.
Before being exposed to the
ReplaySubject thanks to the excellent Rx tutorial, I implemented a less than ideal work around on the backend side that essentially sends notifications after a two seconds delay in order to give the mobile app enough time to register its listeners. It’s time to get rid of it