-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AsyncOpen() with Flexible Sync: Progress Indication #8468
Comments
I don't know much about how Realm's internals work. It seems like Realm is establishing a "history" of changes so that it can sync writes. If that's the case, it would be really great if Unfortunately, I assume that this process works "from the bottom up", so that it's not possible to load the latest state of the Realm (even in a read-only state) until all 12,000+ changesets have been "integrated". That makes Realm Sync a really poor choice for any large, shared data store. |
Each of the log messages are printed after committing a batch of changesets, and if you do a non-async open during bootstrap application you can read the data written so far. We don't release the write lock while this is happening so trying to write while bootstrap application is happening will block until it's complete. I think our assumption around query bootstrapping would be that the download would be the slow part rather than the application, but that's clearly not the case here. |
Question: What is your use case for There is a property wrapper in SwiftUI We've got a fairly large flexSync dataset with about 10,000 objects and never really experienced any kind of delays in accessing the data. We're trying to understand how that would be used so we can avoid crazy long delays going forward. |
Sure, happy to add context!
|
Progress notifications for flexible sync downloads are in progress. The reason we originally didn't have them is that the server is just streaming query results to the client, rather than buffering the full result set in memory on the server before sending them. The server has now implemented estimated progress information for this, and we're working on surfacing it in the client. We're planning on eliminating the separate download and application steps for query bootstraps, at least in the async open case. This would make the whole thing quite a bit faster, and relevant to this issue, make it so that the download progress notifications cover everything you need. We probably need to keep the separate download and apply steps for changes to subscriptions while the app is running, so that won't completely remove the need for bootstrap application progress notifications. |
@Jaycyn regarding The end goal is to disallow users from editing data in the app unless the model is up-to-date with the latest changes. This is a desktop app where the Mac is virtually guaranteed to be online (unless a server is down, etc.). Suppose a project named "Top Gun" exists. If we open the Realm with empty data and it takes a few minutes to sync everything down in the background, we don't want the user to go, "Oh, the Top Gun project is missing. I'll just create it again." Then, once sync finishes, we have two "Top Gun" projects that we now have to merge/de-dupe. This is my use-case for |
I see and thanks for the explanation - we are a macOS developer and no SwiftUI so we understand - please bear with my question. I am not really clear on the difference (speed wise) between the bootstrapping and downloading. We've used await code (previously a closure) that shows a spinner while downloading data with a 'please wait' message which doesn't allow the user to create anything new while downloading, just browse existing data.
once that completes, the data is fully downloaded and the user can then create objects. While not ideal for the user, it at least avoids them creating duplicate objects. I am no longer finding references or examples to That then begs the question; what about AsyncOpenTask? Which states
So... and this is more of a @tgoyne question; is AsycOpenTask not functional? Or will that be the implementation with Asking questions as while the delays we experience are handled via the above, as the dataset and app size grows, so will the delays so what's the best practice here - the workaround provided in the original post? |
@Jaycyn yea, in my case the download is pretty quick: 20 seconds or so. It's the bootstrapping that requires MINUTES to complete. I don't think that progresstask tracks the bootstrapping process after the actual bytes have finished downloading from Atlas. @tgoyne mentioned these two steps are going to be combined. At that point, the progresstask might be useful. |
@tgoyne In the meantime, what can I do to minimize the number of changesets that must be bootstrapped? I've lowered the client-reset window to 5 days on Atlas so the app isn't keeping 30 days' of sync events. Do many small write transactions (as opposed to fewer, larger write transactions) produce more changesets? If there's something I can do to optimize/reduce these, I'm game. |
The changesets in the query bootstrapped phase are synthesized history from the backing mongodb data and nothing you do other than change how much data you're subscribing to will change how many of them there are. It's only after bootstrapping is complete that you start receiving changesets from other clients. The raw number of changesets is also not super meaningful, as we apply the changesets in batches to reduce the per-transaction overhead. The batch size is currently only 1 MB which is probably too small, but increasing that to a much larger number would probably be something like a 10-20% speedup and not something which makes it not painfully slow. |
Hey, so I'll close this as a duplicate of #8476 for the progress notifications case. It's something we've been working on for a while server-side and hope to expose support for it on the client in the near future. For the actual changeset application speedup, you can subscribe to realm/realm-core#7285 for updates. This is more of a medium-term project with a lot of moving parts though, so I imagine it'll take a bit longer to land. |
Problem
If you use
asyncOpen()
to open a flexible-sync Realm of any non-trivial size, you'll find that it takes FOREVER. The download completes quickly, but Realm then takes an absolute geologic era to "bootstrap changesets", as shown here:Unfortunately, Realm provides no progress API for this and requests to add it have been ignored for years...like many Realm issues. Mongo staff just tell you not to use
{}asyncOpen(){
}, but that's not an acceptable solution for many apps: users will start creating new model objects because they think those objects are missing when, in reality, they just haven't been "bootstrapped" yet. Once they are, we've got a mess of duplicate model objects: ugh.The Correct Solution
Realm needs to add a progress API for
asyncOpen()
so that download and bootstrap progress can be reported to the user.The Stupid Workaround
Until Realm fixes this, there is a fragile, no-good way to hack it ourselves. It's resource-intensive and dumb, but when it takes 5+ minutes to open a Realm, users NEED to see progress. My approach relies on the fact that Realm is very chatty in the logs:
Using The Workaround
In whatever controller where you're opening the Realm async, add an ivar:
Then, just before you call
{}asyncOpen(){
}, call{}setupBootstrapProgressTrackingTask(){
}. This task will check for Realm log messages every 3 seconds and calculate the current progress of the bootstrapping. You'll just need to swap in your own code to go back to the main thread and update whatever UI is appropriate (for me, that's a "SpinnerItem" object that holds a message and progress value that I display in my UI.)When
asyncOpen()
completes or errors, cancel the Task. (Or have the Task look up some iVar on your controller object that indicates loading is happening and cancel itself if that value changes.)Warnings:
This is obviously stupid and fragile. If the log messages change, it will break. If an error occurs, the Task cancels and just resets to an indeterminate progress indicator. This is also resource-intensive and wasteful. But it's worth it so that we don't leave users hanging with a 5-minute indeterminate spinner, wondering if the app is broken or hung.
This method does not show progress of the actual download from Atlas. For my app, that happens very quickly—about 15 seconds. The "bootstrapping" is what takes FOREVER to complete.
For Realm Engineers:
Progress APIs for long-running tasks are not optional. They are not "nice to have" things. They are expected and this problem should have been solved and closed years ago. Users can't be expected to let an app just sit and spin for 5+ minutes without force-quitting it and thinking it's broken.
How important is this improvement for you?
Dealbreaker
Feature would mainly be used with
Atlas Device Sync
The text was updated successfully, but these errors were encountered: