WAL proposer 连接所有三个 safekeeper不是只连 donor。WalProposerStartvoid WalProposerStart(WalProposer *wp) { /* Initiate connections to all safekeeper nodes */ for (int i 0; i wp-n_safekeepers; i) ResetConnection(wp-safekeeper[i]); WalProposerLoop(wp); }WAL 广播也是发给所有SS_ACTIVE 的 safekeeperstatic void BroadcastAppendRequest(WalProposer *wp) { for (int i 0; i wp-n_safekeepers; i) if (wp-safekeeper[i].state SS_ACTIVE) SendMessageToNode(wp-safekeeper[i]); }Quorum 只需要 2 个walproposer.c:179wp-quorum 3/2 1 2但连接保持 3 个。Paxos 在 WAL proposer 和 safekeeper 之间不是 safekeeper 之间流程全部由 WAL proposer 驱动WAL proposer (计算节点) safekeeper 1,2,3 │ │ ├─ Greeting ──────────────────→ │ 发起连接 │ │ ├─ VoteRequest ───────────────→ │ 请求投票 │←─ VoteResponse ────────────── │ 各回各的 │ │ ├─ 收集 quorum(≥2)选出 donor │ │ │ ├─ ProposerElected ───────────→ │ 宣布当选 │ │ ├─ AppendRequest(WAL) ─────────→ │ 推送 WAL │←─ AppendResponse ──────────── │ 各回各的确认Safekeeper 之间不会互相投票、不会互相选举。WAL proposer 就是 Paxos 的 proposersafekeeper 是 acceptor。WAL 发三个两个确认就算成功核心在 walproposer.c:1956-1964// 收集所有 safekeeper 的 flushLsn 到数组 for (uint32 i 0; i mset-len; i) { if (sk ! NULL sk-appendResponse.flushLsn wp-propTermStartLsn) responses[i] sk-appendResponse.flushLsn; else responses[i] 0; } qsort(responses, mset-len, sizeof(XLogRecPtr), CompareLsn); // 升序排列 // 取 跳过 n - quorum 个 之后的值 中间值 return responses[mset-len - MsetQuorum(mset)]; // responses[3 - 2] // responses[1] ← 升序排列后的第 2 个中间值图示3 个 safekeeperquorum 2safekeeper1 flushLsn 100 safekeeper2 flushLsn 200 safekeeper3 flushLsn 300 升序: [100, 200, 300] ↑ ↑ responses[0] responses[2] responses[3 - 2] responses[1] 200 → commitLsn 200至少 2 个节点确认到这里这就是 Raft 标准做法commit 到多数派都确认的位置。3 个里任意 2 个就行所以即使你有一台 safekeeper 偶尔断连业务也能正常工作。WAL proposer 的超时看门狗机制wp_log(WARNING, terminating connection to safekeeper %s:%s in %s state: no messages received during the last %dms or connection attempt took longer than that,sk-host, sk-port, FormatSafekeeperState(sk), wp-config-safekeeper_connection_timeout);ShutdownConnection(sk);触发条件WalProposerPoll()主循环中每次轮询都会检查每个 safekeeper 连接如果距离latestMsgReceivedAt最后一次收到消息的时间已经超过了safekeeper_connection_timeout默认10000ms 10秒就会打印这条 WARNING然后调用ShutdownConnection关闭该连接。latestMsgReceivedAt只在三个地方更新行号场景431发起连接时进入SS_CONNECTING_WRITE状态593连接成功建立时2449成功从 safekeeper 读取到消息时出现告警是否正常不频繁出现是正常的— 属于连接超时后的自动恢复机制。safekeeper 连接被关闭后ReconnectSafekeepers()会自动尝试重连。常见触发场景网络抖动— safekeeper 与 WAL proposer 之间网络短暂不通safekeeper 重启— 对端进程重启导致连接静默safekeeper 过载— CPU/IO 繁忙导致响应变慢10秒内未回复任何消息频繁出现则说明有问题需要排查网络是否稳定丢包、延迟safekeeper 是否健康CPU、IO、内存10 秒的超时是否太短可能通过wal_acceptor_connection_timeoutGUC 参数调整定义在 walproposer_pg.c:66总结这是 WAL proposer 的健康检查机制超过 10 秒没收到 safekeeper 的消息就认为连接已死并重连。偶尔出现属于自愈行为持续/频繁出现则需要排查网络或 safekeeper 节点状态。
neon walproposer
WAL proposer 连接所有三个 safekeeper不是只连 donor。WalProposerStartvoid WalProposerStart(WalProposer *wp) { /* Initiate connections to all safekeeper nodes */ for (int i 0; i wp-n_safekeepers; i) ResetConnection(wp-safekeeper[i]); WalProposerLoop(wp); }WAL 广播也是发给所有SS_ACTIVE 的 safekeeperstatic void BroadcastAppendRequest(WalProposer *wp) { for (int i 0; i wp-n_safekeepers; i) if (wp-safekeeper[i].state SS_ACTIVE) SendMessageToNode(wp-safekeeper[i]); }Quorum 只需要 2 个walproposer.c:179wp-quorum 3/2 1 2但连接保持 3 个。Paxos 在 WAL proposer 和 safekeeper 之间不是 safekeeper 之间流程全部由 WAL proposer 驱动WAL proposer (计算节点) safekeeper 1,2,3 │ │ ├─ Greeting ──────────────────→ │ 发起连接 │ │ ├─ VoteRequest ───────────────→ │ 请求投票 │←─ VoteResponse ────────────── │ 各回各的 │ │ ├─ 收集 quorum(≥2)选出 donor │ │ │ ├─ ProposerElected ───────────→ │ 宣布当选 │ │ ├─ AppendRequest(WAL) ─────────→ │ 推送 WAL │←─ AppendResponse ──────────── │ 各回各的确认Safekeeper 之间不会互相投票、不会互相选举。WAL proposer 就是 Paxos 的 proposersafekeeper 是 acceptor。WAL 发三个两个确认就算成功核心在 walproposer.c:1956-1964// 收集所有 safekeeper 的 flushLsn 到数组 for (uint32 i 0; i mset-len; i) { if (sk ! NULL sk-appendResponse.flushLsn wp-propTermStartLsn) responses[i] sk-appendResponse.flushLsn; else responses[i] 0; } qsort(responses, mset-len, sizeof(XLogRecPtr), CompareLsn); // 升序排列 // 取 跳过 n - quorum 个 之后的值 中间值 return responses[mset-len - MsetQuorum(mset)]; // responses[3 - 2] // responses[1] ← 升序排列后的第 2 个中间值图示3 个 safekeeperquorum 2safekeeper1 flushLsn 100 safekeeper2 flushLsn 200 safekeeper3 flushLsn 300 升序: [100, 200, 300] ↑ ↑ responses[0] responses[2] responses[3 - 2] responses[1] 200 → commitLsn 200至少 2 个节点确认到这里这就是 Raft 标准做法commit 到多数派都确认的位置。3 个里任意 2 个就行所以即使你有一台 safekeeper 偶尔断连业务也能正常工作。WAL proposer 的超时看门狗机制wp_log(WARNING, terminating connection to safekeeper %s:%s in %s state: no messages received during the last %dms or connection attempt took longer than that,sk-host, sk-port, FormatSafekeeperState(sk), wp-config-safekeeper_connection_timeout);ShutdownConnection(sk);触发条件WalProposerPoll()主循环中每次轮询都会检查每个 safekeeper 连接如果距离latestMsgReceivedAt最后一次收到消息的时间已经超过了safekeeper_connection_timeout默认10000ms 10秒就会打印这条 WARNING然后调用ShutdownConnection关闭该连接。latestMsgReceivedAt只在三个地方更新行号场景431发起连接时进入SS_CONNECTING_WRITE状态593连接成功建立时2449成功从 safekeeper 读取到消息时出现告警是否正常不频繁出现是正常的— 属于连接超时后的自动恢复机制。safekeeper 连接被关闭后ReconnectSafekeepers()会自动尝试重连。常见触发场景网络抖动— safekeeper 与 WAL proposer 之间网络短暂不通safekeeper 重启— 对端进程重启导致连接静默safekeeper 过载— CPU/IO 繁忙导致响应变慢10秒内未回复任何消息频繁出现则说明有问题需要排查网络是否稳定丢包、延迟safekeeper 是否健康CPU、IO、内存10 秒的超时是否太短可能通过wal_acceptor_connection_timeoutGUC 参数调整定义在 walproposer_pg.c:66总结这是 WAL proposer 的健康检查机制超过 10 秒没收到 safekeeper 的消息就认为连接已死并重连。偶尔出现属于自愈行为持续/频繁出现则需要排查网络或 safekeeper 节点状态。