fix: ignore log db corruptions (#29656)

* fix: ignore log db corruptions

* review suggestion

* log corruption recovery

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong
2026-07-09 22:06:04 +05:30
committed by GitHub
parent 2db1e02cdf
commit 4fe33289b8
3 changed files with 46 additions and 18 deletions
@@ -373,6 +373,15 @@ final class _DriftPoolStreamQueries extends StreamQueryStore {
}
}
Future<void> deleteSqliteDatabase({required String name}) async {
final file = await _databaseFile(name);
await [
file.path,
'${file.path}-wal',
'${file.path}-shm',
].map((path) => File(path).delete().catchError((_) => File(path), test: (e) => e is FileSystemException)).wait;
}
Future<SqliteConnection> openSqliteConnection({required String name}) async {
return _openImmichDatabase(await _databaseFile(name));
}
@@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
@@ -12,6 +11,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/generated/codegen_loader.g.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
@@ -25,8 +25,6 @@ import 'package:immich_mobile/theme/theme_data.dart';
import 'package:immich_mobile/widgets/common/immich_logo.dart';
import 'package:immich_mobile/widgets/common/immich_title_text.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:url_launcher/url_launcher.dart' show launchUrl, LaunchMode;
class BootstrapErrorWidget extends StatelessWidget {
@@ -132,13 +130,7 @@ class _BottomPanelState extends State<_BottomPanel> {
}
try {
final dir = await getApplicationDocumentsDirectory();
for (final suffix in ['', '-wal', '-shm']) {
final file = File(path.join(dir.path, 'immich.sqlite$suffix'));
if (await file.exists()) {
await file.delete();
}
}
await deleteSqliteDatabase(name: 'immich');
} catch (_) {
return;
}
+35 -8
View File
@@ -6,10 +6,13 @@ import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
import 'package:immich_mobile/utils/debug_print.dart';
import 'package:logging/logging.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:sqlite3/common.dart';
void configureFileDownloaderNotifications() {
FileDownloader().configureNotificationForGroup(
@@ -46,18 +49,12 @@ abstract final class Bootstrap {
await configureSqliteCache();
final (db, updatePool) = await openSqliteConnectionWithUpdatePool(name: 'immich');
final drift = Drift.sqlite(db, updatePool);
final logDb = DriftLogger.sqlite(await openSqliteConnection(name: 'immich_logs'));
final DriftStoreRepository storeRepo = DriftStoreRepository(drift);
await StoreService.init(storeRepository: storeRepo, listenUpdates: listenStoreUpdates);
final settingsRepo = await SettingsRepository.ensureInitialized(drift);
await LogService.init(
logRepository: LogRepository(logDb),
settingsRepository: settingsRepo,
shouldBuffer: shouldBufferLogs,
);
final logDb = await _initLogger(settingsRepository: settingsRepo, shouldBufferLogs: shouldBufferLogs);
await NetworkRepository.init();
// Remove once all asset operations are migrated to Native APIs
@@ -65,3 +62,33 @@ abstract final class Bootstrap {
return (drift, logDb);
}
}
Future<DriftLogger> _initLogger({required SettingsRepository settingsRepository, bool shouldBufferLogs = true}) async {
Future<DriftLogger> open() async => DriftLogger.sqlite(await openSqliteConnection(name: 'immich_logs'));
DriftLogger logDb = await open();
bool wasCorrupt = false;
try {
await logDb.customSelect('SELECT COUNT(*) FROM logger_messages').get();
} on SqliteException catch (error) {
if (error.resultCode != SqlError.SQLITE_CORRUPT && error.resultCode != SqlError.SQLITE_NOTADB) {
await logDb.close();
rethrow;
}
dPrint(() => 'Logs database is corrupt, recreating it');
await logDb.close();
await deleteSqliteDatabase(name: 'immich_logs');
logDb = await open();
wasCorrupt = true;
}
await LogService.init(
logRepository: LogRepository(logDb),
settingsRepository: settingsRepository,
shouldBuffer: shouldBufferLogs,
);
if (wasCorrupt) {
Logger('bootstrap:initLogger').warning('Logs database was corrupt and has been recreated');
}
return logDb;
}