Skip to content

这份任务对应从局部回退到备份与日志保留。完成时交出的应是一份逐页、逐记录可复查的结果:哪些操作已经被补偿,哪些日志仍可能需要,哪些提交必须保留,哪一种输入必须明确拒绝。

标准库核验器只向stdout写JSON,普通和python -O均执行显式检查。它模拟整数页和持久边界,不创建数据库,不执行真实fsync或删除文件。日志在教学实现中占固定槽,LSN为10、20、30……;真实变长记录需要用其实际长度确认连续区间。

一、保存点不是提交 ​

四页A/P/Q/R初值全0。T的日志如下;S在20后建立,不另写持久记录。

LSN 动作 prevLSN undoNextLSN
10 BEGIN T 0 —
20 A:0→2;随后建立S 10 —
30 Q:0→7 20 —
40 P:0→9 30 —
50 CLR把P恢复0 40 30
60 CLR把Q恢复0 50 20
70 R:0→4 60 —
80 COMMIT T 70 —

按局部回退协议,写完60后T仍ACTIVE,S仍有效,P/Q的锁仍归T。70的前驱必须是60,事务链不能假装这两条补偿未曾发生。若最后commit80可靠完成,结果为A2/P0/Q0/R4。

另走没有commit的分支:稳定日志只到70就崩溃。第一次恢复撤销70,写CLR80,prev70、undoNext60;故意只稳定CLR、不刷补偿页,再次崩溃。第二次恢复先redo CLR80,使R=0,之后游标80→60→20→10;只需新CLR90撤A20,再写END100。两次分支里的LSN80分别是COMMIT和CLR,它们不能混成一条历史。

迁移:在70后建立V,再写P=6,然后回到S。新P和R被撤回,原30/40经CLR跳过,V失效而S保留。紧接着再回S不会重复补偿30/40。若只release S,值不变;释放保存点也不释放事务持有的逻辑锁。

二、明确复制开始前已经稳定的页 ​

在记录30之后、40之前,先可靠刷Q页。此刻磁盘Q7@30,A20仍仅在内存中,所以DPT确为{A:20},而不是漏写Q。取得短原子元数据快照后,事务表含T(first10,last30),下一位置B=40。

计算两种恢复下界:

  • r=min(B40,DPT中的20)=20,负责补回落后页
  • q=min(r20,T的first10)=10,保留可能需要的整个undo前缀

给出复制事件表。所有复制发生在开始快照以后,读到一个完整稳定页版本;@后面是该版本的pageLSN,不是复制时刻。

页 复制时已生成日志到 镜像 说明
A 30 0@0 A20仍脏,旧磁盘合法
P 30 0@0 P40尚未发生
Q 30 7@30 Q30已在开始前可靠刷出
R 70 4@70 R70生成后先刷日志与页,再复制

复制结束E=70。此时所需日志至少[q10,E70]齐全。先稳定全部副本和这段归档,再发布完成manifest;附件先尝试过早publish并得到拒绝,然后才完成。停止复制不等于备份已经可用。

三、同一备份,两份恢复证书 ​

恢复到T=80,逐条写出redo或skip:20 redo A;30 skip(Q镜像已30);40 redo P9;50 redo P0;60 redo Q0;70 skip(R镜像已70)。T有稳定commit80,无需undo。输出A2/P0/Q0/R4。

用这条运行检查两个错误变体。若只从B40开始redo,A仍为0;若忽略两条CLR,P/Q保留9/7。不要只说“可能出错”,写出实际四页值。

扩展归档:90 BEGIN U;100 U写Q:0→8;110 BEGIN V;120 V写P:0→6;130 COMMIT V。恢复目标130的完整redo先得到A2/P6/Q8/R4。U没有commit,沿100→90撤销并结束,得到A2/P6/Q0/R4;T和V的结果保留。下载结果同时记录before_undo和最终值。

以下输入均应失败,而不是交一份貌似合理的结果:目标60<E70,缺日志60,缺日志120,历史代际不匹配,页面集合不全,manifest未完成。早目标尤其要说明R4已来自70;仅前滚≤60的日志无法生成旧R0。需要更早恢复时,选择更早基线并保留对应完整日志,不能直接修改目标数字。

截掉早前缀后再崩溃 ​

另构两页迁移:OLD10开始、A20写5、COMMIT30并刷A;NEW40开始、B50写9未提交。元数据给q40/r50/B60,复制A5@20和B0@0,E50。丢弃10..30,仅以40/50归档restore并在第一条CLR60后停止。只稳定CLR60,调用resume_restore保留原manifest再入,应redo50、60,沿60跳到40,最后END70,输出A5/B0且不再生成补偿50的CLR。

这里调用restart会得到明确接口拒绝,因为它要求从10开始的完整日志。正确迁移保留的是原镜像、原TT种子和[q40,新稳定末位置],不是重新要求已经淘汰的10..30。再试不稳定CLR60就崩溃:稳定尾仍50,下一次可重新生成60。两种分支都要得到A5/B0。

四、整段日志什么时候可以删除 ​

按保留前沿协议,稳定域为[0,160),段长40。这里160是下一个位置,不是“最后记录编号”;复制/恢复阶段中的E70、目标130则指已包含的末记录。四pin为redo90、undo10、backup10、replica70。

事件完成后 有效pin 逻辑R 实际H 本次可删段
全部登记 90/10/10/70 10 0 无
undo需要结束;备份独立稳定后取消两pin 90/70 70 40 [0,40)
replica持久到100,再推进pin 90/100 90 80 [40,80)

源日志pin取消不表示唯一归档也能删除。要继续支持第三节的恢复目标130,归档仍须保留该备份的[q10,130];若没有第二份日志来源,就不能按源域的H80销毁唯一副本。

代码会拒绝replica未持久时直接advance(100)。错误变体刻意绕过这个检查,先把pin报到100,再删到80,最后才保存恢复点:在最后一步之前掉电,持久进度仍70,所需记录已丢。枚举D(进度稳定)、P(pin推进)、G(GC)六种次序,写出每步H和持久恢复点;固定各做一次GC的本例中,PGD的G后产生真实缺口。合法实现必须拒绝未经D的P,不靠后来D补救先前承诺。

五、消费者身份与准入迁移 ​

H80时要求从50开始的新消费者,必须明确拒绝。若确有可恢复到80的新基线,可以从80登记;不许只把失败请求的50改成80就宣称补齐了历史。

登记者读到H40之后暂停,GC推进H80,再尝试登记50,原子重检应拒绝;若登记50先成功,则GC最多到40。取消一个名为late的消费者再重建,令牌代际必须变化。用旧令牌读取、推进和宣告完成,三者均被拒绝;新令牌的pin仍留在80。

最后把所有消费者完成/取消,F=160时应允许回收全部四个完整段。再把稳定尾端改为155:逻辑R可以到155,但H最多120,未满的[120,160)不能被当作完整稳定段删除。这是日志域进度与段几何的区别。

六、说明你的证据覆盖到哪里 ​

交付内容至少包含两条保存点分支、四页copy/redo/undo表、两个恢复目标、缺日志和早目标的拒绝理由、三次前沿表及持久顺序反例。保存点测试用原始值快照作oracle,备份测试选择独立页前缀再与提交后的逻辑结果比较,日志保留测试直接构造消费者所需位置集合;不能让三个检查都只复述同一段实现。

本单元假定原子页写、完整日志、可信生产者和可恢复注册表。模型没有实现文件分配、物理损坏修复、跨历史分支选择或分布式未决事务。把这些排除项写清,才知道哪份恢复证书可以复用,哪一步还需要新的机制。