IceCream: Hangs when there are too many changes

My app gives the user the option to delete all his data and also to restore data from backup files.

When erasing all data, I used realm.deleteAll(). Now that I’m using IceCream, I iterate over all my objects and set isDeleted to true.

Then, when restoring data, I go over a different Realm database and use realm.create() to add it to the current Realm.

Now, using IceCream, if I open my app in two devices at the same time, erase all data and then add it back from a backup file, the app will receive the cloudKitDataDidChangeRemotely hundreds of times and it’ll hang forever, making it unusable and requiring me to force quit.

In these cases I think it would be better to group multiple changes together and sync them on a background thread.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 18 (16 by maintainers)

Most upvoted comments

@dbmrq I think I’ve gotten something to work (heavily inspired by your branch). I modified the BackgroundWorker to behave slightly differently, so it behaves sort of like a Realm-enabled background dispatch queue. I’ve also changed the SyncEngine such that fetching database changes occurs serially (which could be a problem now that we’re using a background process).

You may want to try this: https://github.com/kitlangton/IceCream/tree/background-sync

I’m going to clean it up a little bit and submit a PR soon.

I am running into the same issue with the one-to-many relationships triggering a cascade of updates for child objects when the parent object changes.

The only solution I have come up with so far is to use foreign key association of parent/child objects. So instead of Dog having an Owner as a stored property it just stores the primary key of an owner and has a convenience property to lookup the Owner by key in the Realm.

Aha! I think I got it now. Looking into it a little further I think my problem is related to using multiple objects with one-to-many relationships.

I have Book and ReadingSession objects. A book can have many reading sessions (linked with Realm’s LinkingObjects), and each ReadingSession has a single Book (stored in a book property).

If I add just SyncObject<Book>() or just SyncObject<ReadingSession>() to the SyncEngine, everything works fine: I make a change to a Book, get a single notification with zid = BooksZone and the Book is updated on the other device; I make a change to a ReadingSession, get a single notification with zid = ReadingSessionsZone and the ReadingSession is updated. The problem starts when I add both objects to the SyncEngine. Then, if I make a change to a Book, I get a notification with zid = BooksZone and, besides that, a notification with zid = ReadingSessionsZone for every ReadingSession that book has. I assume this happens because the ReadingSession has a book property populated with the Book object, and that object was changed, so it’s like the ReadingSession changed (but it doesn’t actually need to be updated, because it still belongs to the same Book, it’s only the Book that changed).

So if I have a Book with 100 reading sessions and I change its title, I should get only one notification with zid = BooksZone, but instead I get 101 notifications: the correct one and 100 others with zid = ReadingSessionsZone, one for each of the Book’s reading sessions. And that’s just the tip of the iceberg, because my reading sessions can also have notes and quotes and those should be synced too. So changing a book triggers notifications for the book and all it’s reading sessions and all the notes and quotes for each one of those reading sessions. This makes the library pretty much unusable with multiple objects. I get hundreds of notifications for every little change and the app freezes in no time.

So there are two different problems here. One is that those changes block the interface, which shouldn’t happen. But the second problem is that there shouldn’t actually be that many changes. Let me know if you want me to open a different issue for that.

Also I have no idea how to go about this, I still don’t completely understand how this library works. But if you don’t have the time to fix it I’d appreciate if you could guide me in the right direction so I can try to do it myself.

@kitlangton Fantastic, thank you! I’m on a trip right now, so I won’t be able to try this until after the holidays, but it’s looking great! 🍻

I just used the Time Profiler instrument on my app and triggered the problem. Here’s the result for the main thread:

captura de tela 2018-10-10 as 23 27 03

The worst offenders seem to be SyncObject.add(record:) and SyncObject.registerLocalDatabase(). Then there are all those Realm functions, but most of them are probably triggered by these two, and I think they could be performed in a background thread.