chore: try to track why node missing

This commit is contained in:
hjlarry 2026-03-27 15:55:48 +08:00
parent 018b213ae1
commit 976fa30664

View File

@ -76,7 +76,32 @@ type GraphImportLogEntry = {
}
}
type SetNodesAnomalyReason = 'node_count_decrease' | 'start_removed'
type SetNodesAnomalyLogEntry = {
timestamp: number
appId: string | null
reasons: SetNodesAnomalyReason[]
oldCount: number
newCount: number
removedNodeIds: string[]
oldStartNodeIds: string[]
newStartNodeIds: string[]
oldNodeIds: string[]
newNodeIds: string[]
visibilityState: DocumentVisibilityState | 'unknown'
meta: {
leaderId: string | null
isLeader: boolean
graphViewActive: boolean | null
pendingInitialSync: boolean
isConnected: boolean
}
stack: string
}
const GRAPH_IMPORT_LOG_LIMIT = 20
const SET_NODES_ANOMALY_LOG_LIMIT = 100
const toLoroValue = (value: unknown): Value => cloneDeep(value) as Value
const toLoroRecord = (value: unknown): Record<string, Value> => cloneDeep(value) as Record<string, Value>
@ -101,6 +126,7 @@ export class CollaborationManager {
private pendingGraphImportEmit = false
private graphViewActive: boolean | null = null
private graphImportLogs: GraphImportLogEntry[] = []
private setNodesAnomalyLogs: SetNodesAnomalyLogEntry[] = []
private pendingImportLog: {
timestamp: number
sources: Set<'nodes' | 'edges'>
@ -402,6 +428,7 @@ export class CollaborationManager {
if (this.isUndoRedoInProgress)
return
this.captureSetNodesAnomaly(oldNodes, newNodes)
this.syncNodes(oldNodes, newNodes)
this.doc.commit()
}
@ -1075,6 +1102,7 @@ export class CollaborationManager {
clearGraphImportLog(): void {
this.graphImportLogs = []
this.setNodesAnomalyLogs = []
this.pendingImportLog = null
}
@ -1084,8 +1112,10 @@ export class CollaborationManager {
appId: this.currentAppId,
generatedAt: new Date().toISOString(),
entries: this.graphImportLogs,
setNodesAnomalies: this.setNodesAnomalyLogs,
summary: {
logCount: this.graphImportLogs.length,
setNodesAnomalyCount: this.setNodesAnomalyLogs.length,
leaderId: this.leaderId,
isLeader: this.isLeader,
graphViewActive: this.graphViewActive,
@ -1116,6 +1146,55 @@ export class CollaborationManager {
URL.revokeObjectURL(url)
}
private captureSetNodesAnomaly(oldNodes: Node[], newNodes: Node[]): void {
const oldNodeIds = oldNodes.map(node => node.id)
const newNodeIds = newNodes.map(node => node.id)
const newNodeIdSet = new Set(newNodeIds)
const removedNodeIds = oldNodeIds.filter(nodeId => !newNodeIdSet.has(nodeId))
const oldStartNodeIds = oldNodes
.filter(node => (node.data as CommonNodeType | undefined)?.type === 'start')
.map(node => node.id)
const newStartNodeIds = newNodes
.filter(node => (node.data as CommonNodeType | undefined)?.type === 'start')
.map(node => node.id)
const reasons: SetNodesAnomalyReason[] = []
if (newNodes.length < oldNodes.length)
reasons.push('node_count_decrease')
if (oldStartNodeIds.length > 0 && newStartNodeIds.length === 0)
reasons.push('start_removed')
if (!reasons.length)
return
const stack = new Error('setNodes anomaly').stack || ''
const entry: SetNodesAnomalyLogEntry = {
timestamp: Date.now(),
appId: this.currentAppId,
reasons,
oldCount: oldNodes.length,
newCount: newNodes.length,
removedNodeIds,
oldStartNodeIds,
newStartNodeIds,
oldNodeIds,
newNodeIds,
visibilityState: typeof document === 'undefined' ? 'unknown' : document.visibilityState,
meta: {
leaderId: this.leaderId,
isLeader: this.isLeader,
graphViewActive: this.graphViewActive,
pendingInitialSync: this.pendingInitialSync,
isConnected: this.isConnected(),
},
stack,
}
this.setNodesAnomalyLogs.push(entry)
if (this.setNodesAnomalyLogs.length > SET_NODES_ANOMALY_LOG_LIMIT)
this.setNodesAnomalyLogs.splice(0, this.setNodesAnomalyLogs.length - SET_NODES_ANOMALY_LOG_LIMIT)
}
private snapshotReactFlowGraph(): { nodes: Node[], edges: Edge[] } {
if (!this.reactFlowStore) {
return {