From 7c2e4bb9f10059375a360e922200d5121bae780b Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2026 16:41:17 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Remove=20redundant=20query?= =?UTF-8?q?=20in=20=5Fbuild=5Fissues=5Fbatch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed a separate `COUNT(*)` query for calculating open blockers in `_build_issues_batch`. We instead check `len(blocked_by_id)` which calculates the exact same thing using data from a previous step. Co-authored-by: tachyon-beep <544926+tachyon-beep@users.noreply.github.com> --- .jules/bolt.md | 5 +++++ src/filigree/db_issues.py | 17 +++-------------- 2 files changed, 8 insertions(+), 14 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..e28a3287 --- /dev/null +++ b/.jules/bolt.md @@ -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. diff --git a/src/filigree/db_issues.py b/src/filigree/db_issues.py index f59be287..66b1fe0d 100644 --- a/src/filigree/db_issues.py +++ b/src/filigree/db_issues.py @@ -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: @@ -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, []),