ObjectBox Dart/Flutter мультиизолированный доступ

Создание отдельной ветки для вопроса, указанного в комментарии ...

Как ObjectBox обрабатывает одновременные (разными потоками / изоляторами) запросы записи? Пример моего варианта использования: вызов FCM «onBackgroundmessage» выполняется в другом изолированном пространстве, в то же время может произойти несколько запросов на запись. «Улей» в этом случае полностью рушится. Есть ли встроенное решение в ObjectBox?

2 ответа

ObjectBox для dart основан на собственной базовой библиотеке ObjectBox, которая обрабатывает параллелизм с помощью транзакций . Позвольте мне выделить несколько моментов, имеющих отношение к вопросу:

  • Транзакции управляют многопоточностью; например, транзакция привязана к потоку и наоборот.
  • Транзакции (только для чтения) никогда не блокируются и не блокируют транзакцию записи.
  • Одновременно может быть только одна транзакция записи; они выполняются строго один за другим (последовательно).

Что касается изолят в Dart / флаттере - да, они могут безопасно получить доступ к такому же магазину, вам просто нужно , чтобы убедиться , что он действительно является тот же родным экземпляром магазина. Для этого вы делаете следующие шаги:

  1. Создать Store() instance в вашем основном изоляте, как обычно.
  2. Получать ByteData store.reference который содержит информацию о собственном магазине.
  3. Отправьте ссылку на другой изолят через SendPort.
  4. В другом изоляте получите ссылку и откройте локальный экземпляр, используя Store.fromReference(getObjectBoxModel(), msg as ByteData).
  5. Теперь у обоих изолятов есть собственный дротик. Storeэкземпляры, которые внутренне используют одно и то же базовое собственное хранилище. Следовательно, их транзакции синхронизированы, они оба видят одни и те же данные и получают уведомления об изменении данных. 🎉

Вы можете увидеть код, который я только что описал для этого тестового примера: https://github.com/objectbox/objectbox-dart/blob/461a948439dcc42f3956b7d21b232eb9c2bc26e1/objectbox/test/isolates_test.dart#L50

Убедитесь, что вы не закрываете магазин, пока его использует другой изолят. Лучше вообще не закрывать его - это лучший способ для обычных приложений без огромной фоновой работы.

Спустя несколько лет после принятого ответа у objectbox теперь есть дополнительная функция, которая может хорошо работать с изолятом.

Это функция Store.attach() . Используя эту функцию, вы можете подключить хранилище, указав путь к базе данных, не беспокоясь о том, что отдельный изолят закроет хранилище:

Фактическое базовое хранилище закрывается только тогда, когда закрывается последний экземпляр хранилища (например, при выходе из приложения).

В моем случае мне пришлось использоватьpackage для выполнения фоновой задачи, которая зависит от хранилища объектов.

Вы можете создать класс «инициализатор» следующим образом:

      class ObjectBox {
  static late final Store store;

  static Future initialize() async {
    var docsDir = await getApplicationDocumentsDirectory();
    var dbPath = p.join(docsDir.path, "objectbox");

    if (Store.isOpen(dbPath)) {
      store = Store.attach(getObjectBoxModel(), dbPath);
    } else {
      store = await openStore(directory: dbPath);
    }
  }
}

Получив это, вы можете использоватьinitialize()в вашем изоляте. Пример сworkmanagerупаковка:

main.dart

      @pragma("vm:entry-point")
void callbackDispatcher() {
  Workmanager().executeTask((taskName, inputData) async {
    await ObjectBox.initialize(); // Initialize in background isolate.

    return true;
  });
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await ObjectBox.initialize(); // Initialize in main isolate

  Workmanager().initialize(callbackDispatcher);

  runApp(const MyApp());
}

Таким образом, даже если вы закроете базу данных в основном изоляте, фоновый изолят продолжит функционировать. Само базовое хранилище закрывается после закрытия всех экземпляров хранилища.

Другие вопросы по тегам