Closed thumbert closed 1 year ago
Is this how it should work?
Yes, because the standard Actor
always runs in a Dart Isolate
. The name Isolate
comes from the fact that it runs on an almost completely isolated environment: there's no memory sharing, no global state sharing, between isolates. The only way to share information is by passing messages around.
In your case, you should ensure the initialization method is called on all handlers. I believe you can do that in the constructor to avoid having to check on every handle invocation.
Alternatively, you could try to make the timezone itself part of the message you send to the actor.
It doesn't make sense to open and close the connection on each actor.
Why doesn't it make sense? An Actor seems to be a great place to keep a database connection pool. It has a lifetime, just like a connection pool.
Perhaps what does not make sense is to start a large number of Actors, each with its own connection pool. But that's not how Actors in Dart are supposed to be used... you probably shouldn't start more Actors than you have CPUs available, unless the actors are indepdendent of each other, in which case that would be fine. But if an Actor holds a stateful object like a connection pool, then you want to have only a few open at any given time: and that's ok. Perhaps use an ActorGroup
in such case.
Thanks. I will explore some more.
Hi,
Here's a minimal example using a database connection that I can't make it to work. I use mongodb via the package mongo_dart
.
class FileHandler with Handler<String, int> {
FileHandler();
late Db db;
@override
Future<int> handle(String dateTime) async {
await Future.delayed(Duration(seconds: 1));
var x = <String, dynamic>{
'datetime': dateTime,
'values': List.generate(100, (index) => Random().nextInt(100)),
};
var collection = db.collection('actors');
await collection.insert(x);
print('Finished with $dateTime');
return 0;
}
}
Future<void> main() async {
var withActor = true;
var db = Db('mongodb://127.0.0.1/test');
await db.open();
var handler = FileHandler()..db = db;
if (withActor) {
final actor = Actor(handler);
await actor.send('2023-01-01');
await actor.close();
} else {
await handler.handle('2023-01-01');
}
await db.close();
}
I get the error message
Unhandled exception:
Invalid argument(s): Illegal argument in isolate message: (object extends NativeWrapper - Library:'dart:io' Class: _NativeSocket@14069316)
#0 Isolate._spawnFunction (dart:isolate-patch/isolate_patch.dart:399:25)
#1 Isolate.spawn (dart:isolate-patch/isolate_patch.dart:379:7)
#2 ActorImpl.spawn (package:actors/src/isolate/isolate_actor.dart:23:20)
#3 new Actor (package:actors/src/actors_base.dart:122:16)
#4 main (file:///.../test/db/update_dbs_test.dart:60:19)
<asynchronous suspension>
If I just use the handler
directly, the code works fine.
What do I need to do to make my simple example work?
Thanks, Tony
The Db
object must be created in the Handler
's constructor, as I had said before. You cannot "set" it fom the main Isolate as you're doing.
The simple rule is: never keep an instance of a Handler
around. The pattern should always be:
final actor = Actor(< create handler here >);
Try something like this:
class FileHandler with Handler<String, int> {
Future<Db> _init() async {
var db = Db('mongodb://127.0.0.1/test');
await db.open();
return db;
});
Db? _db;
FileHandler();
@override
Future<int> handle(String dateTime) async {
var db = _db;
if (db == null) {
db = await _init();
_db = db;
}
final db = await _db;
await Future.delayed(Duration(seconds: 1));
var x = <String, dynamic>{
'datetime': dateTime,
'values': List.generate(100, (index) => Random().nextInt(100)),
};
var collection = db.collection('actors');
await collection.insert(x);
print('Finished with $dateTime');
return 0;
}
}
Future<void> main() async {
var withActor = true;
if (withActor) {
final actor = Actor(FileHandler());
await actor.send('2023-01-01');
await actor.close();
} else {
await handler.handle('2023-01-01');
}
await db.close();
}
This lazily initializes the Db on first usage, avoiding initializing it outside the Actor's Isolate.
Many thanks for the example. I finally get it. Not sure why I wasn't understanding you the first time. I've also noticed the close method for a Handler, where I can close my db object. Very exciting!
Hi,
I'm exploring your package. Looks great. I'm running into an issue and want to understand if it's normal or there is a work around. See the example below. I'm using the package 'timezone'.
This code will fail because the the actor doesn't see the timezone database initialized although I do it in the main thread. If I explicitly initialize it in the handle method, it works. Is this how it should work?
Related to this situation, if I have a database connection and I open it in the main thread, from inside the actor the database connection does not appear to be open. It doesn't make sense to open and close the connection on each actor. How to deal with this situation?
Thanks for any suggestions, Tony