-
-
Notifications
You must be signed in to change notification settings - Fork 727
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
ValueObservation notifies duplicate initial value when started from a DatabasePool #937
Comments
Hello @pocketpixels, This is a known behavior of the GRDB 5 DatabasePool that I'm still struggling to avoid. It is not strictly a bug, because the documentation carefully says that ValueObservation can notify duplicates: apps should be ready for such stuttering. But just as you say, this can create performance issues. And this is just weird. And once you notice it, you can't forget it. Meh. There is a benefit, though. When you start a ValueObservation on a DatabasePool, you are now guaranteed to get the initial value shortly, even if there is a long write transaction in the background. With GRDB 4, observations could only start after all pending writes were completed, and this could create a really bad user experience. Slow writes happen, for example, in apps that sync a lot of data with a remote server, and those could not observe and quickly display data on screen until those long writes would complete (#601, addressed by #736). So for one win (butter-smooth observations), we get one loss (double notification of the initial value). There is already a fix, for custom SQLite builds compiled with the The difficulty is that when you start a
On step (4), if we can prove for sure that between the initial read in a reader connection (1), and the setup of the commit hooks on the writer connection (3), no change was performed, then we can avoid the double notification. It's possible with Without this option, I could never manage to do it. Some serious extra SQLite mojo is needed 😅 |
I currently have no real workaround. You can try Another solution is to keep a long living observation, so that the cost of the initial double fetch happens only once in the lifetime of your application. YMMV |
Hi Gwendal, Could it be possible to start with step 2, check if the writer connection is idle (with nothing queued up) and if yes, synchronously execute the initial fetch on that (doing the check and conditional scheduling as one atomic operation)? And perform step 1 only if the writer connection is not idle? |
Switching to the |
There's no such API in DispatchQueue. Generally speaking, observations that perform their initial fetch from the writer queue are off the table. Some users would complain that such an observation postpones writes, in a noticeable fashion when the fetch is slow. The purpose of So I'm still looking after another technique.
Yes, please give it a try. Nevertheless, I hope we can make progress on the topic. A few days ago I stumbled upon SQLITE_FCNTL_DATA_VERSION and had great hopes. Unfortunately, this would not give the expected result. The great difficulty is to tell if the database is different when seen from one SQLite connection (the reader that performs the initial fetch), and another SQLite connection (the writer where observation really starts). The Maybe, as you suggest, we can acquire the same level of robustness with DispatchQueue juggling at the GRDB level. Good ideas are always welcome! |
I'll keep this issue open until we find a fix. It's not strictly a bug, but it's a real inconvenience. |
I just can't leave issues open, especially during a lockdown: #940 |
This issue can be solved with a custom SQLite build, with the I reported to Apple the need for these apis (FB9793771 - Expose SQLite APIs enabled by the SQLITE_ENABLE_SNAPSHOT compilation option). I'm not aware of any other solution. When a ValueObservation fetches its initial value without waiting for a write access, we need a way to tell if the initial read was performed on the same database state than the one found when the transaction observer is installed. I'm closing this issue with the "needs revisiting" flag, because there's not much that can be done at this point. |
#1248 is the final nail |
What did you do?
I just updated from GRDB 4 + GRDBCombine to GRDB 5.
It was surprisingly painless and the only code change I had to make was to replace the
fetchOnSubscription()
calls withscheduling: .immediate
when creating the Combine publishers.What did you expect to happen?
I expected identical notification behavior as before. That is, one initial value notification right away, and then one notification for each observed value change after that.
What happened instead?
The initial value is being sent twice.
Dealing with the duplicate notification is trivial.
However the fact that the SQL query is executed twice can be a potentially significant performance regression.
(In my application this is an issue on the watch)
Environment
GRDB flavor(s): GRDB
GRDB version: 5.5.0
Installation method: CocoaPods
Xcode version: 12.5 beta 3
Swift version: 5
Platform(s) running GRDB: iOS and watchOS
macOS version running Xcode:
The text was updated successfully, but these errors were encountered: