Skip to content

多模冗余(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⌋ 错)。

与「三模便捷封装」的区别

三模存储 / 三模计算单节点三通路便捷封装; XMR 是多副本投票的冗余容错层。两者正交、可叠加。

一、设计要点

  • 位置透明的组:一个组 = 一个名字。各角色按组名 join,不假设同机;跨节点天然可用。
  • 每角色一行submit / compute(worker)/ consume(投票消费端)/ store(副本)/ put / getopts=NULL 即全默认。
  • auto-N(存活感知自动计数):worker 上线即被消费端自动计入 N,spawn 几个就是几模 冗余,worker 增减自适应;opts.expected 可钉死。
  • 边缘投票(无单点):投票在消费端本地完成。投票器本身是单点(SPOF)——标准解 是让每个消费端各自投票(等价 triplicated voters),XMR 默认即如此。
  • 端到端完整性:每份结果默认带 CRC32;被翻转的结果在投票前剔除 (opts.integrity = ONEPATH_XMR_NONE 可关)。
  • 可插投票:默认精确多数;opts.vote_fn 可换 median / 阈值 / 取最值等策略。

二、快速开始(跨节点,每角色一行)

c
#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 (读端按键投票选举)。配套示例程序 onepath_xmr_compute_demoonepath_xmr_store_demo 给出完整可运行版本。

三、API 参考

c
int  onepath_xmr_submit(onepath_session_t s, const char *group, const void *data, size_t len);
int  onepath_xmr_compute(onepath_session_t s, onepath_xmr_t *out, const char *group,
                         onepath_xmr_fn fn, void *ud, const onepath_xmr_opts_t *opts);
int  onepath_xmr_emit(onepath_xmr_sink_t *sink, const void *out, size_t len);
int  onepath_xmr_consume(onepath_session_t s, onepath_xmr_t *out, const char *group,
                         onepath_xmr_result_cb cb, void *ud, const onepath_xmr_opts_t *opts);
int  onepath_xmr_store(onepath_session_t s, onepath_xmr_t *out, const char *group,
                       const onepath_xmr_opts_t *opts);
int  onepath_xmr_put(onepath_session_t s, const char *group, const char *key,
                     const void *data, size_t len);
int  onepath_xmr_get(onepath_session_t s, const char *group, const char *keyexpr,
                     const onepath_xmr_opts_t *opts, onepath_xmr_result_cb cb, void *ud);
void onepath_xmr_close(onepath_xmr_t h);
函数说明
onepath_xmr_submit(s, group, data, len)生产者:提交一条计算任务
onepath_xmr_compute(s, &out, group, fn, ud, opts)worker:订阅任务→计算→发布结果
onepath_xmr_emit(sink, out, len)在 worker 计算函数内产出结果
onepath_xmr_consume(s, &out, group, cb, ud, opts)投票消费端:收集→投票→选举(边缘投票)
onepath_xmr_store(s, &out, group, opts)存储副本:摄入写入、应答读取
onepath_xmr_put(s, group, key, data, len)写入(广播到全部副本)
onepath_xmr_get(s, group, keyexpr, opts, cb, ud)读取并按键投票选举
onepath_xmr_close(h)关闭 worker / 消费端 / 副本句柄

选项 onepath_xmr_opts_t

字段含义默认
expected钉死 N;0 = 存活感知自动计数0(auto)
quorum需多少票一致;0 = 多数 ⌊N/2⌋+10
window_ms一次选举的收齐超时2000
vote_fn / vote_ud自定义投票;NULL = 精确多数NULL
integrityONEPATH_XMR_CRC32 / ONEPATH_XMR_NONECRC32
republish_key消费端非空则把 elected 转发到此键供扇出NULL
node_id覆盖自动节点编号NULL(用节点 ID)
encoding发布编码NULL

选举结果 onepath_xmr_result_t

agreed=1 表示达到多数、结果可靠;agreed=0 表示无定论(DMR 两份分歧或收齐窗口内不足 多数),不应被当作可靠结果votes/n 为一致票数与参与候选数(已剔除 CRC 失败者)。

创建 / 销毁配对

创建销毁
onepath_xmr_compute / onepath_xmr_consume / onepath_xmr_storeonepath_xmr_close

四、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 错。
  • 低延迟快路径:一旦达到多数即选举,不必等齐全部 worker(落后者不阻塞结果)。
  • 自定义投票vote_fn 接收候选数组,返回胜者索引——可实现 median(数值)、阈值 (近似一致)、取最值、加权等策略。

五、可靠性模型与限制(务必阅读)

XMR 用软件冗余屏蔽独立瞬态故障(如不同 worker 各自被偶发错误命中)。它的能力边界 如下。

投票器是单点;用边缘投票化解

多数投票器本身若出错则结果出错。消除单点的标准做法是让每个消费端各自投票(不是共用 一个投票节点)。XMR 默认即「投票消费端」合一:消费端订阅 N 份原始结果并本地投票, 因此:

  • 没有独立「已选举结果」的额外传输跳;
  • 没有单一投票节点单点(每个消费端等价一个独立投票器)。

Case 1:节点有物理加固 / EDAC(纠错存储)

投票消费端在加固节点上投票,结果在该节点可靠。但若把选举结果再传给另一台未加固节点 的消费者,这一跳与远端内存又脱离 XMR 保护、可能再次出错。

建议:要么就地消费(voter+consumer 合一于加固节点),要么远端消费者不要信任转发来 的结果,而是自己也订阅 N 份原始结果本地再投一次(边缘投票)。republish_key 的转发 仅用于带宽 / 扇出,不应作为跨未加固跳的「可信」结果。

Case 2:无加固 / 无 EDAC

XMR 能赋能到:

  • 屏蔽 worker 阶段的独立瞬态故障(N=3 容 1、N=5 容 2…);
  • 端到端 CRC 把传输中被翻转的结果在投票前剔除,把可靠边界推到消费节点。

但 XMR 不能根除以下风险

  1. 最终合并那一刻:投票消费端自身那一次比较 / 内存若出错,结果仍可能错。软件只能用 边缘投票把它从「单点」降为「多点」,但每个消费节点那一刻的内存仍需该节点有 EDAC 才 彻底可靠。
  2. 共模 / 相关故障:所有 worker 跑相同代码 / 相同硬件时,确定性缺陷、或同一窗口多个 worker 同时被命中,会让多数一致地错,投票失效。抗共模需实现多样性(不同算法 / 不同硬件,即 N-version)。XMR over 同构 worker 只屏蔽独立瞬态故障。
  3. 共享资源:共用电源 / 时钟 / 链路、或同机多 worker 同时失效 → 无保护。

一句话:XMR 提供「可调 N 的独立瞬态故障屏蔽 + 端到端校验 + 无单点的边缘投票」;残留风险 是消费节点最终合并的内存(需硬件 EDAC)、共模故障(需实现多样性)。

六、注意事项

  • 同一组内各角色的 integrity 设置应一致(默认全开即一致)。
  • 任务序号跨生产者须唯一:每次 onepath_xmr_submit 即一个独立任务实例;多生产者建议用 不同组。
  • onepath_xmr_get 内部对读取使用「查询全部副本 + 不合并应答」(对应 onepath_get_opts_ttarget = ONEPATH_QUERY_TARGET_ALLconsolidation = ONEPATH_CONSOLIDATION_NONE), 以便看到每个副本的应答用于投票。
  • 计算结果与存储值对 XMR 是不透明字节;编码 / 元信息请自行编排在值内。

OnePath™ 以预构建库形式交付,运行时零外部依赖。