join-status.js
2.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* fan-in 等待态(27-P4 T10):一个 pending 节点在等哪几个上游、各自是什么状态。
*
* 并行(27-P4 T4/T6)落地之后一张图可能同时有好几个分支在跑,「这个节点为什么还没轮到」
* 不再是一眼能看出来的事——以前只有一条链,pending 的下一个必然是紧邻上游那一个;
* 现在它可能在等三个并行分支里最慢的那一个。§4.3 决策要求「run 层面要能回答『我卡在等
* 哪几个节点』」,这里就是那句话的数据源。
*
* 判据与引擎 `graph-engine.activateSuccessTargets`/`allIncomingSatisfied` 同源:
* 只看 forward 的 success 入边——backward 边是异常流程,不参与「汇合点」这道闸,
* fail 边同理。跟 io-model.js 的 inboundEdges 用的是同一条筛选条件,别改出分叉。
*/
/**
* @param {object} node getRunState 的合并节点(要 status 与 defNodeId 两个键)
* @param {Array} nodes 同图全部节点(拿名字与上游状态用)
* @param {Array} edges 同图全部边
* @returns {null | {
* waiting: Array<{defNodeId, name, status}>,
* total: number, done: number, satisfied: boolean,
* isFanIn: boolean, // 2+ 条 forward success 入边才算真正的 fan-in;单一入边只是普通顺序等待
* blockedBy: Array<{defNodeId, name, status}>,
* }}
*/
export function resolveJoinWait(node, nodes = [], edges = []) {
if (!node || node.status !== 'pending') return null;
const incoming = (edges || []).filter((e) => (
String(e.to) === String(node.defNodeId)
&& (e.condition || 'success') === 'success'
&& (e.edgeKind || 'forward') !== 'backward'
));
if (!incoming.length) return null; // 没有 forward success 入边:不是在等 fan-in,是别的原因还没轮到
const byId = new Map((nodes || []).map((n) => [String(n.defNodeId), n]));
const waiting = incoming.map((e) => {
const src = byId.get(String(e.from));
return {
defNodeId: e.from,
name: (src && (src.name || src.defNodeId)) || e.from,
status: (src && src.status) || 'pending',
};
});
const done = waiting.filter((w) => w.status === 'success').length;
return {
waiting,
total: waiting.length,
done,
satisfied: done === waiting.length,
isFanIn: waiting.length > 1,
blockedBy: waiting.filter((w) => w.status !== 'success'),
};
}
/**
* 画布上的紧凑摘要——克制原则同 `edgeContractLabel`:只占一个小徽标的位置,
* 完整明细留给 title。`satisfied` 时不显示(该节点这一刻就会被点亮,不算「在等」);
* 非 fan-in(只有一条入边)时也不显示——普通顺序等待,看紧邻上游的状态就够了,
* 加一个「1/1」徽标只会制造噪声。
*/
export function joinWaitLabel(wait) {
if (!wait || !wait.isFanIn || wait.satisfied) return null;
return {
text: `⏳${wait.done}/${wait.total}`,
title: `在等:${wait.waiting.map((w) => `${w.name}(${w.status})`).join(' · ')}`,
};
}