chore: try to track why nodes missing

This commit is contained in:
hjlarry 2026-03-27 17:09:18 +08:00
parent ee093a21c8
commit 4e397a97a5
4 changed files with 20 additions and 13 deletions

View File

@ -81,6 +81,7 @@ type SetNodesAnomalyReason = 'node_count_decrease' | 'start_removed'
type SetNodesAnomalyLogEntry = {
timestamp: number
appId: string | null
source: string
reasons: SetNodesAnomalyReason[]
oldCount: number
newCount: number
@ -97,7 +98,6 @@ type SetNodesAnomalyLogEntry = {
pendingInitialSync: boolean
isConnected: boolean
}
stack: string
}
const GRAPH_IMPORT_LOG_LIMIT = 20
@ -420,7 +420,7 @@ export class CollaborationManager {
this.connect(appId, reactFlowStore)
}
setNodes = (oldNodes: Node[], newNodes: Node[]): void => {
setNodes = (oldNodes: Node[], newNodes: Node[], source = 'collaboration-manager:setNodes'): void => {
if (!this.doc)
return
@ -428,7 +428,7 @@ export class CollaborationManager {
if (this.isUndoRedoInProgress)
return
this.captureSetNodesAnomaly(oldNodes, newNodes)
this.captureSetNodesAnomaly(oldNodes, newNodes, source)
this.syncNodes(oldNodes, newNodes)
this.doc.commit()
}
@ -510,8 +510,9 @@ export class CollaborationManager {
if (value?.value && typeof value.value === 'object' && 'selectedNodeId' in value.value && this.reactFlowStore) {
const selectedNodeId = (value.value as { selectedNodeId?: string | null }).selectedNodeId
if (selectedNodeId) {
const { setNodes } = this.reactFlowStore.getState()
const nodes = this.reactFlowStore.getState().getNodes()
const state = this.reactFlowStore.getState()
const { setNodes } = state
const nodes = state.getNodes()
const newNodes = nodes.map((n: Node) => ({
...n,
data: {
@ -519,6 +520,7 @@ export class CollaborationManager {
selected: n.id === selectedNodeId,
},
}))
this.captureSetNodesAnomaly(nodes, newNodes, 'reactflow-native:undo-redo-selection-restore')
setNodes(newNodes)
}
}
@ -826,9 +828,11 @@ export class CollaborationManager {
requestAnimationFrame(() => {
// Get ReactFlow's native setters, not the collaborative ones
const state = reactFlowStore.getState()
const previousNodes = state.getNodes()
const updatedNodes = Array.from(this.nodesMap?.values() || []) as Node[]
const updatedEdges = Array.from(this.edgesMap?.values() || []) as Edge[]
// Call ReactFlow's native setters directly to avoid triggering collaboration
this.captureSetNodesAnomaly(previousNodes, updatedNodes, 'reactflow-native:undo-apply')
state.setNodes(updatedNodes)
state.setEdges(updatedEdges)
@ -866,9 +870,11 @@ export class CollaborationManager {
requestAnimationFrame(() => {
// Get ReactFlow's native setters, not the collaborative ones
const state = reactFlowStore.getState()
const previousNodes = state.getNodes()
const updatedNodes = Array.from(this.nodesMap?.values() || []) as Node[]
const updatedEdges = Array.from(this.edgesMap?.values() || []) as Edge[]
// Call ReactFlow's native setters directly to avoid triggering collaboration
this.captureSetNodesAnomaly(previousNodes, updatedNodes, 'reactflow-native:redo-apply')
state.setNodes(updatedNodes)
state.setEdges(updatedEdges)
@ -1007,6 +1013,7 @@ export class CollaborationManager {
})
// Call ReactFlow's native setter directly to avoid triggering collaboration
this.captureSetNodesAnomaly(previousNodes, updatedNodes, 'reactflow-native:import-nodes-map-subscribe')
state.setNodes(updatedNodes)
this.scheduleGraphImportEmit()
@ -1146,7 +1153,7 @@ export class CollaborationManager {
URL.revokeObjectURL(url)
}
private captureSetNodesAnomaly(oldNodes: Node[], newNodes: Node[]): void {
private captureSetNodesAnomaly(oldNodes: Node[], newNodes: Node[], source: string): void {
const oldNodeIds = oldNodes.map(node => node.id)
const newNodeIds = newNodes.map(node => node.id)
const newNodeIdSet = new Set(newNodeIds)
@ -1168,10 +1175,10 @@ export class CollaborationManager {
if (!reasons.length)
return
const stack = new Error('setNodes anomaly').stack || ''
const entry: SetNodesAnomalyLogEntry = {
timestamp: Date.now(),
appId: this.currentAppId,
source,
reasons,
oldCount: oldNodes.length,
newCount: newNodes.length,
@ -1188,7 +1195,6 @@ export class CollaborationManager {
pendingInitialSync: this.pendingInitialSync,
isConnected: this.isConnected(),
},
stack,
}
this.setNodesAnomalyLogs.push(entry)
if (this.setNodesAnomalyLogs.length > SET_NODES_ANOMALY_LOG_LIMIT)

View File

@ -39,13 +39,14 @@ export const useCollaborativeWorkflow = () => {
const store = useStoreApi()
const { setNodes: collabSetNodes, setEdges: collabSetEdges } = collaborationManager
const setNodes = useCallback((newNodes: Node[], shouldBroadcast: boolean = true) => {
const setNodes = useCallback((newNodes: Node[], shouldBroadcast: boolean = true, source = 'use-collaborative-workflow:setNodes') => {
const { getNodes, setNodes: reactFlowSetNodes } = store.getState()
if (shouldBroadcast) {
const oldNodes = getNodes()
collabSetNodes(
oldNodes.map(sanitizeNodeForBroadcast),
newNodes.map(sanitizeNodeForBroadcast),
source,
)
}
reactFlowSetNodes(newNodes)

View File

@ -45,7 +45,7 @@ export const usePerformRestore = () => {
const currentNodes = collaborationManager.getNodes()
const currentEdges = collaborationManager.getEdges()
collaborationManager.setNodes(currentNodes, nodes)
collaborationManager.setNodes(currentNodes, nodes, 'leader-restore:apply-graph')
collaborationManager.setEdges(currentEdges, edges)
collaborationManager.refreshGraphSynchronously()

View File

@ -787,7 +787,7 @@ export const useNodesInteractions = () => {
}
const { newNodes, newEdges } = computeBatchDelete(filteredDeleteSet, nodes, edges)
setNodes(newNodes)
setNodes(newNodes, true, 'nodes:perform-batch-cascade-delete')
setEdges(newEdges)
handleSyncWorkflowDraft()
@ -2251,7 +2251,7 @@ export const useNodesInteractions = () => {
const shouldBroadcast = collaborationManager.isConnected()
setEdges(edges, shouldBroadcast)
setNodes(nodes, shouldBroadcast)
setNodes(nodes, shouldBroadcast, 'nodes:history-back')
if (shouldBroadcast)
collaborationManager.emitHistoryAction('undo')
workflowStore.setState({ edgeMenu: undefined })
@ -2276,7 +2276,7 @@ export const useNodesInteractions = () => {
const shouldBroadcast = collaborationManager.isConnected()
setEdges(edges, shouldBroadcast)
setNodes(nodes, shouldBroadcast)
setNodes(nodes, shouldBroadcast, 'nodes:history-forward')
if (shouldBroadcast)
collaborationManager.emitHistoryAction('redo')
workflowStore.setState({ edgeMenu: undefined })