From 9996a1ec3e91f2aaa88f7b8606780405d55b56f7 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 8 Sep 2025 21:10:51 -0500 Subject: [PATCH] fix: prevent database deadlock (eps 2) --- mobile/integration_test/test_utils/general_helper.dart | 2 +- .../lib/domain/services/background_worker.service.dart | 2 +- .../lib/infrastructure/repositories/db.repository.dart | 10 ++++++++-- .../repositories/logger_db.repository.dart | 8 ++++++-- mobile/lib/main.dart | 2 +- mobile/lib/services/background.service.dart | 2 +- mobile/lib/services/backup_verification.service.dart | 2 +- mobile/lib/utils/bootstrap.dart | 6 +++--- mobile/lib/utils/isolate.dart | 2 +- mobile/lib/widgets/common/immich_sliver_app_bar.dart | 4 +++- mobile/test/drift/main/migration_test.dart | 2 +- .../repositories/local_album_repository_test.dart | 2 +- mobile/test/modules/shared/sync_service_test.dart | 5 ++++- 13 files changed, 32 insertions(+), 17 deletions(-) diff --git a/mobile/integration_test/test_utils/general_helper.dart b/mobile/integration_test/test_utils/general_helper.dart index d6065170ef..f4026fecee 100644 --- a/mobile/integration_test/test_utils/general_helper.dart +++ b/mobile/integration_test/test_utils/general_helper.dart @@ -39,7 +39,7 @@ class ImmichTestHelper { static Future loadApp(WidgetTester tester) async { await EasyLocalization.ensureInitialized(); // Clear all data from Isar (reuse existing instance if available) - final (isar, drift, logDb) = await Bootstrap.initDB(); + final (isar, drift, logDb) = await Bootstrap.initDB(shareAcrossIsolates: true); await Bootstrap.initDomain(isar, drift, logDb); await Store.clear(); await isar.writeTxn(() => isar.clear()); diff --git a/mobile/lib/domain/services/background_worker.service.dart b/mobile/lib/domain/services/background_worker.service.dart index b3d97e0938..60f9f9ce08 100644 --- a/mobile/lib/domain/services/background_worker.service.dart +++ b/mobile/lib/domain/services/background_worker.service.dart @@ -246,7 +246,7 @@ Future backgroundSyncNativeEntrypoint() async { WidgetsFlutterBinding.ensureInitialized(); DartPluginRegistrant.ensureInitialized(); - final (isar, drift, logDB) = await Bootstrap.initDB(); + final (isar, drift, logDB) = await Bootstrap.initDB(shareAcrossIsolates: false); await Bootstrap.initDomain(isar, drift, logDB, shouldBufferLogs: false); await BackgroundWorkerBgService(isar: isar, drift: drift, driftLogger: logDB).init(); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 028fbda403..6b574b94be 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -66,8 +66,14 @@ class IsarDatabaseRepository implements IDatabaseRepository { include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'}, ) class Drift extends $Drift implements IDatabaseRepository { - Drift([QueryExecutor? executor]) - : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); + Drift({QueryExecutor? executor, bool shareAcrossIsolates = true}) + : super( + executor ?? + driftDatabase( + name: 'immich', + native: DriftNativeOptions(shareAcrossIsolates: shareAcrossIsolates), + ), + ); @override int get schemaVersion => 10; diff --git a/mobile/lib/infrastructure/repositories/logger_db.repository.dart b/mobile/lib/infrastructure/repositories/logger_db.repository.dart index 583fc42813..571525a7ca 100644 --- a/mobile/lib/infrastructure/repositories/logger_db.repository.dart +++ b/mobile/lib/infrastructure/repositories/logger_db.repository.dart @@ -7,9 +7,13 @@ import 'logger_db.repository.drift.dart'; @DriftDatabase(tables: [LogMessageEntity]) class DriftLogger extends $DriftLogger implements IDatabaseRepository { - DriftLogger([QueryExecutor? executor]) + DriftLogger({QueryExecutor? executor, bool shareAcrossIsolates = true}) : super( - executor ?? driftDatabase(name: 'immich_logs', native: const DriftNativeOptions(shareAcrossIsolates: true)), + executor ?? + driftDatabase( + name: 'immich_logs', + native: DriftNativeOptions(shareAcrossIsolates: shareAcrossIsolates), + ), ); @override diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 9066c5bfc7..fa94c3c10f 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -42,7 +42,7 @@ import 'package:worker_manager/worker_manager.dart'; void main() async { ImmichWidgetsBinding(); - final (isar, drift, logDb) = await Bootstrap.initDB(); + final (isar, drift, logDb) = await Bootstrap.initDB(shareAcrossIsolates: true); await Bootstrap.initDomain(isar, drift, logDb); await initApp(); // Warm-up isolate pool for worker manager diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart index e6436df244..82efc9bad4 100644 --- a/mobile/lib/services/background.service.dart +++ b/mobile/lib/services/background.service.dart @@ -330,7 +330,7 @@ class BackgroundService { } Future _onAssetsChanged() async { - final (isar, drift, logDb) = await Bootstrap.initDB(); + final (isar, drift, logDb) = await Bootstrap.initDB(shareAcrossIsolates: false); await Bootstrap.initDomain(isar, drift, logDb); final ref = ProviderContainer( diff --git a/mobile/lib/services/backup_verification.service.dart b/mobile/lib/services/backup_verification.service.dart index 94c4721cca..899b4621f9 100644 --- a/mobile/lib/services/backup_verification.service.dart +++ b/mobile/lib/services/backup_verification.service.dart @@ -115,7 +115,7 @@ class BackupVerificationService { assert(tuple.deleteCandidates.length == tuple.originals.length); final List result = []; BackgroundIsolateBinaryMessenger.ensureInitialized(tuple.rootIsolateToken); - final (isar, drift, logDb) = await Bootstrap.initDB(); + final (isar, drift, logDb) = await Bootstrap.initDB(shareAcrossIsolates: true); await Bootstrap.initDomain(isar, drift, logDb); await tuple.fileMediaRepository.enableBackgroundAccess(); final ApiService apiService = ApiService(); diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index e7abc66040..f02b23f5f3 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -56,9 +56,9 @@ void configureFileDownloaderNotifications() { } abstract final class Bootstrap { - static Future<(Isar isar, Drift drift, DriftLogger logDb)> initDB() async { - final drift = Drift(); - final logDb = DriftLogger(); + static Future<(Isar isar, Drift drift, DriftLogger logDb)> initDB({required bool shareAcrossIsolates}) async { + final drift = Drift(shareAcrossIsolates: shareAcrossIsolates); + final logDb = DriftLogger(shareAcrossIsolates: shareAcrossIsolates); Isar? isar = Isar.getInstance(); diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart index cca1498e0f..07ff897cbb 100644 --- a/mobile/lib/utils/isolate.dart +++ b/mobile/lib/utils/isolate.dart @@ -34,7 +34,7 @@ Cancelable runInIsolateGentle({ BackgroundIsolateBinaryMessenger.ensureInitialized(token); DartPluginRegistrant.ensureInitialized(); - final (isar, drift, logDb) = await Bootstrap.initDB(); + final (isar, drift, logDb) = await Bootstrap.initDB(shareAcrossIsolates: true); await Bootstrap.initDomain(isar, drift, logDb, shouldBufferLogs: false); final ref = ProviderContainer( overrides: [ diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index ee111851ad..e30213b10d 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -297,7 +297,9 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with Widget build(BuildContext context) { final syncStatus = ref.watch(syncStatusProvider); final isSyncing = syncStatus.isRemoteSyncing || syncStatus.isLocalSyncing; - + print( + "SyncStatusIndicator build - syncStatus.isRemoteSyncing: ${syncStatus.isRemoteSyncing} , isLocalSyncing: ${syncStatus.isLocalSyncing}, isHashing: ${syncStatus.isHashing}", + ); // Control animations based on sync status if (isSyncing) { if (!_rotationController.isAnimating) { diff --git a/mobile/test/drift/main/migration_test.dart b/mobile/test/drift/main/migration_test.dart index 74467492ae..6f36953a74 100644 --- a/mobile/test/drift/main/migration_test.dart +++ b/mobile/test/drift/main/migration_test.dart @@ -27,7 +27,7 @@ void main() { for (final toVersion in versions.skip(i + 1)) { test('to $toVersion', () async { final schema = await verifier.schemaAt(fromVersion); - final db = Drift(schema.newConnection()); + final db = Drift(executor: schema.newConnection()); await verifier.migrateAndValidate(db, toVersion); await db.close(); }); diff --git a/mobile/test/infrastructure/repositories/local_album_repository_test.dart b/mobile/test/infrastructure/repositories/local_album_repository_test.dart index fae0e09171..4d69cb9a2b 100644 --- a/mobile/test/infrastructure/repositories/local_album_repository_test.dart +++ b/mobile/test/infrastructure/repositories/local_album_repository_test.dart @@ -12,7 +12,7 @@ void main() { late MediumFactory mediumFactory; setUp(() { - db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); + db = Drift(executor: DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); mediumFactory = MediumFactory(db); }); diff --git a/mobile/test/modules/shared/sync_service_test.dart b/mobile/test/modules/shared/sync_service_test.dart index 767a52b8d8..f994f5b4f9 100644 --- a/mobile/test/modules/shared/sync_service_test.dart +++ b/mobile/test/modules/shared/sync_service_test.dart @@ -62,7 +62,10 @@ void main() { ); setUpAll(() async { - final loggerDb = DriftLogger(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); + final loggerDb = DriftLogger( + executor: DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true), + shareAcrossIsolates: true, + ); final LogRepository logRepository = LogRepository(loggerDb); WidgetsFlutterBinding.ensureInitialized();