Repository navigation
Use sqlite3_connection_pool package on native platforms - #131
Conversation
simolus3
left a comment
There was a problem hiding this comment.
Some comments that might help to review this.
rkistner
left a comment
There was a problem hiding this comment.
I like this change overall.
To confirm my understanding:
- This still uses a pool of connections and a pool of Isolates, similar to before.
- Unlikely before, it is no longer one connection per Isolate - each individual operation gets assigned a connection ref and an Isolate, but that Isolate can use any connection.
Is that correct?
I am curious, since this is not using the Isolate.run optimization, where do the performance improvements come from?
I notice there is a still a custom_functions_example.dart, but it crashes hard with Callback invoked after it has been deleted. Is there a way to prevent those custom functions from being created in the APIs, or otherwise throw a better error?
Correct.
That is a good question. I wanted to use It is worth noting that default benchmarks only measure writes without I believe the performance benefits at this point are mostly a result of managing locks in Rust. Some benchmarks that only acquire / release locks did see substantial improvements:
Managing locks wasn't particularly expensive before either, but I suspect it's enough to cause the modest improvement. |
I'm not aware of a good way to do that without adding APIs to |
This replaces the custom connection pool implementation based on isolate messages with the
sqlite3_connection_poolpackage.That package manages a SQLite connection pool (one writer, additional readers) in Rust. Compared to our Dart implementation, this has a few benefits:
Arcin Rust, two isolates can access the same pool independently and share connection locks. In Dart, coordinating this required explicit isolate communication. This also works with different Flutter engines (e.g. a background task and the main UI, which was impossible before).The size of the diff really tells the full story here, implementing connection pools via isolate communication is really hard and this removes a lot of complexity from the project.
Changes in behavior and possible regressions
Previously, connections were owned by a single isolate. Now, connections are shared in a pool and may be accessed from different isolates too (but never concurrently, we're still compatible with
THREADSAFE=1mode in SQLite). Due to the way native callables work in Dart, this makes it impossible to register Dart functions that could be called from SQLite:I have also removed the isolate connection factory API. Instead, users should simply create a second instance using the same path on another isolate. The Rust helper will automatically merge the two instances internally.
Other changes in behavior that I observed from tests:
PoolAbortExceptioninstead of aTimeoutException. I think this is fine, especially since we're about to migrate from timeouts to a more general abort mechanism.Benchmarks
I re-ran benchmarks that are enabled by default using
dart run scripts/benchmark.dart:Most benchmarks benefit from this change,
writeTransaction no awaitis a notable regression. I didn't profile this, but I assume that's because the non-awaiting benchmark relied on a single isolate owning the database serializing everything (since it's a single thread). The other regression for write transactions is worth investigating as well, but I don't think that's a blocker for this PR.