Repository navigation
sqflite_watchos: SQL runs on a background isolate - #38
Merged
Merged
Conversation
Issue #20, item 1, spec 0024. Every call ran on the calling isolate, the UI isolate in an app, so a long query held up a frame and the 2-second lock wait froze the UI. * lib/src/worker.dart runs the handler on one long-lived isolate, as sqflite_common_ffi's databaseFactoryFfi does: the first call starts it, every database shares it, and the databases, transactions and cursors live there. Each call crosses as a message with an id, and every answer comes back on one port. Calls made before the isolate is up keep their order, and a call queued behind a transaction does not hold up the call that ends it. It is adapted from sqflite_common_ffi's isolate_io.dart and method_call.dart and carries their BSD-2-Clause notice. * A SqfliteDatabaseException crosses as data (message, details, result code, transactionClosed) and is built again on the caller's side, so sqflite_common sees the same error. A file error or an ArgumentError is copied as it is, as Isolate.run copies one. An argument that cannot be sent fails its call as a SqfliteDatabaseException. * SqfliteWatchos.libraryPathOverride, a path the isolate opens, replaces libraryOverride, since a DynamicLibrary cannot cross isolates. resetForTesting returns a Future: the isolate closes its databases and ends. sqliteVersion stays synchronous, and registerWith binds nothing until the first call. * The busy test's comment names the likelier cause of the 39 ms wait on Ubuntu: flutter test runs the Dart profiler, which samples threads with SIGPROF on Linux and so ends SQLite's busy-handler sleeps early. On macOS the test also checks that the test isolate ticks while the worker waits. * README, CHANGELOG (0.0.1, unpublished, edited in place) and PORTING_REPORT describe the new model and what the lock wait blocks. Checked: 52 host tests on macOS (39 as before, 13 new). The two tick checks fail on the old code (0 ticks). Mutations that drop transactionClosed, send other errors as text, skip closing the databases, or await each call in turn each fail tests. flutter analyze, dart format, check_words, check_versions, check_test_names, symcheck and check_readme_snippets pass. Not run: CI's Ubuntu job, the Simulator and a physical watch.
From the review of the background isolate, spec 0024. All eight findings were low after verification. * The worker ends with the isolate that started it. When the worker's port arrives, that isolate adds it as an exit listener of its own, and the null exit message stops the worker as a stop does: it closes every database and ends. The answer port is a RawReceivePort that keeps the isolate alive only while an answer is due, a call is unsent or a stop has not finished, so an isolate that used sqflite can end on its own. Before, every isolate that used the factory left a worker behind, and an Isolate.spawn helper never ended. * SqfliteWatchosWorker gets two @VisibleForTesting members: isStarted, and crashForTesting, which ends the isolate with an error it does not catch. * New worker tests: - an isolate ended by that error fails a call queued behind an open transaction with isDatabaseClosedError() and the error's text; - stop() completes only on the isolate's exit, after every answer; - a stop made with calls in flight, or before the isolate's port arrived, answers those calls first and still closes their databases; - the isolate starts on the first call, and building the factory starts none; - an argument that cannot be sent fails a whole batch; - an Isolate.run session leaves no worker once it ends, and an Isolate.spawn helper that used the factory ends on its own. * The stop test's matcher is the handler's own cancel error, and the lock checks write through a connection with busy_timeout 0, so a lock still held fails at once. * An argument that cannot be sent keeps failing the whole call, as a method channel's codec fails it on iOS. The CHANGELOG and worker.dart now say so, and that its details hold the call's SQL and arguments but no database, and a batch's none. * Upstream's integration test passed on the watchOS 27.0 Simulator with SQL on the background isolate (the owner's run of 2026-10-30), so PORTING_REPORT and the CHANGELOG record it. Checked: 60 host tests on macOS (42 and 18; 8 new). Each of these changes to the worker fails tests: leaking the calls an ended isolate never answered, ignoring its error report, closing without cancelling, a stop() that resolves early, a stop that fails the calls already made, a stop before the port that is dropped, an eager start, no exit listener, and an answer port that always keeps its isolate alive. flutter analyze, dart format, check_words, check_versions, check_test_names, symcheck and check_readme_snippets pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #20: SQL moves off the UI isolate.
Design. One background isolate per isolate that uses the factory. It follows sqflite_common_ffi's
databaseFactoryFfimodel, andlib/src/worker.dartis adapted from its isolate code (with the BSD-2 notice).COMMIT.SqfliteDatabaseExceptioncrosses as data, keeping its message, details, result code andtransactionClosed, and is rebuilt as the same type.database_closed, and none hangs.Behaviour that changes, also in the README and CHANGELOG:
singleInstance: true.libraryOverridebecomeslibraryPathOverride. The package is unpublished, so there is no deprecation.Tests. 60 host tests pass, 21 of them new. They show that:
Isolate.runor a helper isolate.Each of nine mutations of the worker fails at least one test.
Simulator (watchOS 27.0, Series 12 46 mm). The first commit, merged with #37, passed upstream's integration test (14 tests), and upstream's example ran its "Raw tests" page with no failure in the console. The final commit passed the same checks: the integration test (14 tests), the example starting with no error in its console (first frame 0.7 s), and its "Raw tests" page.
Not yet: a physical watch. That stays on #20, along with the busy test's SIGPROF note, which is not proven on Linux.
An independent review confirmed eight low-severity findings in the first commit. The second commit fixes them all.