mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
fix: concurrency issue (#21830)
This commit is contained in:
parent
722a464e23
commit
42a03f2556
4 changed files with 90 additions and 73 deletions
|
|
@ -169,7 +169,10 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
|
|||
}
|
||||
|
||||
try {
|
||||
final backgroundSyncManager = _ref.read(backgroundSyncProvider);
|
||||
_isCleanedUp = true;
|
||||
_ref.dispose();
|
||||
|
||||
_cancellationToken.cancel();
|
||||
_logger.info("Cleaning up background worker");
|
||||
final cleanupFutures = [
|
||||
|
|
@ -179,14 +182,13 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
|
|||
}),
|
||||
_drift.close(),
|
||||
_driftLogger.close(),
|
||||
_ref.read(backgroundSyncProvider).cancel(),
|
||||
_ref.read(backgroundSyncProvider).cancelLocal(),
|
||||
backgroundSyncManager.cancel(),
|
||||
backgroundSyncManager.cancelLocal(),
|
||||
];
|
||||
|
||||
if (_isar.isOpen) {
|
||||
cleanupFutures.add(_isar.close());
|
||||
}
|
||||
_ref.dispose();
|
||||
await Future.wait(cleanupFutures);
|
||||
_logger.info("Background worker resources cleaned up");
|
||||
} catch (error, stack) {
|
||||
|
|
@ -195,35 +197,42 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
|
|||
}
|
||||
|
||||
Future<void> _handleBackup() async {
|
||||
if (!_isBackupEnabled || _isCleanedUp) {
|
||||
_logger.info("[_handleBackup 1] Backup is disabled. Skipping backup routine");
|
||||
return;
|
||||
}
|
||||
await runZonedGuarded(
|
||||
() async {
|
||||
if (!_isBackupEnabled || _isCleanedUp) {
|
||||
_logger.info("[_handleBackup 1] Backup is disabled. Skipping backup routine");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.info("[_handleBackup 2] Enqueuing assets for backup from the background service");
|
||||
_logger.info("[_handleBackup 2] Enqueuing assets for backup from the background service");
|
||||
|
||||
final currentUser = _ref.read(currentUserProvider);
|
||||
if (currentUser == null) {
|
||||
_logger.warning("[_handleBackup 3] No current user found. Skipping backup from background");
|
||||
return;
|
||||
}
|
||||
final currentUser = _ref.read(currentUserProvider);
|
||||
if (currentUser == null) {
|
||||
_logger.warning("[_handleBackup 3] No current user found. Skipping backup from background");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.info("[_handleBackup 4] Resume backup from background");
|
||||
if (Platform.isIOS) {
|
||||
return _ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id);
|
||||
}
|
||||
_logger.info("[_handleBackup 4] Resume backup from background");
|
||||
if (Platform.isIOS) {
|
||||
return _ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id);
|
||||
}
|
||||
|
||||
final canPing = await _ref.read(serverInfoServiceProvider).ping();
|
||||
if (!canPing) {
|
||||
_logger.warning("[_handleBackup 5] Server is not reachable. Skipping backup from background");
|
||||
return;
|
||||
}
|
||||
final canPing = await _ref.read(serverInfoServiceProvider).ping();
|
||||
if (!canPing) {
|
||||
_logger.warning("[_handleBackup 5] Server is not reachable. Skipping backup from background");
|
||||
return;
|
||||
}
|
||||
|
||||
final networkCapabilities = await _ref.read(connectivityApiProvider).getCapabilities();
|
||||
final networkCapabilities = await _ref.read(connectivityApiProvider).getCapabilities();
|
||||
|
||||
return _ref
|
||||
.read(uploadServiceProvider)
|
||||
.startBackupWithHttpClient(currentUser.id, networkCapabilities.hasWifi, _cancellationToken);
|
||||
return _ref
|
||||
.read(uploadServiceProvider)
|
||||
.startBackupWithHttpClient(currentUser.id, networkCapabilities.hasWifi, _cancellationToken);
|
||||
},
|
||||
(error, stack) {
|
||||
debugPrint("Error in backup zone $error, $stack");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _syncAssets({Duration? hashTimeout}) async {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
|
||||
Future<void> syncLinkedAlbumsIsolated(ProviderContainer ref) {
|
||||
final user = ref.read(currentUserProvider);
|
||||
final user = Store.tryGet(StoreKey.currentUser);
|
||||
if (user == null) {
|
||||
return Future.value();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ void main() async {
|
|||
await Bootstrap.initDomain(isar, drift, logDb);
|
||||
await initApp();
|
||||
// Warm-up isolate pool for worker manager
|
||||
await workerManager.init(dynamicSpawning: false);
|
||||
await workerManager.init(dynamicSpawning: true);
|
||||
await migrateDatabaseIfNeeded(isar, drift);
|
||||
HttpSSLOptions.apply();
|
||||
|
||||
|
|
|
|||
|
|
@ -31,55 +31,62 @@ Cancelable<T?> runInIsolateGentle<T>({
|
|||
}
|
||||
|
||||
return workerManager.executeGentle((cancelledChecker) async {
|
||||
BackgroundIsolateBinaryMessenger.ensureInitialized(token);
|
||||
DartPluginRegistrant.ensureInitialized();
|
||||
await runZonedGuarded(
|
||||
() async {
|
||||
BackgroundIsolateBinaryMessenger.ensureInitialized(token);
|
||||
DartPluginRegistrant.ensureInitialized();
|
||||
|
||||
final (isar, drift, logDb) = await Bootstrap.initDB();
|
||||
await Bootstrap.initDomain(isar, drift, logDb, shouldBufferLogs: false);
|
||||
final ref = ProviderContainer(
|
||||
overrides: [
|
||||
// TODO: Remove once isar is removed
|
||||
dbProvider.overrideWithValue(isar),
|
||||
isarProvider.overrideWithValue(isar),
|
||||
cancellationProvider.overrideWithValue(cancelledChecker),
|
||||
driftProvider.overrideWith(driftOverride(drift)),
|
||||
],
|
||||
);
|
||||
final (isar, drift, logDb) = await Bootstrap.initDB();
|
||||
await Bootstrap.initDomain(isar, drift, logDb, shouldBufferLogs: false);
|
||||
final ref = ProviderContainer(
|
||||
overrides: [
|
||||
// TODO: Remove once isar is removed
|
||||
dbProvider.overrideWithValue(isar),
|
||||
isarProvider.overrideWithValue(isar),
|
||||
cancellationProvider.overrideWithValue(cancelledChecker),
|
||||
driftProvider.overrideWith(driftOverride(drift)),
|
||||
],
|
||||
);
|
||||
|
||||
Logger log = Logger("IsolateLogger");
|
||||
Logger log = Logger("IsolateLogger");
|
||||
|
||||
try {
|
||||
HttpSSLOptions.apply(applyNative: false);
|
||||
return await computation(ref);
|
||||
} on CanceledError {
|
||||
log.warning("Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}");
|
||||
} catch (error, stack) {
|
||||
log.severe("Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", error, stack);
|
||||
} finally {
|
||||
try {
|
||||
await LogService.I.dispose();
|
||||
await logDb.close();
|
||||
await ref.read(driftProvider).close();
|
||||
|
||||
// Close Isar safely
|
||||
try {
|
||||
final isar = ref.read(isarProvider);
|
||||
if (isar.isOpen) {
|
||||
await isar.close();
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("Error closing Isar: $e");
|
||||
}
|
||||
HttpSSLOptions.apply(applyNative: false);
|
||||
return await computation(ref);
|
||||
} on CanceledError {
|
||||
log.warning("Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}");
|
||||
} catch (error, stack) {
|
||||
log.severe("Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", error, stack);
|
||||
} finally {
|
||||
try {
|
||||
ref.dispose();
|
||||
|
||||
ref.dispose();
|
||||
} catch (error, stack) {
|
||||
debugPrint("Error closing resources in isolate: $error, $stack");
|
||||
} finally {
|
||||
ref.dispose();
|
||||
// Delay to ensure all resources are released
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
}
|
||||
}
|
||||
await LogService.I.dispose();
|
||||
await logDb.close();
|
||||
await drift.close();
|
||||
|
||||
// Close Isar safely
|
||||
try {
|
||||
if (isar.isOpen) {
|
||||
await isar.close();
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("Error closing Isar: $e");
|
||||
}
|
||||
} catch (error, stack) {
|
||||
debugPrint("Error closing resources in isolate: $error, $stack");
|
||||
} finally {
|
||||
ref.dispose();
|
||||
// Delay to ensure all resources are released
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
(error, stack) {
|
||||
debugPrint("Error in isolate zone: $error, $stack");
|
||||
},
|
||||
);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue