Merge pull request #1598 from kuhnroyal/feature/isolate-serialization

Move serialization decision from server to client
This commit is contained in:
Simon Binder 2021-12-22 18:22:19 +01:00 committed by GitHub
commit 344453750f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 43 deletions

View File

@ -39,13 +39,33 @@ class DriftIsolate {
/// can reconstruct a [DriftIsolate] by using [DriftIsolate.fromConnectPort]. /// can reconstruct a [DriftIsolate] by using [DriftIsolate.fromConnectPort].
final SendPort connectPort; final SendPort connectPort;
/// The flag indicating whether messages between this [DriftIsolate]
/// and the [DriftServer] should be serialized.
final bool serialize;
/// Creates a [DriftIsolate] talking to another isolate by using the /// Creates a [DriftIsolate] talking to another isolate by using the
/// [connectPort]. /// [connectPort].
DriftIsolate.fromConnectPort(this.connectPort); ///
/// {@template drift_isolate_serialize}
/// Internally, drift uses ports from `dart:isolate` to send commands to an
/// internal server dispatching database actions.
/// In most setups, those ports can send and receive almost any Dart object.
/// In special cases though, the platform only supports sending simple types
/// across send types. In particular, isolates across different Flutter
/// engines (such as the ones spawned by the `workmanager` package) are
/// unable to handle most objects.
/// To support those setups, drift can serialize its internal communication
/// channel to only send simple types across isolates. The [serialize]
/// parameter, which is enabled by default, controls this behavior.
///
/// In most scenarios, [serialize] can be disabled for a considerable
/// performance improvement.
/// {@endtemplate}
DriftIsolate.fromConnectPort(this.connectPort, {this.serialize = true});
StreamChannel _open() { StreamChannel _open() {
final receive = ReceivePort('drift client receive'); final receive = ReceivePort('drift client receive');
connectPort.send(receive.sendPort); connectPort.send([receive.sendPort, serialize]);
final controller = final controller =
StreamChannelController(allowForeignErrors: false, sync: true); StreamChannelController(allowForeignErrors: false, sync: true);
@ -65,27 +85,8 @@ class DriftIsolate {
/// All operations on the returned [DatabaseConnection] will be executed on a /// All operations on the returned [DatabaseConnection] will be executed on a
/// background isolate. Setting the [isolateDebugLog] is only helpful when /// background isolate. Setting the [isolateDebugLog] is only helpful when
/// debugging drift itself. /// debugging drift itself.
///
/// {@template drift_isolate_serialize}
/// Internally, drift uses ports from `dart:isolate` to send commands to an
/// internal server dispatching database actions.
/// In most setups, those ports can send and receive almost any Dart object.
/// In special cases though, the platform only supports sending simple types
/// across send types. In particular, isolates across different Flutter
/// engines (such as the ones spawned by the `workmanager` package) are
/// unable to handle most objects.
/// To support those setups, drift can serialize its internal communication
/// channel to only send simple types across isolates. The [serialize]
/// parameter, which is enabled by default, controls this behavior.
///
/// In most scenarios, [serialize] can be disabled for a considerable
/// performance improvement.
/// Please note that the value of [serialize] __must__ be the same across the
/// methods [spawn], [inCurrent], [connect] and [shutdownAll].
/// {@endtemplate}
// todo: breaking: Make synchronous in drift 2 // todo: breaking: Make synchronous in drift 2
Future<DatabaseConnection> connect( Future<DatabaseConnection> connect({bool isolateDebugLog = false}) async {
{bool isolateDebugLog = false, bool serialize = true}) async {
return remote(_open(), debugLog: isolateDebugLog, serialize: serialize); return remote(_open(), debugLog: isolateDebugLog, serialize: serialize);
} }
@ -93,9 +94,7 @@ class DriftIsolate {
/// created. /// created.
/// If you only want to disconnect a database connection created via /// If you only want to disconnect a database connection created via
/// [connect], use [GeneratedDatabase.close] instead. /// [connect], use [GeneratedDatabase.close] instead.
/// Future<void> shutdownAll() {
/// {@macro drift_isolate_serialize}
Future<void> shutdownAll({bool serialize = true}) {
return shutdown(_open(), serialize: serialize); return shutdown(_open(), serialize: serialize);
} }
@ -113,14 +112,13 @@ class DriftIsolate {
/// ///
/// {@macro drift_isolate_serialize} /// {@macro drift_isolate_serialize}
static Future<DriftIsolate> spawn(DatabaseOpener opener, static Future<DriftIsolate> spawn(DatabaseOpener opener,
{bool serialize = true}) async { {bool serialize = false}) async {
final receiveServer = ReceivePort(); final receiveServer = ReceivePort();
final keyFuture = receiveServer.first; final keyFuture = receiveServer.first;
await Isolate.spawn( await Isolate.spawn(_startDriftIsolate, [receiveServer.sendPort, opener]);
_startDriftIsolate, [receiveServer.sendPort, opener, serialize]);
final key = await keyFuture as SendPort; final key = await keyFuture as SendPort;
return DriftIsolate.fromConnectPort(key); return DriftIsolate.fromConnectPort(key, serialize: serialize);
} }
/// Creates a [DriftIsolate] in the [Isolate.current] isolate. The returned /// Creates a [DriftIsolate] in the [Isolate.current] isolate. The returned
@ -134,10 +132,13 @@ class DriftIsolate {
/// ///
/// {@macro drift_isolate_serialize} /// {@macro drift_isolate_serialize}
factory DriftIsolate.inCurrent(DatabaseOpener opener, factory DriftIsolate.inCurrent(DatabaseOpener opener,
{bool killIsolateWhenDone = false, bool serialize = true}) { {bool killIsolateWhenDone = false, bool serialize = false}) {
final server = RunningDriftServer(Isolate.current, opener(), final server = RunningDriftServer(Isolate.current, opener(),
killIsolateWhenDone: killIsolateWhenDone, serialize: serialize); killIsolateWhenDone: killIsolateWhenDone);
return DriftIsolate.fromConnectPort(server.portToOpenConnection); return DriftIsolate.fromConnectPort(
server.portToOpenConnection,
serialize: serialize,
);
} }
} }
@ -151,9 +152,7 @@ class DriftIsolate {
void _startDriftIsolate(List args) { void _startDriftIsolate(List args) {
final sendPort = args[0] as SendPort; final sendPort = args[0] as SendPort;
final opener = args[1] as DatabaseOpener; final opener = args[1] as DatabaseOpener;
final serialize = args[2] as bool;
final server = final server = RunningDriftServer(Isolate.current, opener());
RunningDriftServer(Isolate.current, opener(), serialize: serialize);
sendPort.send(server.portToOpenConnection); sendPort.send(server.portToOpenConnection);
} }

View File

@ -13,7 +13,6 @@ import '../remote.dart';
class RunningDriftServer { class RunningDriftServer {
final Isolate self; final Isolate self;
final bool killIsolateWhenDone; final bool killIsolateWhenDone;
final bool serialize;
final DriftServer server; final DriftServer server;
final ReceivePort connectPort = ReceivePort('drift connect'); final ReceivePort connectPort = ReceivePort('drift connect');
@ -22,14 +21,16 @@ class RunningDriftServer {
SendPort get portToOpenConnection => connectPort.sendPort; SendPort get portToOpenConnection => connectPort.sendPort;
RunningDriftServer(this.self, DatabaseConnection connection, RunningDriftServer(this.self, DatabaseConnection connection,
{this.killIsolateWhenDone = true, required this.serialize}) {this.killIsolateWhenDone = true})
: server = DriftServer(connection, allowRemoteShutdown: true) { : server = DriftServer(connection, allowRemoteShutdown: true) {
final subscription = connectPort.listen((message) { final subscription = connectPort.listen((message) {
if (message is SendPort) { if (message is List && message.length == 2) {
final sendPort = message[0]! as SendPort;
final serialize = message[1]! as bool;
final receiveForConnection = final receiveForConnection =
ReceivePort('drift channel #${_counter++}'); ReceivePort('drift channel #${_counter++}');
message.send(receiveForConnection.sendPort); sendPort.send(receiveForConnection.sendPort);
final channel = IsolateChannel(receiveForConnection, message); final channel = IsolateChannel(receiveForConnection, sendPort);
server.serve(channel, serialize: serialize); server.serve(channel, serialize: serialize);
} }

View File

@ -116,8 +116,7 @@ void _runTests(FutureOr<DriftIsolate> Function() spawner, bool terminateIsolate,
isolate = await spawner(); isolate = await spawner();
database = TodoDb.connect( database = TodoDb.connect(
DatabaseConnection.delayed( DatabaseConnection.delayed(isolate.connect()),
isolate.connect(isolateDebugLog: false, serialize: serialize)),
); );
}); });
@ -125,7 +124,7 @@ void _runTests(FutureOr<DriftIsolate> Function() spawner, bool terminateIsolate,
await database.close(); await database.close();
if (terminateIsolate) { if (terminateIsolate) {
await isolate.shutdownAll(serialize: serialize); await isolate.shutdownAll();
} }
}); });