From 39d5d407f41a004335fea8c8e40cf5d2be4601a1 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2026 13:02:30 +0000 Subject: [PATCH] perf(db): replace correlated subqueries with early aggregation for unpaginated queries When querying entire unpaginated tables in SQLite, using correlated scalar subqueries `(SELECT COUNT(*) FROM...)` inside the `SELECT` clause results in an N+1 execution pattern. This commit optimizes two critical full-table scans (`consolidate_duplicate_books` and `find_book_by_title_author_conn`) by refactoring them to use "early aggregation" derived tables via `LEFT JOIN`s, dramatically improving query performance from O(N log M) to O(N + M). Co-authored-by: jspann21 <179991454+jspann21@users.noreply.github.com> --- .jules/bolt.md | 3 +++ src-tauri/src/library/db.rs | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..2681816 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2025-01-20 - Avoid repository pollution with benchmark scripts +**Learning:** Checking in temporary test scripts and binary dummy databases (like `benchmark.py` and `test.db`) during development is considered a blocking issue for merges as it pollutes the repository. +**Action:** Always place temporary testing artifacts in `/tmp/` and clean them up before requesting code review or submitting changes. diff --git a/src-tauri/src/library/db.rs b/src-tauri/src/library/db.rs index 6040018..38f645c 100644 --- a/src-tauri/src/library/db.rs +++ b/src-tauri/src/library/db.rs @@ -946,6 +946,9 @@ impl Repository { let tx = conn.transaction()?; let mut stmt = tx.prepare( + // Performance optimization: Avoid correlated scalar subqueries in the SELECT clause (EXISTS, COUNT) + // during unpaginated full-table scans. Using Early Aggregation via derived tables prevents + // an N+1 execution bottleneck. "SELECT b.id, b.title, @@ -953,9 +956,11 @@ impl Repository { b.isbn10, b.isbn13, b.updated_at, - EXISTS(SELECT 1 FROM manual_overrides mo WHERE mo.book_id = b.id) AS has_manual_overrides, - (SELECT COUNT(*) FROM book_files bf WHERE bf.book_id = b.id) AS file_count - FROM books b", + mo.has_manual IS NOT NULL AS has_manual_overrides, + COALESCE(bf.file_count, 0) AS file_count + FROM books b + LEFT JOIN (SELECT book_id, 1 AS has_manual FROM manual_overrides GROUP BY book_id) mo ON mo.book_id = b.id + LEFT JOIN (SELECT book_id, COUNT(*) AS file_count FROM book_files GROUP BY book_id) bf ON bf.book_id = b.id", )?; let mut candidates = Vec::new(); @@ -1339,9 +1344,12 @@ impl Repository { (SELECT COUNT(*) FROM book_files bf WHERE bf.book_id = lb.id) AS file_count FROM limited_books lb" } else { + // Performance optimization: Avoid correlated scalar subqueries (COUNT) in the SELECT + // clause during large unpaginated table scans. Early Aggregation prevents an N+1 bottleneck. "SELECT b.id, b.title, b.authors_json, - (SELECT COUNT(*) FROM book_files bf WHERE bf.book_id = b.id) AS file_count - FROM books b" + COALESCE(bf.file_count, 0) AS file_count + FROM books b + LEFT JOIN (SELECT book_id, COUNT(*) AS file_count FROM book_files GROUP BY book_id) bf ON bf.book_id = b.id" }; let mut stmt = conn.prepare(query)?;