Skip to content

Commit 8112e22

Browse files
committed
perf: reduce workflow graph sync overhead
1 parent d0f9f49 commit 8112e22

1 file changed

Lines changed: 65 additions & 52 deletions

File tree

frontend/src/pages/WorkflowView.vue

Lines changed: 65 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -602,29 +602,30 @@ const deleteNodeById = async (nodeId) => {
602602
if (!nodeId) {
603603
return
604604
}
605-
const snapshot = snapshotYamlContent()
606-
if (!snapshot?.graph) {
605+
const source = yamlContent.value
606+
if (!source?.graph) {
607607
return
608608
}
609-
const nodesArr = Array.isArray(snapshot.graph.nodes) ? snapshot.graph.nodes : []
610-
const edgesArr = Array.isArray(snapshot.graph.edges) ? snapshot.graph.edges : []
609+
const sourceGraph = source.graph
610+
const nodesArr = Array.isArray(sourceGraph.nodes) ? sourceGraph.nodes : []
611+
const edgesArr = Array.isArray(sourceGraph.edges) ? sourceGraph.edges : []
611612

612613
// Remove the node and its related edges
613614
const nextNodes = nodesArr.filter(node => node?.id !== nodeId)
614615
const nextEdges = edgesArr.filter(edge => edge?.from !== nodeId && edge?.to !== nodeId)
615616

616617
// Remove node ID from graph.start/end
617-
const nextStart = Array.isArray(snapshot.graph.start)
618-
? snapshot.graph.start.filter(id => id !== nodeId)
619-
: snapshot.graph.start
620-
const nextEnd = Array.isArray(snapshot.graph.end)
621-
? snapshot.graph.end.filter(id => id !== nodeId)
622-
: snapshot.graph.end
618+
const nextStart = Array.isArray(sourceGraph.start)
619+
? sourceGraph.start.filter(id => id !== nodeId)
620+
: sourceGraph.start
621+
const nextEnd = Array.isArray(sourceGraph.end)
622+
? sourceGraph.end.filter(id => id !== nodeId)
623+
: sourceGraph.end
623624

624625
const nextSnapshot = {
625-
...snapshot,
626+
...source,
626627
graph: {
627-
...snapshot.graph,
628+
...sourceGraph,
628629
nodes: nextNodes,
629630
edges: nextEdges,
630631
start: nextStart,
@@ -647,13 +648,14 @@ const deleteEdgeByEndpoints = async (fromId, toId) => {
647648
if (!fromId || !toId) {
648649
return
649650
}
650-
const snapshot = snapshotYamlContent()
651-
if (!snapshot?.graph || !Array.isArray(snapshot.graph.edges)) {
651+
const source = yamlContent.value
652+
if (!source?.graph || !Array.isArray(source.graph.edges)) {
652653
return
653654
}
655+
const sourceGraph = source.graph
654656

655657
let removed = false
656-
const nextEdges = snapshot.graph.edges.filter(edge => {
658+
const nextEdges = sourceGraph.edges.filter(edge => {
657659
if (!removed && edge?.from === fromId && edge?.to === toId) {
658660
removed = true
659661
return false
@@ -662,11 +664,11 @@ const deleteEdgeByEndpoints = async (fromId, toId) => {
662664
})
663665

664666
// Delete from .start if edge is from Start Node
665-
let nextStart = snapshot.graph.start
667+
let nextStart = sourceGraph.start
666668
if (fromId === START_NODE_ID) {
667-
nextStart = Array.isArray(snapshot.graph.start)
668-
? snapshot.graph.start.filter(id => id !== toId)
669-
: snapshot.graph.start
669+
nextStart = Array.isArray(sourceGraph.start)
670+
? sourceGraph.start.filter(id => id !== toId)
671+
: sourceGraph.start
670672

671673
// Empty start node array is not allowed
672674
const startArray = Array.isArray(nextStart) ? nextStart : []
@@ -677,9 +679,9 @@ const deleteEdgeByEndpoints = async (fromId, toId) => {
677679
}
678680

679681
const nextSnapshot = {
680-
...snapshot,
682+
...source,
681683
graph: {
682-
...snapshot.graph,
684+
...sourceGraph,
683685
edges: nextEdges,
684686
start: nextStart
685687
}
@@ -888,6 +890,11 @@ const updateNodesAndEdgesFromYaml = (preserveExistingLayout = false) => {
888890

889891
const currentNodes = nodes.value || []
890892
const currentEdges = edges.value || []
893+
const defaultCenterPosition = getCentralPosition()
894+
const getDefaultCenterPosition = () => ({
895+
x: defaultCenterPosition.x,
896+
y: defaultCenterPosition.y
897+
})
891898

892899
const existingNodeById = preserveExistingLayout
893900
? new Map(currentNodes.map(node => [node.id, node]))
@@ -947,8 +954,9 @@ const updateNodesAndEdgesFromYaml = (preserveExistingLayout = false) => {
947954
}
948955
}
949956

950-
while (q.length) {
951-
const id = q.shift()
957+
let queueIndex = 0
958+
while (queueIndex < q.length) {
959+
const id = q[queueIndex++]
952960
const baseLevel = levelById.get(id) || 0
953961
const neighbors = adj.get(id) || new Set()
954962
for (const nb of neighbors) {
@@ -1033,7 +1041,7 @@ const updateNodesAndEdgesFromYaml = (preserveExistingLayout = false) => {
10331041
}
10341042
}
10351043

1036-
const pos = positions.get(id) || getCentralPosition()
1044+
const pos = positions.get(id) || getDefaultCenterPosition()
10371045
return {
10381046
id,
10391047
type: 'workflow-node',
@@ -1051,7 +1059,7 @@ const updateNodesAndEdgesFromYaml = (preserveExistingLayout = false) => {
10511059
id: yamlNode.id,
10521060
type: 'workflow-node',
10531061
label: yamlNode.id,
1054-
position: getCentralPosition(),
1062+
position: getDefaultCenterPosition(),
10551063
data: yamlNode
10561064
}))
10571065
nodes.value = nextNodes
@@ -1097,13 +1105,13 @@ const updateNodesAndEdgesFromYaml = (preserveExistingLayout = false) => {
10971105
// Place start node to the left of the leftmost column
10981106
const yamlNodesInGraph = (nodes.value || []).filter(n => n && n.id !== START_NODE_ID)
10991107
if (yamlNodesInGraph.length) {
1100-
const xs = yamlNodesInGraph.map(n => (n?.position && typeof n.position.x === 'number') ? n.position.x : getCentralPosition().x)
1108+
const xs = yamlNodesInGraph.map(n => (n?.position && typeof n.position.x === 'number') ? n.position.x : defaultCenterPosition.x)
11011109
const minX = Math.min(...xs)
11021110
// Find nodes in that left column
11031111
const tol = 1
11041112
const leftColumn = yamlNodesInGraph.filter(n => Math.abs((n?.position?.x || 0) - minX) <= tol)
1105-
const ys = leftColumn.map(n => (n?.position && typeof n.position.y === 'number') ? n.position.y : getCentralPosition().y)
1106-
const avgY = ys.length ? ys.reduce((a, b) => a + b, 0) / ys.length : getCentralPosition().y
1113+
const ys = leftColumn.map(n => (n?.position && typeof n.position.y === 'number') ? n.position.y : defaultCenterPosition.y)
1114+
const avgY = ys.length ? ys.reduce((a, b) => a + b, 0) / ys.length : defaultCenterPosition.y
11071115
const startXOffset = -100
11081116
const startYOffset = 80
11091117
startNode = {
@@ -1118,7 +1126,7 @@ const updateNodesAndEdgesFromYaml = (preserveExistingLayout = false) => {
11181126
id: START_NODE_ID,
11191127
type: 'start-node',
11201128
label: 'Start',
1121-
position: getCentralPosition(),
1129+
position: getDefaultCenterPosition(),
11221130
data: { id: START_NODE_ID, label: 'Start' }
11231131
}
11241132
}
@@ -1128,7 +1136,7 @@ const updateNodesAndEdgesFromYaml = (preserveExistingLayout = false) => {
11281136
id: START_NODE_ID,
11291137
type: 'start-node',
11301138
label: 'Start',
1131-
position: getCentralPosition(),
1139+
position: getDefaultCenterPosition(),
11321140
data: { id: START_NODE_ID, label: 'Start' }
11331141
}
11341142
}
@@ -1167,13 +1175,14 @@ const updateNodesAndEdgesFromYaml = (preserveExistingLayout = false) => {
11671175
}).filter(Boolean)
11681176

11691177
// Combine YAML edges with visual start edges (preserve any existing non-yaml edges)
1178+
const nextYamlEdgeIdSet = new Set(nextYamlEdges.map(edge => edge.id))
11701179
edges.value = [
11711180
// keep any existing edges that are not YAML edges (e.g., visual-only) when preserving layout
11721181
// but always exclude previous Start edges so they are replaced by the newly computed ones
11731182
...(preserveExistingLayout ? currentEdges.filter(e => {
11741183
const k = `${e.source}-${e.target}`
11751184
// drop if it's a YAML-defined edge or a previous Start edge
1176-
const isYamlEdge = nextYamlEdges.some(ne => ne.id === k)
1185+
const isYamlEdge = nextYamlEdgeIdSet.has(k)
11771186
const isStartEdge = e.source === START_NODE_ID
11781187
// Also drop if it looks like a YAML edge (has data.from/to) but isn't in nextYamlEdges (stale)
11791188
const isStaleYamlEdge = e.data?.from && e.data?.to
@@ -1292,8 +1301,6 @@ const updateVueFlowNodeId = (oldId, newId) => {
12921301
}
12931302

12941303
// FormGenerator integration
1295-
const snapshotYamlContent = () => cloneDeep(yamlContent.value ?? null)
1296-
12971304
// Build YAML without specific node (shallow clone path to avoid full deep-clone on editor open)
12981305
const buildYamlWithoutNode = (nodeId) => {
12991306
const source = yamlContent.value
@@ -1376,17 +1383,18 @@ const buildYamlWithoutGraph = () => {
13761383
const autoAddStartEdge = async (nextNodeId) => {
13771384
const workflowNodes = (yamlContent.value?.graph?.nodes || []).filter(node => node?.id !== START_NODE_ID)
13781385
if (workflowNodes.length === 1 && workflowNodes[0]?.id === nextNodeId) {
1379-
const snapshot = snapshotYamlContent()
1380-
if (!snapshot?.graph) {
1381-
snapshot.graph = {}
1382-
}
1383-
if (!Array.isArray(snapshot.graph.start)) {
1384-
snapshot.graph.start = []
1385-
}
1386-
if (!snapshot.graph.start.includes(nextNodeId)) {
1387-
// Add node
1388-
snapshot.graph.start.push(nextNodeId)
1389-
const ok = await persistYamlSnapshot(snapshot)
1386+
const source = yamlContent.value
1387+
const sourceGraph = source?.graph && typeof source.graph === 'object' ? source.graph : {}
1388+
const currentStart = Array.isArray(sourceGraph.start) ? sourceGraph.start : []
1389+
if (!currentStart.includes(nextNodeId)) {
1390+
const nextSnapshot = {
1391+
...source,
1392+
graph: {
1393+
...sourceGraph,
1394+
start: [...currentStart, nextNodeId]
1395+
}
1396+
}
1397+
const ok = await persistYamlSnapshot(nextSnapshot)
13901398
if (ok) {
13911399
await loadYamlFile()
13921400
syncVueNodesAndEdgesData()
@@ -1644,25 +1652,30 @@ const onConnect = async (connection) => {
16441652
// Special handling for StartNode connections
16451653
if (connection.source === START_NODE_ID) {
16461654
// Add target node to graph.start array instead of opening FormGenerator
1647-
const snapshot = snapshotYamlContent()
1648-
if (!snapshot?.graph) {
1655+
const source = yamlContent.value
1656+
if (!source?.graph) {
16491657
setTimeout(() => {
16501658
isCreatingConnection.value = false
16511659
}, 10)
16521660
return
16531661
}
1662+
const sourceGraph = source.graph
16541663

16551664
// Ensure graph.start exists as an array
1656-
if (!Array.isArray(snapshot.graph.start)) {
1657-
snapshot.graph.start = []
1658-
}
1665+
const currentStart = Array.isArray(sourceGraph.start) ? sourceGraph.start : []
16591666

16601667
// Add target node to start array if not already present
1661-
if (!snapshot.graph.start.includes(connection.target)) {
1662-
snapshot.graph.start.push(connection.target)
1668+
if (!currentStart.includes(connection.target)) {
1669+
const nextSnapshot = {
1670+
...source,
1671+
graph: {
1672+
...sourceGraph,
1673+
start: [...currentStart, connection.target]
1674+
}
1675+
}
16631676

16641677
// Persist the updated YAML
1665-
const ok = await persistYamlSnapshot(snapshot)
1678+
const ok = await persistYamlSnapshot(nextSnapshot)
16661679
if (ok) {
16671680
await loadYamlFile()
16681681
syncVueNodesAndEdgesData()

0 commit comments

Comments
 (0)