From eae5776dab4b82406969fb37150459ba02cddc55 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 5 Jul 2026 21:15:51 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20ERD=20=EB=82=B4=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EA=B8=B0=20=EC=84=B1=EB=8A=A5=20=EC=B5=9C=EC=A0=81?= =?UTF-8?q?=ED=99=94=EB=A5=BC=20=EC=9C=84=ED=95=9C=20Map=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=A3=A8=ED=94=84=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/bolt.md | 4 ++++ frontend/src/erd/export.ts | 28 +++++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index b45f9caa..d4d55185 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -60,3 +60,7 @@ Optimized metric route processing to O(N) by creating a mapping of routes direct ## 2026-06-25 - Avoid Map allocations in frontend ERD loops and mutate asyncpg records in-place **Learning:** The frontend `snapshotToGraph` iterates over thousands of columns to generate the graph, so repeated lookups and redundant collection assignments increase GC pressure. Backend snapshot column dictionaries are freshly instantiated for the payload, so `add_column_examples` can safely fill missing fields in place. **Action:** Reuse existing collections while aggregating relational data, create `Map`/`Set` entries only on first use, and check for missing example fields before calling expensive inference helpers. + +## 2025-01-20 - Avoid array map allocations when initializing large Maps +**Learning:** Initializing Maps with `new Map(array.map(...))` creates large intermediate arrays in memory (`[key, value][]`), resulting in unnecessary memory allocation, high GC pressure, and significantly worse performance during iterative processes (like diagram exports). +**Action:** Prefer explicitly initializing a `new Map()` and populating it within a standard `for...of` loop over `new Map(array.map(...))` for high-frequency or large-scale data processing in the frontend. \ No newline at end of file diff --git a/frontend/src/erd/export.ts b/frontend/src/erd/export.ts index b172947e..793fcb82 100644 --- a/frontend/src/erd/export.ts +++ b/frontend/src/erd/export.ts @@ -92,7 +92,11 @@ export function exportDDL(nodes: Node[], edges: Edge[]): string { let ddl = '-- Generated DDL\n\n'; // Bolt: Use map for O(1) node lookup instead of O(N) array find - const nodesById = new Map(nodes.map(n => [n.id, n])); + // Bolt: Avoid array allocations via .map() when building Maps + const nodesById = new Map>(); + for (const n of nodes) { + nodesById.set(n.id, n); + } // Export tables for (const node of nodes) { @@ -281,19 +285,25 @@ export function exportDiagramSvg( snapshot?: SnapshotJson | null, ): string { // Bolt: Use map for O(1) node lookup instead of O(N) array find - const nodesById = new Map(nodes.map(n => [n.id, n])); + // Bolt: Avoid array allocations via .map() when building Maps + const nodesById = new Map>(); + for (const n of nodes) { + nodesById.set(n.id, n); + } const width = 280; const headerHeight = 34; const rowHeight = 22; const padding = 40; const indexes = indexesByRelation(snapshot); - const heights = new Map( - nodes.map((node) => { - // ponytail: cap rendered index rows; add full index export when the canvas carries index nodes. - const indexRows = Math.min((indexes.get(node.id)?.length || 0) + (node.data.indexes?.length || 0), 8); - return [node.id, headerHeight + rowHeight * ((node.data.columns?.length || 0) + indexRows + (indexRows ? 1 : 0))]; - }), - ); + + // Bolt: Avoid Map allocations with .map() inside loops for large graph exports + const heights = new Map(); + for (const node of nodes) { + // ponytail: cap rendered index rows; add full index export when the canvas carries index nodes. + const indexRows = Math.min((indexes.get(node.id)?.length || 0) + (node.data.indexes?.length || 0), 8); + heights.set(node.id, headerHeight + rowHeight * ((node.data.columns?.length || 0) + indexRows + (indexRows ? 1 : 0))); + } + let minX = 0; let minY = 0; let maxX = width;