From c974bde11cbd95b3064585c2497da23aaeec1c3c Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 2 Sep 2025 17:02:51 +0530 Subject: [PATCH 1/4] Don't go to setup on error --- mobile/apps/photos/lib/ui/tools/similar_images_page.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart index bdd7b8802f..5dd62fdd85 100644 --- a/mobile/apps/photos/lib/ui/tools/similar_images_page.dart +++ b/mobile/apps/photos/lib/ui/tools/similar_images_page.dart @@ -672,10 +672,7 @@ class _SimilarImagesPageState extends State await showGenericErrorDialog(context: context, error: e); } if (_isDisposed) return; - setState(() { - _pageState = SimilarImagesPageState.setup; - }); - return; + Navigator.of(context).pop(); } } From 972be1f41ebcc20042613dc17a2d10e9cfefcfbc Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 3 Sep 2025 10:27:30 +0530 Subject: [PATCH 2/4] Use load for usearch index --- mobile/apps/photos/rust/src/api/usearch_api.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mobile/apps/photos/rust/src/api/usearch_api.rs b/mobile/apps/photos/rust/src/api/usearch_api.rs index 395f93b04d..b2c77f0fd3 100644 --- a/mobile/apps/photos/rust/src/api/usearch_api.rs +++ b/mobile/apps/photos/rust/src/api/usearch_api.rs @@ -32,8 +32,11 @@ impl VectorDB { if file_exists { println!("Loading index from disk."); - // Use view to not load the index into memory. https://docs.rs/usearch/latest/usearch/struct.Index.html#method.view - db.index.view(file_path).expect("Failed to load index"); + // Must use load() instead of view() because: + // - view() creates a read-only memory-mapped view (immutable) + // - load() loads the index into RAM for read/write operations (mutable) + // Using view() causes "Can't add to an immutable index" error + db.index.load(file_path).expect("Failed to load index"); } else { println!("Creating new index."); db.save_index(); From 4098c1a0722d74255f0216506e1bedc8886d32fb Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 3 Sep 2025 10:36:03 +0530 Subject: [PATCH 3/4] Delete index file on load error --- .../apps/photos/lib/db/ml/clip_vector_db.dart | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart index 29d24b324e..0ac9b4929a 100644 --- a/mobile/apps/photos/lib/db/ml/clip_vector_db.dart +++ b/mobile/apps/photos/lib/db/ml/clip_vector_db.dart @@ -39,10 +39,26 @@ class ClipVectorDB { final documentsDirectory = await getApplicationDocumentsDirectory(); final String dbPath = join(documentsDirectory.path, _databaseName); _logger.info("Opening vectorDB access: DB path " + dbPath); - final vectorDB = VectorDb( - filePath: dbPath, - dimensions: _embeddingDimension, - ); + late VectorDb vectorDB; + try { + vectorDB = VectorDb( + filePath: dbPath, + dimensions: _embeddingDimension, + ); + } catch (e, s) { + _logger.severe("Could not open VectorDB at path $dbPath", e, s); + _logger.severe("Deleting the index file and trying again"); + await deleteIndexFile(); + try { + vectorDB = VectorDb( + filePath: dbPath, + dimensions: _embeddingDimension, + ); + } catch (e, s) { + _logger.severe("Still can't open VectorDB at path $dbPath", e, s); + rethrow; + } + } final stats = await getIndexStats(vectorDB); _logger.info("VectorDB connection opened with stats: ${stats.toString()}"); From 2b76b71db835015a8ea266e1fc69c9ddbb6142c0 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 3 Sep 2025 11:15:07 +0530 Subject: [PATCH 4/4] atomic save of index file --- .../apps/photos/rust/src/api/usearch_api.rs | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/mobile/apps/photos/rust/src/api/usearch_api.rs b/mobile/apps/photos/rust/src/api/usearch_api.rs index b2c77f0fd3..01823cb0f0 100644 --- a/mobile/apps/photos/rust/src/api/usearch_api.rs +++ b/mobile/apps/photos/rust/src/api/usearch_api.rs @@ -49,9 +49,37 @@ impl VectorDB { if let Some(parent) = self.path.parent() { std::fs::create_dir_all(parent).expect("Failed to create directory"); } - self.index - .save(self.path.to_str().expect("Invalid path")) - .expect("Failed to save index"); + + // Use atomic write: save to temp file first, then rename + let temp_path = self.path.with_extension("tmp"); + let temp_path_str = temp_path.to_str().expect("Invalid temp path"); + + // Save to temporary file + match self.index.save(temp_path_str) { + Ok(_) => { + // Atomic rename - guaranteed atomic on iOS/Android + // This will atomically replace the existing file + // The rename ensures we never have a partially written file, + // even if the app is suspended or crashes + match std::fs::rename(&temp_path, &self.path) { + Ok(_) => { + println!("Successfully saved index atomically"); + } + Err(e) => { + println!("Failed to rename temp index file: {:?}", e); + // Try to clean up temp file + let _ = std::fs::remove_file(&temp_path); + panic!("Failed to atomically save index: {:?}", e); + } + } + } + Err(e) => { + println!("Failed to save index to temp file: {:?}", e); + // Try to clean up temp file if it exists + let _ = std::fs::remove_file(&temp_path); + panic!("Failed to save index: {:?}", e); + } + } } fn ensure_capacity(&self, margin: usize) {