Realm Sync vs. Firestore experience comparison and notes

I recently bit the bullet on SparkEatz and would like to share my experiences with the community in case it helps anyone dealing with one or both systems (Realm Sync + Firestore). I am a lot more familiar with Firestore, having used it on many projects, and so this may inform a lot of the opinions below.

  • First off, Realm Sync does an amazing job when it works — and it solves the key problem I was trying to address when porting from Firestore to Realm Sync in the first place: full offline capability as well as fast local-first data access. SparkEatz would not be where it is today without Realm Sync — thanks to the whole Realm team for a job very well done!

  • Overall, the biggest qualitative difference by far, at least from my perspective having launched only one production app on Realm, is that Realm Sync is far more fragile than Firestore. Perhaps this is just a reflection of my relative newness to the platform. But when I develop on Firestore, nothing ever seems to go wrong — sure, I can get schemas mixed up, occasionally try to read a Firestore Timestamp before it gets back down from the server, or mess up a trigger. But Firestore never takes down my app. Realm, however, frequently crashes the entire app if I do the wrong thing or if the wind blows the wrong way. Client resets, error logs on inability to connect to the network, etc etc. The times when I’ve had to Terminate Sync and Restart are always fraught with anxiety that it might all not reboot and that I might need to manually delete all client files in my code to recover. Note: I’m well aware that perhaps Realm Sync is rock solid and that I’m just ranting like a naive n00b — but I’m conveying my qualitative experience of developing on Realm Sync vs. Firestore, which is that for me, personally, I’m always afraid that Realm might crash the app if I cough wrong, whereas this has never been a fear of mine with Firestore (via RNFirebase).

  • The biggest functionality difference I’ve had to wrestle with is the semantics of permissioning: who can access which objects. Realm’s model is per-realm (i.e. all objects in a Realm have the same read/write permissions). Firestore’s model is per document path, which some pretty interesting deeper semantics (e.g. “you may only read this doc if you’re listed in the ‘subscribers’ field of the doc”). Realm’s monolithic permission model makes building multi-user apps quite tricky. For instance, consider an app where you and a friend can share some objects in a group – e.g. photos of cats. Now you want to add a new friend to the group as well. To do this in Firestore, you just have to author the initial permissions correctly: whoever’s in the group can read the object. To do this in Realm, you need to create a new Realm with all three people, copy all the objects from the original two-person Realm into the three-person, and delete the old Realm. Besides the trivial cat photos example, these sorts of permission semantics come up all the time in typical projects. So it ends up, at least for me, easiest to only use Realm as a per-user local store of their own objects. I keep all shared cross-user objects in Firestore. For instance, I want all users to read each other’s profiles, but only the user themselves may change their own profile. My understanding is that this isn’t possible with Realm permission semantics; this setup is trivial in Firestore.

  • The UI for managing Realm Sync on MongoDB feels a bit janky relative to the fully-integrated UI of Firebase/Firestore. There’s stuff you can see in parts of the UI that you shouldn’t modify because you should use the Sync tab instead (IIUC); there’s hopping between the Realm tab and the Atlas tab in order to check out some data; there are several changes you can make in the Schema or Permissions areas which, IIRC, lead to a bunch of website error popups that then require Sync Termination to recover from (and even an occasional realm-cli push). This might all be an artifact of Realm getting integrated into MongoDB; it feels a bit like “shipping the org chart.”

  • The documentation on Realm is worse than Firestore, making the learning curve steep (IMO). Understanding Realm is a lot more about searching the web and this forum for Q&A type solutions than it is about reading the docs and just getting set up and started.

  • Lemma: key topics required for any real production deployment should be covered in more depth, with more examples and explanation. Three that come to mind are: a) how really to handle CLIENT_RESET fully (including data porting, local file deletion, launching the new client file, etc), b) the subtle differences between opening a Realm synchronously and asynchronously, including the gotchas, and c) best practices around schema changes. More on that below.

  • When it comes to schema changes, I find the Realm Sync documents fall just a bit short of what you need to know as someone running a production service. For instance, the list of destructive changes finally made it from a forum post here into the docs – but not in all the places, just in one place IIRC that you happen to need to see. But furthermore, I think a clear explanation of how to deploy a schema change end-to-end in production should be covered somewhere. Some of the docs say to go back into Development Mode, but I can’t imagine that’s what’s really advised in the production DB (or please correct me if indeed it is the right thing!). So I end up hand-modifying the schema in the Web UI first, then updating the schema in my React Native Realm object, and finally deploying/launching the new code. I’m still not even sure if I’m doing the right thing here.

  • On the learning curve bit: there are some real oddities in Realm’s JS/TS implementation. It took me quite a bit of debugging and web sleuthing before figuring out that the JS objects that Realm returns cannot be spread. That is, no {...soup} for you. The Realm objects need you to access them by property name explicitly to fully instantiate / par-boil them. So to preserve reasonable semantics in the rest of my codebase, I implement a derealm function on all my Realm objects where I explicitly reference each property before returning it to the client code.

  • Similarly, one needs to be careful mixing Realm with React because of differences in expectation around data-consistency / data-currency. One common example is deleting an object from Realm – you better make sure none of your React objects are holding on to JS related to that object because you’ll get an error when they render, even if it’s the dying-gasp last render prior to React also figuring out the object is now deleted. Unlike most React work, you need to deal with Realm objects the way you deal with useRef – basically realize that the underlying object can change without your React code necessarily detecting it prior to a re-render. To make these semantics easier, I also derealm all objects (copy all Realm properties into a separate non-Realm JS object) before handing them to a React component so that React semantics can be maintained more easily.

