“替补副本与hint交接展示共同宇宙改变后的具体失败:N=3的家副本为A/B/C,写在D/E两个替补持久完成,后继读只得到A/B的旧值。虽然R+W=4 3,实际读写响应集却不相交。该页随后逐步…”
形式陈述
正常归属与这一次实际响应者
法定人数的相交公式先固定一个副本宇宙。暂时故障时若允许另找替补,操作可能更容易凑够响应,但原先那条相交证明不再自动适用。本页把“正常应保存在哪”与“这次谁真的保存了”分别写进状态。
固定一个成员epoch及有限物理节点集合。每个键有无重复物理身份的首选列表;前N个是家副本,后面的节点可以替补。每次操作按本协调者观察到的可达性,从首选列表取至多N个可达节点。可达性可以判断错,各协调者可以不同;它不改变已登记的家副本身份。[1,§§4.3、4.5–4.6]
若家副本A不可达,替补D保存的记录还要写明“最终交给A”。这份带原接收者的持久记录称为hint。一个物理节点不能通过占据多个虚拟位置被重复计票;列表先去除重复物理身份。本文固定成员和归属,不处理改epoch后的迁移授权。
写操作带不复用的客户端操作ID,向实际选中的节点发送。W个不同物理节点对本操作的匹配持久确认齐备后,写才返回。读从R个不同物理节点收集本操作的匹配回复,再按本页版本规则选值。明确的缺键回复是底状态,可以计入R;超时没有回复,不能当作缺键。读并不执行ABD式返回前多数写回;超时未收齐时只能报告未完成,不能推断远端没有生效。
明确选择一套版本语义
为单独研究复制与交接,本文调用LWW寄存器的最大标签合并。每键记录为(key,tag,deleted,payload),tag在该键内全序、不同写唯一,并随值与删除标志一同持久化。相同(key,tag)必须携带相同内容;复制消息只传播已有写,不能把旧请求改成一次新写。
合法新写的标签生成另由LWW合同负责:大于本地已观察标签且不复用。附件直接接收满足该合同的记录,整数标签按键划分命名空间;它没有用全局协调计数器伪装成分布式实现。同一键收到较小标签便保留已有较大记录,重复相同记录无副作用。
因此“最后”指标签顺序,不是现实中最后返回。本文允许确定丢弃并发的较小标签意图;Dynamo原文的向量版本及应用合并是另一套可选语义,不能把这里每键一个最大值直接用于多值寄存器。[1,§4.4与§6]
hint何时可以退场
替补端以(epoch,home,key)索引hint,只保留该项的最大标签记录。交接请求携带源替补、目标家副本、epoch及完整记录身份。接收者先把该记录join进自己的稳定状态,然后才发确认,承诺“持久状态至少包含本次记录在LWW序中的信息”。仅把字节收到内存不产生这项确认。
替补端收到确认时核对源、接收者、epoch、键和记录身份。只有ACK对应当前仍保存的那份hint,才原子持久删除它;若同槽hint已被更高版本替换,旧ACK不能删除新记录。丢失确认造成重复发送,由join幂等性吸收。
本页的稳定步骤是教学存储接口:提交要么完整生效并在重启后保留,要么尚未生效,易失暂存则可丢失。参考器模拟这个接口,不声称用Python内存赋值实现了真实磁盘掉电安全。节点恢复保留稳定状态和epoch,不允许从旧备份悄悄回滚。
直觉
代收包裹与完成交接不是一回事
D可以代A保存数据,让客户在A暂时失联时继续完成一项写。但“D已经收好”不能改写成“A已经知道”;后来只问A和B的读者仍可能看不见这次写。
hint的home字段保留这份未履行的交接责任。网络恢复后,D要等A真的把数据纳入稳定版本再退场;它不能在发送后立刻删除,也不能把某个键的一份旧确认当成该键此后所有版本的收据。
例子与边界
R加W大于N,仍然读不到已经完成的写
首选顺序为A、B、C、D、E、F,家副本H={A,B,C},N=3、W=2、R=2。A/B/C初始为版本0的old。
某次版本7/v7的写只能到D/E/F。三个替补分别代A/B/C;D和E已经持久保存hint并确认,F的请求尚未完成。写者凭两个不同物理确认返回。随后另一协调者可以联系A/B/C,却暂时联系不到D/E/F;它读A和B,两者仍是0。
这里4=R+W>N=3,但实际写确认集合是{D,E},读响应集合是{A,B},交集为空。包含—排除公式没有错,错在把六节点中的两份不同响应集当成同一个三元素集合的子集。即便F也保存了7,只要后继读仍只问A/B,问题不变。
正常家副本上也不能只凭相交便宣称线性一致性:未完成写的新值被某次读公开后,仍需要完整协议处理读后的证据继承。现有ABD页已经给出写前查询与读后写回,本页不重新命名那套构造。
先持久吸收,再确认
D有发给A的hint7。第一次交接,A只收到易失暂存便崩溃;没有持久ACK,D必须继续保留7。第二次重试,A把7稳定提交,但ACK在网络中丢失;D仍保留7。第三次发相同请求,A保留7并再次确认,重复不会生成新版本。
若改成A一收到字节就ACK、D据此删除、A随后崩溃,唯一这份交接副本就可能消失。这是确认语义错误,不是多重重试能补救的偶发网络问题。
旧确认不能清除新责任
让交接7的ACK在途中停留。期间D接到9/v9,原子把同一(home=A,key)hint从7提升到9。旧ACK7现在到达:它只证明A已吸收至少7,不能证明A已到9,所以D保留9。
等交接9在A稳定提交并返回匹配ACK后,D才可删当前9。若D在删除前崩溃,恢复后可能再发9;若在稳定删除后崩溃,A已经拥有至少9。两种窗口都不产生“双方都因这次交接而丢失证据”的空档。
A若先有11,收到9仍可确认;它保留11,满足“至少9”。要求A只有恰好改成9才ACK会让较旧交接无谓卡住。相反,按键名一刀删掉hint,则会在7的旧确认到来时丢掉9。
一份持久副本不是无限故障的保证
W按实际不同物理持久者计数,同机的两个进程或两个虚拟节点不会变成两份独立故障域。确认以后若所有保存者的磁盘都永久丢失,协议不能凭ACK历史复原payload。hint保留、修复调度及跨故障域布置分别影响耐久性。
hint也不能无限替代归属管理。若A永久退役或家副本表改了epoch,旧home需要明确迁移协议;本页直接拒绝跨epoch消息,不靠重试猜新目的地。TTL到期不证明别处已收好。
推论与应用
安全退场的短证明
对一项被删除的hint x,删除规则要求一个匹配ACK。诚实接收者只有在稳定状态y≥x后才发此ACK;稳定状态只按join上升,且恢复不能回滚,所以删除时它仍≥x。若源端已换成x'>x,身份不匹配使旧ACK无法触发删除。于是删除要么移走一份已经被家副本吸收的信息,要么被拒绝,没有把尚未交付的新责任静默移走。
该证明不要求消息及时或恰一次,只要求确认身份可信、接收者持久语义和版本单调。它证明的是单项退场安全,不是所有家副本已同步,也不是业务操作线性一致。
最终能交回去还需要什么
若最终不再产生更高版本,目标家副本恢复并持续正确,某个保留hint的正确替补公平重试,且请求和确认最终有成功往返,则最大hint终会被吸收并退场。每个前提都有作用:替补永久丢盘使数据源消失,永久分区使交付失败,不运行修复使可靠链路也无事可做。
没有hint的普通家副本差异仍需其他修复渠道。按范围比较Merkle快照可以定位遗漏并join回活动数据;它同样不会从“当前有多少副本”推出授权或实时顺序。
附件实际做了多少工作
设首选列表长P、可达输入序列长Q。附件先读取Q项建立可达散列表,再扫描P项;家副本与已选物理身份也各建散列表,保留原列表次序输出,期望O(1+P+Q),最多输出N个目标。Q允许包含不在首选列表中的身份及重复项,不能把这次输入读取省掉。W个有效确认至少来自W份稳定处理,但N次请求的响应时延没有同步上界;只等W个并不让每个请求免费。
每个hint查找、最大版本比较及精确删除是期望常数字典工作,另计payload比较/复制、持久存储成本。当前H项hint占O(1+H)条元数据及其值字节;保存所有事件日志另随历史增长。附件show()为了可读输出排序n条普通记录及H项hint,还要另付O(n log(n+1)+H log(H+1)+1),这不属于单项交接核心成本。批量扫描待交接项要付O(1+H),不能把每项常数操作相加后仍称整个修复常数时间。
附件中的提交与崩溃窗口、旧ACK7和当前hint9见复制修复终点。返回的记录可逐步检查,不连接任何真实存储节点。
参考资料
- [1] Giuseppe DeCandia等,Dynamo: Amazon’s Highly Available Key-value Store,SOSP2007,pp.205–220;§4.3(PDF6页)明确物理身份去重,§§4.5–4.6(PDF7–8页)给可达首选节点、R/W与hint原接收者,§4.7与§5给后台修复和读路径修复。本文固定epoch,并独立展开持久ACK/旧确认条件;版本合并选择LWW,不复刻原文完整向量冲突实现