Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## 2024-06-06 - N+1 Issue Query Optimization

**Learning:** `_build_issues_batch` in `db_issues.py` performs 6 separate batched queries to resolve an issue's labels, blocks, blocked_by, children, and open_blockers_by_id, but the open_blockers_by_id is just `len(blocked_by)`. We can remove the 6th batched query entirely to reduce round trips and DB load.

**Action:** Remove the `open_blockers_by_id` query from `_build_issues_batch` and replace it with `len(blocked_by_id.get(iid, []))` when constructing the Issue object.
17 changes: 3 additions & 14 deletions src/filigree/db_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,19 +686,6 @@ def _build_issues_batch(self, issue_ids: list[str]) -> list[Issue]:
for r in self.conn.execute(f"SELECT id, parent_id FROM issues WHERE parent_id IN ({placeholders})", chunk).fetchall():
children_by_id[r["parent_id"]].append(r["id"])

# 6. Batch compute open blocker counts β€” same blocker semantics as step 4.
open_blockers_by_id: dict[str, int] = dict.fromkeys(issue_ids, 0)
for chunk in self._chunk_issue_ids_for_sqlite(issue_ids, extra_params=len(blocker_done_params)):
placeholders = ",".join("?" * len(chunk))
for r in self.conn.execute(
f"SELECT d.issue_id, COUNT(*) as cnt FROM dependencies d "
f"JOIN issues blocker ON d.depends_on_id = blocker.id "
f"WHERE d.issue_id IN ({placeholders}) AND NOT ({blocker_done_sql}) "
f"GROUP BY d.issue_id",
[*chunk, *blocker_done_params],
).fetchall():
open_blockers_by_id[r["issue_id"]] = r["cnt"]

# Build Issue objects preserving input order
result: list[Issue] = []
for iid in issue_ids:
Expand Down Expand Up @@ -732,7 +719,9 @@ def _build_issues_batch(self, issue_ids: list[str]) -> list[Issue]:
# state name shared across types in different categories
# is classified correctly.
self._resolve_status_category(row["type"], row["status"]) == "open"
and open_blockers_by_id.get(iid, 0) == 0
# ⚑ Bolt Optimization: Removed redundant DB COUNT(*) query for open blockers.
# blocked_by_id already contains the exact same filtered list of open blockers.
and len(blocked_by_id.get(iid, [])) == 0
and not (row["assignee"] or "")
),
children=children_by_id.get(iid, []),
Expand Down