neon walproposer

📅 2026/7/3 11:46:32 👁️ 阅读次数 📝 编程学习
neon walproposer

WAL proposer 连接所有三个 safekeeper

不是只连 donor。WalProposerStart:

void 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 的 safekeeper:

static 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 的 proposer,safekeeper 是 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 个 safekeeper,quorum = 2):

safekeeper1 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()会自动尝试重连。常见触发场景:

  1. 网络抖动— safekeeper 与 WAL proposer 之间网络短暂不通
  2. safekeeper 重启— 对端进程重启导致连接静默
  3. safekeeper 过载— CPU/IO 繁忙导致响应变慢,10秒内未回复任何消息

频繁出现则说明有问题需要排查

  • 网络是否稳定(丢包、延迟)
  • safekeeper 是否健康(CPU、IO、内存)
  • 10 秒的超时是否太短(可能通过wal_acceptor_connection_timeoutGUC 参数调整,定义在 walproposer_pg.c:66)

总结

这是 WAL proposer 的健康检查机制:超过 10 秒没收到 safekeeper 的消息就认为连接已死并重连。偶尔出现属于自愈行为,持续/频繁出现则需要排查网络或 safekeeper 节点状态