多模冗余(XMR)
可用性:两个变体(Full / Tiny)均支持。头文件
onepath_xmr.h。
XMR(X-Modular Redundancy,多模冗余)把「N 份冗余 + 投票选举」收敛为按组名 join 的 位置透明角色:一个任务在主机 A 提交,一群 worker(可在任意机器)冗余计算,投票消费端 (可在主机 C)本地投票选出可靠结果。N 任意:1=无冗余、2=DMR(检错)、3=TMR(纠 1 错)、 N=NMR(纠 ⌊(N−1)/2⌋ 错)。
一、设计要点
- 位置透明的组:一个组 = 一个名字。各角色按组名 join,不假设同机;跨节点天然可用。
- 每角色一行:
submit/compute(worker)/consume(投票消费端)/store(副本)/put/get。opts=NULL即全默认。 - auto-N(存活感知自动计数):worker 上线即被消费端自动计入 N,spawn 几个就是几模 冗余,worker 增减自适应;
opts.expected可钉死。 - 边缘投票(无单点):投票在消费端本地完成。投票器本身是单点(SPOF)——标准解 是让每个消费端各自投票(等价 triplicated voters),XMR 默认即如此。
- 端到端完整性:每份结果默认带 CRC32;被翻转的结果在投票前剔除 (
opts.integrity = ONEPATH_XMR_NONE可关)。 - 可插投票:默认精确多数;
opts.vote_fn可换 median / 阈值 / 取最值等策略。
二、快速开始(跨节点,每角色一行)
#include <onepath.h>
#include <onepath_xmr.h>
/* 主机 A — 生产者 */
onepath_xmr_submit(s, "img-infer", data, len);
/* 任意机器 — 每个 worker 一行, spawn N 个 */
static void infer(void *ud, const void *in, size_t n, onepath_xmr_sink_t *sink) {
/* ... 计算 ... */ onepath_xmr_emit(sink, out, out_len);
}
onepath_xmr_t w; onepath_xmr_compute(s, &w, "img-infer", infer, NULL, NULL);
/* 主机 C — 投票消费端 (= voter + consumer 合并, 边缘投票) */
static void on_result(void *ud, const onepath_xmr_result_t *r) {
if (r->agreed) use(r->data, r->data_len); /* 可靠结果 */
else handle_no_consensus(); /* DMR 检出分歧 */
}
onepath_xmr_t c; onepath_xmr_consume(s, &c, "img-infer", on_result, NULL, NULL);存储同理:onepath_xmr_store(副本,spawn N)/ onepath_xmr_put(写)/ onepath_xmr_get (读端按 sign 投票选举)。配套示例程序 onepath_xmr_compute_demo 与 onepath_xmr_store_demo 给出完整可运行版本。
各入口的完整签名、onepath_xmr_opts_t 选项字段、onepath_xmr_result_t 结构与 ONEPATH_XMR_* 常量,见 API 参考 段的链接。
三、N、quorum 与投票
- N 的来源:默认由存活感知自动计数在线 worker / 副本数;
opts.expected可钉死 (适合 N 固定的场景)。 - 多数与容错:精确多数需 ⌊N/2⌋+1 票一致,可容忍 ⌊(N−1)/2⌋ 个故障。
- N=1:无冗余;N=2(DMR):能检错不能纠错(两份分歧 →
agreed=0); N=3(TMR):纠 1 错;N=5:纠 2 错。
- N=1:无冗余;N=2(DMR):能检错不能纠错(两份分歧 →
- 低延迟快路径:一旦达到多数即选举,不必等齐全部 worker(落后者不阻塞结果)。
- 自定义投票:
vote_fn接收候选数组,返回胜者索引——可实现 median(数值)、阈值 (近似一致)、取最值、加权等策略。
四、可靠性模型与限制(务必阅读)
XMR 用软件冗余屏蔽独立瞬态故障(如不同 worker 各自被偶发错误命中)。它的能力边界 如下。
投票器是单点;用边缘投票化解
多数投票器本身若出错则结果出错。消除单点的标准做法是让每个消费端各自投票(不是共用 一个投票节点)。XMR 默认即「投票消费端」合一:消费端订阅 N 份原始结果并本地投票, 因此:
- 没有独立「已选举结果」的额外传输跳;
- 没有单一投票节点单点(每个消费端等价一个独立投票器)。
Case 1:节点有物理加固 / EDAC(纠错存储)
投票消费端在加固节点上投票,结果在该节点可靠。但若把选举结果再传给另一台未加固节点 的消费者,这一跳与远端内存又脱离 XMR 保护、可能再次出错。
建议:要么就地消费(voter+consumer 合一于加固节点),要么远端消费者不要信任转发来 的结果,而是自己也订阅 N 份原始结果本地再投一次(边缘投票)。opts.republish_sign 的 转发仅用于带宽 / 扇出,不应作为跨未加固跳的「可信」结果。
Case 2:无加固 / 无 EDAC
XMR 能赋能到:
- 屏蔽 worker 阶段的独立瞬态故障(N=3 容 1、N=5 容 2…);
- 端到端 CRC 把传输中被翻转的结果在投票前剔除,把可靠边界推到消费节点。
但 XMR 不能根除以下风险
- 最终合并那一刻:投票消费端自身那一次比较 / 内存若出错,结果仍可能错。软件只能用 边缘投票把它从「单点」降为「多点」,但每个消费节点那一刻的内存仍需该节点有 EDAC 才 彻底可靠。
- 共模 / 相关故障:所有 worker 跑相同代码 / 相同硬件时,确定性缺陷、或同一窗口多个 worker 同时被命中,会让多数一致地错,投票失效。抗共模需实现多样性(不同算法 / 不同硬件,即 N-version)。XMR over 同构 worker 只屏蔽独立瞬态故障。
- 共享资源:共用电源 / 时钟 / 链路、或同机多 worker 同时失效 → 无保护。
一句话:XMR 提供「可调 N 的独立瞬态故障屏蔽 + 端到端校验 + 无单点的边缘投票」;残留风险 是消费节点最终合并的内存(需硬件 EDAC)、共模故障(需实现多样性)。
五、注意事项
- 同一组内各角色的
integrity设置应一致(默认全开即一致)。 - 任务序号跨生产者须唯一:每次
onepath_xmr_submit即一个独立任务实例;多生产者建议用 不同组。 onepath_xmr_get内部对读取使用「查询全部副本 + 不合并应答」(对应onepath_get_opts_t的target = ONEPATH_QUERY_TARGET_ALL、consolidation = ONEPATH_CONSOLIDATION_NONE), 以便看到每个副本的应答用于投票。- 计算结果与存储值对 XMR 是不透明字节;编码 / 元信息请自行编排在值内。
API 参考
完整的函数签名、onepath_xmr_opts_t 选项字段、onepath_xmr_result_t 结构、回调类型与 ONEPATH_XMR_* 常量,见 多模冗余 XMR API。