Skip to content
Closed
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
6 changes: 6 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,9 @@ 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.
## 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).
6 changes: 5 additions & 1 deletion backend/app/spec/dbml_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
10 changes: 9 additions & 1 deletion frontend/src/erd/businessGroups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>();
for (const group of existingGroups) {
existingIds.add(group.id);
}

if (!existingIds.has(baseId)) return baseId;
let suffix = 2;
while (existingIds.has(`${baseId}_${suffix}`)) {
Expand Down
Loading