Overall, adopting Realm Sync has been a great boon to my application. It lets me accomplish things that I never could have without it. And when it’s working, it really is pretty fabulous. But I find that I can’t move off Firestore because it offers multi-user permissioning that’s much more suitable for many common app scenarios. And I find that my general experience developing on Realm is that it’s hard to know what’s going on, the documentation is sparse, and there’s always a chance it’ll crash the app if I do something wrong — I’m basically always breathing lightly near it, tiptoeing by in order not to Wake the Giant™.

I hope this feedback is helpful to perhaps the team or to other people considering Realm Sync vs. Firestore. SparkEatz couldn’t be where it is today without Realm Sync — it’s an invaluable platform that has some amazing functionality for which I’m deeply grateful. My hope is that as the Realm community grows, the types of issues I mention above can be addressed over time.

3 Likes

:wave: Welcome to the MongoDB Community Forums @Philip_Su!

Thank you so much for taking the time to provide such detailed and constructive feedback – this is definitely appreciated and very helpful for the Realm product & documentation teams, as well as other users.

Congrats also on SparkEats – your app looks great. I hope there’s an Android version on the way soon ;-).

Regards,
Stennie

Definitely, @Stennie — working on Android as we speak. Reanimated2 requires Hermes while IIUC Realm can’t quite do that yet, and so I’m porting off of Reanimated2.

Realm has been a lifesaver. Totally loving it.

1 Like

This is the sort of thing that frozen objects are supposed to address. Is there something which made them not work for you, or was this one of the docs problems where they failed to introduce the concept?

1 Like

@Thomas_Goyne : I somehow missed this concept. In looking at the Realm React Native SDK guide right now, through the Quick Start, Fundamentals, and even the Reacting To Changes guides, I still don’t see any references to freezing. I think the concept should be covered quite early in the RN SDK in that nearly everyone’s going to hit this problem if they’re on RN.

Thanks for sharing the concept! Does freezing an object make it spreadable as well? Because if so, I can get rid of all the object copying that I do and instead just freeze Realm objects before handing them out to React components.

1 Like

No, frozen objects still aren’t spreadable. They behave exactly like normal Realm objects except for that they always access an immutable snapshot of the data rather than updating to the latest version.

1 Like

This thread worries me as I am about to enable sync for a large user base. When you “restart sync” and “delete client files”, what is the implication of that? Do you delete the data of all your clients? If that would happen for my app only once, I would be out of business :frowning:

@Simon_Persson : I believe the client files are merely the local cache of what’s in the cloud. So deleting them should be Mostly Harmless™. What you lose when you delete a client file are all the changes that haven’t already been synced to the server. So, for instance, if a customer:

  1. Launches your app
  2. Turns on airplane mode
  3. Makes a bunch of changes via your app to the local Realm DB
  4. Deletes the client file (whether manually or through your code doing so)
  5. Turns off airplane mode

… then they will lose all the changes they made in step 3. But no changes prior to step 2 (say, months of data) will be lost if you delete the local client file — those changes will instead be sync’d down the next time you launch the app and it connects to the internet.

So in general, the risk when you delete local client files is that you’ll lose unsync’d changes, IIUC. There are apparently ways to even recover from that, I believe (e.g. by loading those unsync’d changes prior to deleting the client file and copying them into the online Realm) — but I’m unfortunately no expert in that.

Thank you for your response.

So I guess this is the ClientResetError you are talking about? I was thinking it was more than that. Still, just loosing a day of work for a customer could cause bad reviews on AppStore/Google play, so I really need to get the error handling for client resets right. On a sidenote… I believe this was handled automatically at one point, but got removed with the transition to MongoDb Realm (MongoDb employees maybe know?).