From b8195acc9bdbf1d3513f2ce7f40aa1b8149fdd15 Mon Sep 17 00:00:00 2001 From: Edison Jwa Date: Sun, 24 May 2026 21:02:16 +0900 Subject: [PATCH] perf: avoid duplicate bootstrap work on join --- .../lib/services/app_bootstrap.dart | 73 ++++++++++++++++--- .../test/services/app_bootstrap_test.dart | 65 +++++++++++++++++ 2 files changed, 129 insertions(+), 9 deletions(-) diff --git a/apps/chanora_flutter/lib/services/app_bootstrap.dart b/apps/chanora_flutter/lib/services/app_bootstrap.dart index 5a79f15..ef14815 100644 --- a/apps/chanora_flutter/lib/services/app_bootstrap.dart +++ b/apps/chanora_flutter/lib/services/app_bootstrap.dart @@ -1,6 +1,7 @@ -import 'dart:io' show File; +import 'dart:io' show Directory, File; import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; @@ -9,7 +10,19 @@ import '../src/rust/api.dart' as rust; const String appSemverBaseline = 'v0.1.0'; const String _sileroVadAsset = 'assets/models/silero_vad.onnx'; -const String _tenVadAsset = 'assets/models/ten_vad.onnx'; + +typedef StorageDirectoryProvider = Future Function(); +typedef StorageInitializer = Future Function(String dir); + +Future? _storageInitFuture; +Future? _vadBootstrapFuture; +StorageDirectoryProvider _storageDirectoryProvider = + getApplicationSupportDirectory; +StorageInitializer _storageInitializer = _defaultStorageInitializer; + +Future _defaultStorageInitializer(String dir) { + return rust.initStorage(dir: dir); +} Future _copyBundledAssetToDocuments({ required String assetPath, @@ -29,16 +42,28 @@ Future _copyBundledAssetToDocuments({ } Future configureBundledVadModels() async { + final existing = _vadBootstrapFuture; + if (existing != null) { + await existing; + return; + } + + final bootstrap = _configureBundledVadModelsImpl(); + _vadBootstrapFuture = bootstrap; + try { + await bootstrap; + } catch (_) { + _vadBootstrapFuture = null; + rethrow; + } +} + +Future _configureBundledVadModelsImpl() async { final silero = await _copyBundledAssetToDocuments( assetPath: _sileroVadAsset, fileName: 'silero_vad.onnx', ); - final ten = await _copyBundledAssetToDocuments( - assetPath: _tenVadAsset, - fileName: 'ten_vad.onnx', - ); await rust.setVadModelPath(path: silero.path); - await rust.setTenVadModelPath(path: ten.path); } /// Resolve the human-readable app version displayed in About. @@ -69,15 +94,45 @@ String appVersionFromBuildNumber({ } Future wireStorage() async { + final existing = _storageInitFuture; + if (existing != null) { + await existing; + return; + } + + final initFuture = _wireStorageImpl(); + _storageInitFuture = initFuture; + await initFuture; +} + +Future _wireStorageImpl() async { + var initialized = false; try { - final dir = await getApplicationSupportDirectory(); - await rust.initStorage(dir: dir.path); + final dir = await _storageDirectoryProvider(); + await _storageInitializer(dir.path); + initialized = true; } catch (_) { // Best-effort; missing storage just means no identity persistence // and no bookmark list this session. + } finally { + if (!initialized) { + _storageInitFuture = null; + } } } +@visibleForTesting +void debugResetStorageBootstrap({ + StorageDirectoryProvider? storageDirectoryProvider, + StorageInitializer? storageInitializer, +}) { + _storageInitFuture = null; + _vadBootstrapFuture = null; + _storageDirectoryProvider = + storageDirectoryProvider ?? getApplicationSupportDirectory; + _storageInitializer = storageInitializer ?? _defaultStorageInitializer; +} + rust.BridgeNetworkState _mapConnectivity(List results) { if (results.isEmpty) return rust.BridgeNetworkState.unknown; final allNone = results.every((r) => r == ConnectivityResult.none); diff --git a/apps/chanora_flutter/test/services/app_bootstrap_test.dart b/apps/chanora_flutter/test/services/app_bootstrap_test.dart index 2b5a58b..5a077e8 100644 --- a/apps/chanora_flutter/test/services/app_bootstrap_test.dart +++ b/apps/chanora_flutter/test/services/app_bootstrap_test.dart @@ -1,8 +1,19 @@ +import 'dart:async'; +import 'dart:io'; + import 'package:flutter_test/flutter_test.dart'; import 'package:chanora_flutter/services/app_bootstrap.dart'; void main() { + setUp(() { + debugResetStorageBootstrap(); + }); + + tearDown(() { + debugResetStorageBootstrap(); + }); + test('app version appends platform build number', () { expect( appVersionFromBuildNumber( @@ -19,4 +30,58 @@ void main() { 'v1.2.3-rc.4', ); }); + + test('wireStorage reuses the in-flight initialization future', () async { + final completer = Completer(); + final tempDir = await Directory.systemTemp.createTemp( + 'chanora-app-bootstrap-test', + ); + addTearDown(() => tempDir.delete(recursive: true)); + + var initCalls = 0; + debugResetStorageBootstrap( + storageDirectoryProvider: () async => tempDir, + storageInitializer: (dir) async { + initCalls += 1; + await completer.future; + }, + ); + + final first = wireStorage(); + final second = wireStorage(); + var secondCompleted = false; + second.then((_) => secondCompleted = true); + + await Future.delayed(Duration.zero); + expect(initCalls, 1); + expect(secondCompleted, isFalse); + + completer.complete(); + await Future.wait([first, second]); + expect(initCalls, 1); + expect(secondCompleted, isTrue); + }); + + test('wireStorage retries after a failed initialization attempt', () async { + final tempDir = await Directory.systemTemp.createTemp( + 'chanora-app-bootstrap-test', + ); + addTearDown(() => tempDir.delete(recursive: true)); + + var initCalls = 0; + debugResetStorageBootstrap( + storageDirectoryProvider: () async => tempDir, + storageInitializer: (dir) async { + initCalls += 1; + if (initCalls == 1) { + throw const FileSystemException('boom'); + } + }, + ); + + await wireStorage(); + await wireStorage(); + + expect(initCalls, 2); + }); }