serializableConnection method
Creates a DriftIsolate that, when connected to, will run queries on the
database already opened by this
.
This can be used to share existing database across isolates, as instances of generated database classes can't be sent across isolates by default. A DriftIsolate can be sent over ports though, which enables a concise way to open a temporary isolate that is using an existing database:
Future<void> main() async {
final database = MyDatabase(...);
// This is illegal - MyDatabase is not serializable
await Isolate.run(() async {
await database.batch(...);
});
// This will work. Only the `connection` is sent to the new isolate. By
// creating a new database instance based on the connection, the same
// logical database can be shared across isolates.
final connection = await database.serializableConnection();
await Isolate.run(() async {
final database = MyDatabase(await connection.connect());
await database.batch(...);
});
}
The example of running a short-lived database for a single task unit requiring a database is also available through computeWithDatabase.
Implementation
@experimental
Future<DriftIsolate> serializableConnection() async {
final currentlyInRootConnection = resolvedEngine is GeneratedDatabase;
// ignore: invalid_use_of_protected_member
final localConnection = resolvedEngine.connection;
final data = await localConnection.connectionData;
// If we're connected to an isolate already, we can use that one directly
// instead of starting a short-lived drift server.
// However, this does not work if [serializableConnection] is called in a
// transaction zone, since the top-level connection could be blocked waiting
// for the transaction (as transactions can't be concurrent in sqlite3).
if (data is DriftIsolate && currentlyInRootConnection) {
return data;
} else {
// Set up a drift server acting as a proxy to the existing database
// connection.
final server = RunningDriftServer(
Isolate.current,
localConnection,
onlyAcceptSingleConnection: true,
closeConnectionAfterShutdown: false,
killIsolateWhenDone: false,
);
// Since the existing database didn't use an isolate server, we need to
// manually forward stream query updates.
final forwardToServer = tableUpdates().listen((localUpdates) {
server.server.dispatchTableUpdateNotification(
NotifyTablesUpdated(localUpdates.toList()));
});
final forwardToLocal =
server.server.tableUpdateNotifications.listen((remoteUpdates) {
notifyUpdates(remoteUpdates.updates.toSet());
});
server.server.done.whenComplete(() {
forwardToServer.cancel();
forwardToLocal.cancel();
});
return DriftIsolate.fromConnectPort(
server.portToOpenConnection,
serialize: false,
);
}
}