diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index e81fe58ba9..3e74a5ba36 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -1,7 +1,8 @@ import 'dart:async'; +import 'dart:io'; import 'package:drift/drift.dart'; -import 'package:drift_flutter/drift_flutter.dart'; +import 'package:drift_sqlite_async/drift_sqlite_async.dart'; import 'package:flutter/foundation.dart'; import 'package:immich_mobile/infrastructure/entities/asset_edit.entity.dart'; import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart'; @@ -31,6 +32,10 @@ import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart' import 'package:immich_mobile/infrastructure/repositories/db.repository.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.steps.dart'; import 'package:logging/logging.dart'; +import 'package:path/path.dart' as p; +import 'package:path_provider/path_provider.dart'; +import 'package:sqlite3/sqlite3.dart'; +import 'package:sqlite_async/sqlite_async.dart'; @DriftDatabase( tables: [ @@ -60,8 +65,9 @@ import 'package:logging/logging.dart'; include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'}, ) class Drift extends $Drift { - Drift([QueryExecutor? executor]) - : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); + Drift(super.executor); + + Drift.sqlite(SqliteConnection db) : super(SqliteAsyncDriftConnection(db)); Future reset() async { // https://github.com/simolus3/drift/commit/bd80a46264b6dd833ef4fd87fffc03f5a832ab41#diff-3f879e03b4a35779344ef16170b9353608dd9c42385f5402ec6035aac4dd8a04R76-R94 @@ -305,3 +311,18 @@ class DriftDatabaseRepository { Future transaction(Future Function() callback) => _db.transaction(callback); } + +Future openSqliteConnection({required String name}) async { + final dbFolder = await getApplicationDocumentsDirectory(); + final file = File(p.join(dbFolder.path, '$name.sqlite')); + return SqliteDatabase(path: file.path); +} + +Future configureSqliteCache() async { + // Make sqlite3 pick a more suitable location for temporary files - the + // one from the system may be inaccessible due to sand-boxing. + final cacheBase = (await getTemporaryDirectory()).path; + // We can't access /tmp on Android, which sqlite3 would try by default. + // Explicitly tell it about the correct temporary directory. + sqlite3.tempDirectory = cacheBase; +} diff --git a/mobile/lib/infrastructure/repositories/logger_db.repository.dart b/mobile/lib/infrastructure/repositories/logger_db.repository.dart index d11174356d..32af4af748 100644 --- a/mobile/lib/infrastructure/repositories/logger_db.repository.dart +++ b/mobile/lib/infrastructure/repositories/logger_db.repository.dart @@ -1,14 +1,14 @@ import 'package:drift/drift.dart'; -import 'package:drift_flutter/drift_flutter.dart'; +import 'package:drift_sqlite_async/drift_sqlite_async.dart'; import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.drift.dart'; +import 'package:sqlite_async/sqlite_async.dart'; @DriftDatabase(tables: [LogMessageEntity]) class DriftLogger extends $DriftLogger { - DriftLogger([QueryExecutor? executor]) - : super( - executor ?? driftDatabase(name: 'immich_logs', native: const DriftNativeOptions(shareAcrossIsolates: true)), - ); + DriftLogger.fromExecutor(super.executor); + + DriftLogger.sqlite(SqliteConnection db) : super(SqliteAsyncDriftConnection(db)); @override int get schemaVersion => 1; @@ -19,7 +19,8 @@ class DriftLogger extends $DriftLogger { await customStatement('PRAGMA foreign_keys = ON'); await customStatement('PRAGMA synchronous = NORMAL'); await customStatement('PRAGMA journal_mode = WAL'); - await customStatement('PRAGMA busy_timeout = 500'); + await customStatement('PRAGMA busy_timeout = 30000'); // 30s + await customStatement('PRAGMA cache_size = -32000'); // 32MB await customStatement('PRAGMA temp_store = MEMORY'); }, ); diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index 68ebfe9c9f..5630943a8f 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -43,8 +43,9 @@ void configureFileDownloaderNotifications() { abstract final class Bootstrap { static Future<(Drift, DriftLogger)> initDomain({bool listenStoreUpdates = true, bool shouldBufferLogs = true}) async { - final drift = Drift(); - final logDb = DriftLogger(); + await configureSqliteCache(); + final drift = Drift.sqlite(await openSqliteConnection(name: 'immich')); + final logDb = DriftLogger.sqlite(await openSqliteConnection(name: 'immich_logs')); final DriftStoreRepository storeRepo = DriftStoreRepository(drift); await StoreService.init(storeRepository: storeRepo, listenUpdates: listenStoreUpdates); diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 5be3ff9768..4f5e0f642b 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -370,11 +370,11 @@ packages: url: "https://pub.dev" source: hosted version: "2.32.1" - drift_flutter: + drift_sqlite_async: dependency: "direct main" description: - name: drift_flutter - sha256: "887fdec622174dc7eaefd0048403e34ee07cc18626ac8a7544cc3b8a4a172166" + name: drift_sqlite_async + sha256: "1b6e99562fc5d35fe5e3696741720a8aca47f4c3eee35d4b9b94be819f53a6f6" url: "https://pub.dev" source: hosted version: "0.3.0" @@ -1619,30 +1619,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.2" - sqlcipher_flutter_libs: - dependency: transitive - description: - name: sqlcipher_flutter_libs - sha256: "38d62d659d2fb8739bf25a42c9a350d1fdd6c29a5a61f13a946778ec75d27929" - url: "https://pub.dev" - source: hosted - version: "0.7.0+eol" sqlite3: - dependency: transitive + dependency: "direct main" description: name: sqlite3 sha256: "56da3e13ed7d28a66f930aa2b2b29db6736a233f08283326e96321dd812030f5" url: "https://pub.dev" source: hosted version: "3.3.1" - sqlite3_flutter_libs: + sqlite3_connection_pool: dependency: transitive description: - name: sqlite3_flutter_libs - sha256: "3ed7553eee7bb368f8950f58ba29f634e06e813c029aff6a0d60862b96de8454" + name: sqlite3_connection_pool + sha256: "90b25972c7699d84da97df1c5919804275560b4ab8a158bbec890434b9718f65" url: "https://pub.dev" source: hosted - version: "0.6.0+eol" + version: "0.2.4" + sqlite3_web: + dependency: transitive + description: + name: sqlite3_web + sha256: d876398a9f2cbf115d93fc34901f8fa129b58b13b5fa9377156ed3a9a05695e3 + url: "https://pub.dev" + source: hosted + version: "0.7.1" + sqlite_async: + dependency: "direct main" + description: + name: sqlite_async + sha256: "4c243c5386eba3a7102f98999388a7e0a7f2632e4e06dafb3b4f5a44170a26f6" + url: "https://pub.dev" + source: hosted + version: "0.14.1" sqlparser: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 491ecada07..37264fed1d 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: crypto: ^3.0.7 device_info_plus: ^12.4.0 drift: ^2.32.1 - drift_flutter: ^0.3.0 + drift_sqlite_async: 0.3.0 dynamic_color: ^1.8.1 easy_localization: ^3.0.8 ffi: ^2.2.0 @@ -66,6 +66,8 @@ dependencies: share_plus: ^10.1.4 sliver_tools: ^0.2.12 stream_transform: ^2.1.1 + sqlite3: ^3.3.1 + sqlite_async: 0.14.1 thumbhash: 0.1.0+1 timezone: ^0.9.4 url_launcher: ^6.3.2 diff --git a/mobile/test/domain/services/local_sync_service_test.dart b/mobile/test/domain/services/local_sync_service_test.dart index c15bf9b2d0..fbd48ca6a9 100644 --- a/mobile/test/domain/services/local_sync_service_test.dart +++ b/mobile/test/domain/services/local_sync_service_test.dart @@ -131,7 +131,7 @@ void main() { durationMs: 0, orientation: 0, isFavorite: false, - playbackStyle: PlatformAssetPlaybackStyle.image + playbackStyle: PlatformAssetPlaybackStyle.image, ); final assetsToRestore = [LocalAssetStub.image1]; @@ -215,7 +215,7 @@ void main() { isFavorite: false, createdAt: 1700000000, updatedAt: 1732000000, - playbackStyle: PlatformAssetPlaybackStyle.image + playbackStyle: PlatformAssetPlaybackStyle.image, ); final localAsset = platformAsset.toLocalAsset();