From 0862f68b1f7bf70046d6e21927ebf556255e61fa Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:44:54 +0000 Subject: [PATCH 1/2] refactor(frontend): optimize Set initialization in uniqueBusinessGroupId --- .jules/bolt.md | 3 +++ frontend/src/erd/businessGroups.ts | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index f1a8c146..b8e7d976 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -77,3 +77,6 @@ Optimized metric route processing to O(N) by creating a mapping of routes direct ## 2024-07-13 - [Optimize Export Dictionary FK lookups] **Learning:** Found O(N * C * E) performance bottleneck in ERD export dictionaries due to repeated array searching with `edges.some()` inside a nested loop over nodes and columns. **Action:** Replace repeated linear array scans for edges by precomputing O(1) Set lookups of foreign key column handles per node before looping. +## 2024-07-25 - Avoid new Set(array.map(...)) for Set Initializations +**Learning:** Using `new Set(array.map(item => item.id))` creates a completely unnecessary intermediate O(N) array of IDs. This forces the garbage collector to immediately clean up the mapped array once the Set is constructed, leading to memory spikes and GC pauses. +**Action:** Replace `new Set(array.map(...))` with `const set = new Set();` and an iterative `for (const item of array) { set.add(item.id); }` loop to reduce intermediate garbage allocations to zero. diff --git a/frontend/src/erd/businessGroups.ts b/frontend/src/erd/businessGroups.ts index f60edd24..52054516 100644 --- a/frontend/src/erd/businessGroups.ts +++ b/frontend/src/erd/businessGroups.ts @@ -41,7 +41,15 @@ export function uniqueBusinessGroupId( existingGroups: BusinessGroup[], ): string { const baseId = buildBusinessGroupId(name); - const existingIds = new Set(existingGroups.map((group) => group.id)); + + // ⚡ Bolt: Use an iterative `for...of` loop to build the Set directly. + // This avoids the O(N) intermediate array allocation and garbage collection overhead + // that occurs when using `new Set(existingGroups.map(...))`. + const existingIds = new Set(); + for (const group of existingGroups) { + existingIds.add(group.id); + } + if (!existingIds.has(baseId)) return baseId; let suffix = 2; while (existingIds.has(`${baseId}_${suffix}`)) { From db9bf44113291286b969e15c88870acd1959b0b3 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 16 Jul 2026 14:06:57 +0000 Subject: [PATCH 2/2] refactor(backend,frontend): optimize iterations to resolve algorithmic performance issues - Removed O(N^2) complexity in `parse_dbml` where `sum(1 for c in columns if c["relation_oid"] == oid)` generated a linear scan over an accumulating list during every parsed column. Replaced with an O(1) tracking dictionary. - Avoided intermediate array overhead inside `uniqueBusinessGroupId` by switching `new Set(existingGroups.map(...))` to an explicit `for...of` loop. --- .jules/bolt.md | 3 +++ backend/app/spec/dbml_import.py | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index b8e7d976..7441e5de 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -80,3 +80,6 @@ Optimized metric route processing to O(N) by creating a mapping of routes direct ## 2024-07-25 - Avoid new Set(array.map(...)) for Set Initializations **Learning:** Using `new Set(array.map(item => item.id))` creates a completely unnecessary intermediate O(N) array of IDs. This forces the garbage collector to immediately clean up the mapped array once the Set is constructed, leading to memory spikes and GC pauses. **Action:** Replace `new Set(array.map(...))` with `const set = new Set();` and an iterative `for (const item of array) { set.add(item.id); }` loop to reduce intermediate garbage allocations to zero. +## 2024-07-25 - Avoid O(N^2) Loop Scans with O(1) Dictionaries +**Learning:** In loops processing hierarchical structures (like columns of a table), using list comprehensions or `sum()` over the entire collection to count items belonging to a parent (e.g. `sum(1 for c in columns if c["parent_id"] == oid)`) creates an O(N^2) bottleneck. +**Action:** Replace `sum(...)` over accumulated collections with an O(1) dictionary counter (e.g. `col_pos = count_by_oid.get(oid, 0) + 1; count_by_oid[oid] = col_pos`) initialized outside the loop to reduce complexity to O(N). diff --git a/backend/app/spec/dbml_import.py b/backend/app/spec/dbml_import.py index b93454a9..57a16cab 100644 --- a/backend/app/spec/dbml_import.py +++ b/backend/app/spec/dbml_import.py @@ -135,6 +135,7 @@ def parse_dbml(text: str) -> dict[str, Any]: current: tuple[str, str] | None = None in_ignored_block = 0 in_indexes = False + column_position_by_oid: dict[int, int] = {} for raw_line in text.splitlines(): # ReDoS guard: no legitimate DBML line approaches this length; capping @@ -207,11 +208,14 @@ def parse_dbml(text: str) -> dict[str, Any]: settings = (cm.group("settings") or "").lower() oid = oid_by_table[current] is_pk = bool(re.search(r"\bpk\b|primary\s+key", settings)) + col_pos = column_position_by_oid.get(oid, 0) + 1 + column_position_by_oid[oid] = col_pos + columns.append( { "relation_oid": oid, "column_name": col_name, - "column_position": sum(1 for c in columns if c["relation_oid"] == oid) + 1, + "column_position": col_pos, "data_type": cm.group("type"), "is_not_null": is_pk or "not null" in settings, "has_default": "default:" in settings,