Synced Realm Collection Notifications not being triggered

Hi, just trying to understand whether there are some changes in RealmSwift 10.19.x that mean things behave differently that with the old Realm Cloud.

Currently we are not received collection change notifications when another user adds or modifies a record to a synced realm object.

We are registering for collection notifications using the results.observe(…) api but this does not appear to be getting called for synced changes made by other remote clients.

However when invalidating the notification token and recreating it on the same collection the new data is reflected so it seems the data is being synced but the notification is not being sent when changes are received from the Realm App (Cloud).

However it seems on some other collections this is working correctly. Are the some scenarios where these change notifications will not be received for incoming synced changes ?

I am hoping just a coding issue but it seems strange that a local change gets the change notification but not if the collection changes as a result of a synced change.

OK just a further update on this issue which is raised on realm-cocoa.

Kind of a long story but in order to avoid blocking the UI when first opening a realm by calling Realm() we always do this the first time from a background thread in the application.

The reason the call to Realm() can block the UI thread sometimes is because if realm-core needs to perform a file format upgrade/migration then this can take some time if the file is large. In this case the call to Realm() will not return until the upgrade/migration has completed so calling this the first time on the main thread could result in the UI blocking and the app being terminated.

Currently there appears to be no way to detect this before the event (is this correct?) and take the necessary steps to let the user know.

So to avoid potential problems we always make the first call to Realm() from a background thread while showing a “Loading…” screen and once this call returns we then initiate the rest of the application and open the realm from the main thread.

However this appears to result in a problem with more recent versions of RealmSwift because for some reason it is creating problems with sending subsequent change notifications for synced realms.

Things seem to work just fine for local realms - or changes made in the background on local realms still result in change notifications being posted to the observers. While the same changes made by other clients for a synced realm are being received and the database is being updated but no change notifications are being sent to the observers.

For synced realms this issue also seems to be intermittent and sometimes on startup things work and other times not. Mostly not.

If we no longer open the realm the first time from a background thread but do so from the main thread then this issue seems to go away.

I suspect this is a bug since:

a) Sending of change notifications should not behave differently for synced or non-synced realms (?? is this correct??).

b) Sending of change notifications to observers should not be affected by whether the Realm() is first called from a background thread or from the main thread.

Another apparently related issue is that when first opening the synced realm from a background thread the intermittent success of getting change notifications sent seems to coincide with complete failure to call the sync progress callback block. Conversely when change notifications fail to be sent the callback block is always getting called.

For now we are no longer calling Realm() the first time from a background thread since this seems to result in some issues for synced realms.

Is there any reason not to open the realm with asyncOpen(…), that wouldn’t need to operate on a different thread?

Sample in our repository

Well I guess the question is why would you ever use asyncOpen() unless it is required to initialise data the very first time the application is used, in particular given that it will fail if there is no network connection.

Our app will only ever use asyncOpen() the first time the user runs it in order to ensure the initial data is downloaded from the server. If there is no network connect the callback never gets called if I remember correctly.

You should speak to your colleagues about this as well since they have indicated in various posts that it is only necessary to use asyncOpen() if you want to ensure that data is downloaded from the server before the user uses the application since asyncOpen() will only call the callback once the download has completed. But as I mentioned this call fails to return if there is no network connection - which breaks the Offline First model somewhat.

This is only partially correct: when the app receives a Client Reset, it should proceed to an asyncOpen() as well, as the local DB is now invalid.

This is indeed the usual and simple recommendation, and the one the sample I pointed in my previous comment follows (with the provision explained above). Under the hood, however, things are more nuanced, and the why this suggestion is given is worth considering.

When using asyncOpen(), the SDK knows that it’s the only one to have access to the local realm, hence it can apply all possible shortcuts to speed up the application of the transactions: this is obviously mostly needed when the DB is new (or reset), and helps minimising the time for the initial download. Once the realm is opened and made available to the app, all transactions will need to go through conflict resolution, but this usually isn’t a problem, as changes will typically come at a reasonable pace.

When opening the ream synchronously, however, the situation is different: all the transactions need to go through conflict resolution from the start, and this has a performance hit. All fine and good if the client comes online frequently, and the amount of changes to download is small, however, if the client has been offline for a while, and has accumulated a notable amount of changes, opening a realm synchronously will take significant more time than opening it async: with a lot of changes, it’s not unusual for the process to take minutes!

This leads then to my previous comment: calling the synchronous opening of the realm in another thread is the worst of both worlds, you don’t have the realm ready until is finished, and you get the performance hit in return.

This shouldn’t be much of a problem, and has multiple solution paths: for example, you can keep the AsyncOpenTask that asyncOpen() returns at hand, and implement a timeout that calls the cancel() on it, falling back to a synchronous opening (that will likely complete immediately, if there’s no network)

1 Like

Thanks - good explanation. I have send a sample application to illustrate the errant behaviour to Jason Flax via email as the issue is logged on GitHub…