simolus3 / drift

Drift is an easy to use, reactive, typesafe persistence library for Dart & Flutter.
https://drift.simonbinder.eu/
MIT License
2.62k stars 365 forks source link

Publish working example projects that shows how to use the DriftIsolates? #2095

Open madsane29 opened 2 years ago

madsane29 commented 2 years ago

Maybe it is just me but honestly I have no idea how to implement this: https://drift.simonbinder.eu/docs/advanced-features/isolates/

I'd like fully working examples where I can see in which files the given method is, how it is used throughout the app (is it stored somewhere or open a new appdatabase whenever needed), what is in the main etc...

Do I just randomly call into this db from main thread's method middle part and it will run next code on a new thread? Or I have to call the method in a new isolate to make it work?

I'd like to run some inserts in an isolate since it is blocking the UI but I can't decode what's in the doc. A complete project with examples would help a lot...

simolus3 commented 2 years ago

The Flutter app example uses a DriftIsolate, does that help you regarding the setup?

Do I just randomly call into this db from main thread's method middle part and it will run next code on a new thread?

That is the idea, yes. Drift has an asynchronous API and will schedule the operation in the background isolate when you cal it in the main isolate. That is the whole idea of Drift isolates. I will make that clearer in the documentation and also link to the example.

AvidanGC commented 2 years ago

I have a background service where I update data and the change is not reflected in the UI, I am using stream with .watch(), any clue?

jack24254029 commented 1 year ago

I have a background service where I update data and the change is not reflected in the UI, I am using stream with .watch(), any clue?

Same question.

The app received the FCM, called the API and inserted data into database on background (app was not terminated) then back to app, app not trigger watch() to update UI

raghavsatyadev commented 1 year ago

Hello @simolus3 can you provide any solutions to make drift work in the background service? https://pub.dev/packages/flutter_background_service

luohao123 commented 1 year ago

@simolus3 Got same problem here, any solution? I opened an issue for emphasis the priority

jbxbergdev commented 3 months ago

Agree, the Isolates documentation doesn't yield enough information to make Streams across isolates work. Also seems outdated in regards to Flutter APIs (e.g. RootIsolateToken.instance may return null).

simolus3 commented 3 months ago

Do they just not work at all for you? It's a bit surprising if isolates work but streams don't since they use the same protocol - do you have more information about what you've tried? Maybe you're running into a bug, but we have tests for stream updates across isolates.

Also seems outdated in regards to Flutter APIs (e.g. RootIsolateToken.instance may return null).

Thanks, fixed in ca7f177ed94b8482248f024e73d79b3a05d96f2b. We can't depend on Flutter when building the docs so it's not easy to keep up with all Flutter API changes.

jbxbergdev commented 3 months ago

@simolus3 thanks for your reply!

So I initialize Drift as follows (from the main Isolate as well as from the background one):

static Future<DriftIsolate> createIsolateWithSpawn() async {
    final token = RootIsolateToken.instance;
    return await DriftIsolate.spawn(() {
      if (token != null) {
        BackgroundIsolateBinaryMessenger.ensureInitialized(token);
      }
      return LazyDatabase(() async {
        final dbFolder = await getApplicationDocumentsDirectory();
        final path = join(dbFolder.path, 'mydb.sqlite');
        if (Platform.isAndroid) {
          await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
        }
        final cachebase = (await getTemporaryDirectory()).path;
        sqlite3.tempDirectory = cachebase;
        return NativeDatabase(File(path));
      });
    });
  }

Then, I create the database with

final isolate = await MyDatabase.createIsolateWithSpawn();
    _db = MyDatabase(await isolate.connect());

This is what I gathered from the documentation might be the correct way for being able to use streams across isolates (plus the null check on RootIsolateToken, as mentioned above).

The background Isolate is used to write into the database. In the foreground, I use watch to monitor changes on a table. However, the Stream in the foreground Isolate doesn't emit the data that is written into the database at runtime. It will however emit the inserted data after a restart of the app.

So far, I tested on Android only.

jbxbergdev commented 3 months ago

Ah, I think I got it now. I modified my database creation by storing/retrieving the DriftIsolate SendPort in IsolateNameServer:

static Future<DriftIsolate> createIsolateWithSpawn() async {
    final token = RootIsolateToken.instance;
    final isolate = await DriftIsolate.spawn(() {
      if (token != null) {
        BackgroundIsolateBinaryMessenger.ensureInitialized(token);
      }
      return LazyDatabase(() async {
        final dbFolder = await getApplicationDocumentsDirectory();
        final path = join(dbFolder.path, 'mydb.sqlite');
        if (Platform.isAndroid) {
          await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
        }
        final cachebase = (await getTemporaryDirectory()).path;
        sqlite3.tempDirectory = cachebase;
        return NativeDatabase(File(path));
      });
    });
    IsolateNameServer.registerPortWithName(isolate.connectPort, 'drift_connect_port');
    return isolate;
  }

and

static Future<DriftIsolate> createIsolateFromPort() async {
    final port = IsolateNameServer.lookupPortByName('drift_connect_port');
    return DriftIsolate.fromConnectPort(port!);
  }

From the main Isolate, I call

final isolate = await createIsolateWithSpawn();
    _db = MyDatabase(await isolate.connect());

and, after the above, from the background Isolate

final isolate = await createIsolateFromPort();
    _db = MyDatabase(await isolate.connect());

Not sure if this is by the book, but the main isolate stream emits data now. :)

simolus3 commented 3 months ago

Yeah that sounds correct to me - if you call DriftIsolate.spawn multiple times, you'll get two independent isolates that won't talk with each other to update streams. Using name ports to coordinate isolate creation is a correct solution :+1: