Compare commits

...

1 Commits

Author SHA1 Message Date
Alex
9996a1ec3e fix: prevent database deadlock (eps 2) 2025-09-08 21:10:51 -05:00
13 changed files with 32 additions and 17 deletions

View File

@@ -39,7 +39,7 @@ class ImmichTestHelper {
static Future<void> loadApp(WidgetTester tester) async { static Future<void> loadApp(WidgetTester tester) async {
await EasyLocalization.ensureInitialized(); await EasyLocalization.ensureInitialized();
// Clear all data from Isar (reuse existing instance if available) // 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 Bootstrap.initDomain(isar, drift, logDb);
await Store.clear(); await Store.clear();
await isar.writeTxn(() => isar.clear()); await isar.writeTxn(() => isar.clear());

View File

@@ -246,7 +246,7 @@ Future<void> backgroundSyncNativeEntrypoint() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
DartPluginRegistrant.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 Bootstrap.initDomain(isar, drift, logDB, shouldBufferLogs: false);
await BackgroundWorkerBgService(isar: isar, drift: drift, driftLogger: logDB).init(); await BackgroundWorkerBgService(isar: isar, drift: drift, driftLogger: logDB).init();
} }

View File

@@ -66,8 +66,14 @@ class IsarDatabaseRepository implements IDatabaseRepository {
include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'}, include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'},
) )
class Drift extends $Drift implements IDatabaseRepository { class Drift extends $Drift implements IDatabaseRepository {
Drift([QueryExecutor? executor]) Drift({QueryExecutor? executor, bool shareAcrossIsolates = true})
: super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); : super(
executor ??
driftDatabase(
name: 'immich',
native: DriftNativeOptions(shareAcrossIsolates: shareAcrossIsolates),
),
);
@override @override
int get schemaVersion => 10; int get schemaVersion => 10;

View File

@@ -7,9 +7,13 @@ import 'logger_db.repository.drift.dart';
@DriftDatabase(tables: [LogMessageEntity]) @DriftDatabase(tables: [LogMessageEntity])
class DriftLogger extends $DriftLogger implements IDatabaseRepository { class DriftLogger extends $DriftLogger implements IDatabaseRepository {
DriftLogger([QueryExecutor? executor]) DriftLogger({QueryExecutor? executor, bool shareAcrossIsolates = true})
: super( : super(
executor ?? driftDatabase(name: 'immich_logs', native: const DriftNativeOptions(shareAcrossIsolates: true)), executor ??
driftDatabase(
name: 'immich_logs',
native: DriftNativeOptions(shareAcrossIsolates: shareAcrossIsolates),
),
); );
@override @override

View File

@@ -42,7 +42,7 @@ import 'package:worker_manager/worker_manager.dart';
void main() async { void main() async {
ImmichWidgetsBinding(); ImmichWidgetsBinding();
final (isar, drift, logDb) = await Bootstrap.initDB(); final (isar, drift, logDb) = await Bootstrap.initDB(shareAcrossIsolates: true);
await Bootstrap.initDomain(isar, drift, logDb); await Bootstrap.initDomain(isar, drift, logDb);
await initApp(); await initApp();
// Warm-up isolate pool for worker manager // Warm-up isolate pool for worker manager

View File

@@ -330,7 +330,7 @@ class BackgroundService {
} }
Future<bool> _onAssetsChanged() async { Future<bool> _onAssetsChanged() async {
final (isar, drift, logDb) = await Bootstrap.initDB(); final (isar, drift, logDb) = await Bootstrap.initDB(shareAcrossIsolates: false);
await Bootstrap.initDomain(isar, drift, logDb); await Bootstrap.initDomain(isar, drift, logDb);
final ref = ProviderContainer( final ref = ProviderContainer(

View File

@@ -115,7 +115,7 @@ class BackupVerificationService {
assert(tuple.deleteCandidates.length == tuple.originals.length); assert(tuple.deleteCandidates.length == tuple.originals.length);
final List<Asset> result = []; final List<Asset> result = [];
BackgroundIsolateBinaryMessenger.ensureInitialized(tuple.rootIsolateToken); 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 Bootstrap.initDomain(isar, drift, logDb);
await tuple.fileMediaRepository.enableBackgroundAccess(); await tuple.fileMediaRepository.enableBackgroundAccess();
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();

View File

@@ -56,9 +56,9 @@ void configureFileDownloaderNotifications() {
} }
abstract final class Bootstrap { abstract final class Bootstrap {
static Future<(Isar isar, Drift drift, DriftLogger logDb)> initDB() async { static Future<(Isar isar, Drift drift, DriftLogger logDb)> initDB({required bool shareAcrossIsolates}) async {
final drift = Drift(); final drift = Drift(shareAcrossIsolates: shareAcrossIsolates);
final logDb = DriftLogger(); final logDb = DriftLogger(shareAcrossIsolates: shareAcrossIsolates);
Isar? isar = Isar.getInstance(); Isar? isar = Isar.getInstance();

View File

@@ -34,7 +34,7 @@ Cancelable<T?> runInIsolateGentle<T>({
BackgroundIsolateBinaryMessenger.ensureInitialized(token); BackgroundIsolateBinaryMessenger.ensureInitialized(token);
DartPluginRegistrant.ensureInitialized(); 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); await Bootstrap.initDomain(isar, drift, logDb, shouldBufferLogs: false);
final ref = ProviderContainer( final ref = ProviderContainer(
overrides: [ overrides: [

View File

@@ -297,7 +297,9 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with
Widget build(BuildContext context) { Widget build(BuildContext context) {
final syncStatus = ref.watch(syncStatusProvider); final syncStatus = ref.watch(syncStatusProvider);
final isSyncing = syncStatus.isRemoteSyncing || syncStatus.isLocalSyncing; 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 // Control animations based on sync status
if (isSyncing) { if (isSyncing) {
if (!_rotationController.isAnimating) { if (!_rotationController.isAnimating) {

View File

@@ -27,7 +27,7 @@ void main() {
for (final toVersion in versions.skip(i + 1)) { for (final toVersion in versions.skip(i + 1)) {
test('to $toVersion', () async { test('to $toVersion', () async {
final schema = await verifier.schemaAt(fromVersion); final schema = await verifier.schemaAt(fromVersion);
final db = Drift(schema.newConnection()); final db = Drift(executor: schema.newConnection());
await verifier.migrateAndValidate(db, toVersion); await verifier.migrateAndValidate(db, toVersion);
await db.close(); await db.close();
}); });

View File

@@ -12,7 +12,7 @@ void main() {
late MediumFactory mediumFactory; late MediumFactory mediumFactory;
setUp(() { setUp(() {
db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); db = Drift(executor: DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
mediumFactory = MediumFactory(db); mediumFactory = MediumFactory(db);
}); });

View File

@@ -62,7 +62,10 @@ void main() {
); );
setUpAll(() async { 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); final LogRepository logRepository = LogRepository(loggerDb);
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();