Skip to content

perf: eliminate N+1 queries across recall, graph BFS, rooms, and edge insertion - #987

Merged
ajianaz merged 4 commits into
developfrom
perf/p10-n+1-batch-queries
Aug 10, 2026
Merged

perf: eliminate N+1 queries across recall, graph BFS, rooms, and edge insertion#987
ajianaz merged 4 commits into
developfrom
perf/p10-n+1-batch-queries

Conversation

@ajianaz

@ajianaz ajianaz commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

What

Eliminates all N+1 SQL query patterns identified by subagent analysis across 6 hotspots in uteke-core.

Why

Every recall(), room_recall(), and graph BFS traversal was executing 1 SQL query per candidate — a classic N+1. With k = limit * 3 * 9 = 27×limit candidates per recall attempt, this meant up to 27 individual SELECT queries per search. Batch-fetching eliminates this to a single query.

Changes

New batch methods (Store layer):

  • Store::get_by_ids(&[&str]) — single SELECT ... WHERE id IN (...) using params_from_iter
  • Store::touch_access_batch(&[&str]) — single UPDATE with CASE WHEN for batch access tracking

N+1 consumers fixed (7 locations):

File Before After
operations.rs recall() get_by_id() per candidate (k up to 27×limit) get_by_ids() + HashMap lookup
operations.rs 4× touch_access N individual UPDATEs Single touch_access_batch()
rooms.rs room_recall() get_by_id() per missing ID get_by_ids() batch
graph.rs BFS get_by_id() per depth level (frontier + targets) Batch fetch frontier, batch fetch targets

Edge insertion:

  • edges.rs add_memory_edges_batch(): prepare() once outside loop → reuse prepared statement for all inserts + backlinks

Testing

  • cargo test --workspace: 476 pass, 0 fail, 25 ignored
  • cargo clippy --workspace --all-targets: 0 warnings
  • Cora pre-commit: passed (1 MAJOR note — false positive: BFS checks existence via HashMap lookup, not direct DB probe)

…d edge insertion

Store layer (new batch methods):
- crud.rs: get_by_ids() — single SELECT ... WHERE id IN (...) using rusqlite params_from_iter
- aging.rs: touch_access_batch() — single UPDATE with CASE WHEN for batch access tracking

Consumer fixes (6 N+1 hotspots eliminated):
- operations.rs recall(): get_by_id per candidate → batch get_by_ids + HashMap lookup
- operations.rs 4× touch_access loops → single touch_access_batch call
- rooms.rs room_recall(): get_by_id per missing ID → batch get_by_ids
- graph.rs BFS depth traversal: 2× get_by_id per level → batch fetch frontier + batch fetch targets

Edge insertion optimization:
- edges.rs add_memory_edges_batch(): prepare() once outside loop, reuse stmt for all inserts

Test: 476 pass, 0 fail. Clippy: 0 warnings.
Comment thread crates/uteke-core/src/graph.rs Fixed
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

🔍 Cora AI Code Review

No issues found. Code looks good!


Review powered by cora-code · BYOK · MIT

… same level

Cora review finding: when two source nodes in the same BFS frontier level
both reference the same target, the target was added to rel_chains twice.
After batch fetch, both entries would insert the target into results and
next_frontier, causing duplicate entries and wasted traversal.

Fix: check visited set before processing each rel_chain entry.
Comment thread crates/uteke-core/src/graph.rs Outdated
visited.insert(target_id.clone());
let decayed_score = (results[source_id].1 * 0.8).max(0.1);
results
.insert(target_id.clone(), ((*target_memory).clone(), decayed_score));
Comment thread crates/uteke-core/src/memory/crud.rs Outdated
return Ok(Vec::new());
}
let placeholders = ids.iter().map(|_| "?").collect::<Vec<_>>().join(",");
let sql = format!(
Cora review finding: SQLite has SQLITE_MAX_VARIABLE_NUMBER=999 default.
If ids contains >999 entries, the WHERE id IN (?,...) query would fail.

Fix: chunk IDs into batches of 900, execute separate query per chunk,
merge results.
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

// Skip if already visited — multiple source nodes in the same
// frontier level can reference the same target (dedup).
if visited.contains(target_id.as_str()) {
continue;
…e target

Cora review: first-source-wins was non-deterministic and could lose a
better score from a later source. Now tracks max score across all
sources in the same frontier level before committing to results.
@ajianaz
ajianaz merged commit 1f08ccc into develop Aug 10, 2026
14 checks passed
@ajianaz
ajianaz deleted the perf/p10-n+1-batch-queries branch August 10, 2026 04:44
@ajianaz ajianaz mentioned this pull request Aug 10, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants