连续多个乐观操作叠加后,某一步失败的回滚范围需要明确边界
别名: 叠加回滚 · 回滚半径 · rollback radius · 多步乐观
概念解释
人连着做了几件乐观的事:先改标题,再加标签,再发一条评论。中间某一步被拒绝,回滚该掀掉哪几件?回滚范围(rollback scope)是事先说清的边界:只撤回这一步、撤回依赖它的后续步、还是整串都倒。范围含糊时,界面会掀错层——没失败的评论跟着消失,或失败的标题留着、后面建在错误标题上的标签还在。
这条不讨论单步回滚要不要干净,那是安放问题。这里是多步之间的半径。
机制
每一步乐观都在上一步的结果上开新分支。步骤之间有依赖:评论「回复刚才那句」依赖那句还在;标签「加到这篇」依赖这篇的身份还在。一步失败,依赖它的后续步在真实世界上没有地基,继续显示它们等于把后续也变成谎言。反过来,与失败步正交的步(改的是另一条记录)被连坐,是把一次拒绝扩大成误伤。
范围因此不是「全部回滚」或「全部保留」的口味,是依赖图上的闭包。产品必须有一张图,即使图很浅:同一对象上的后续写入依赖,跨对象不依赖。没有图,实现往往用「时间上在失败之后的都倒」——这把正交操作也 sweep 进去——或「只倒这一条字段」,把建在失败结果上的后续留成幽灵。
边界
完全独立的乐观(给三封不同邮件标星)一步失败不应碰另外两封。向导式多步若每步都已向服务器提交,失败步之后的步可能已经在对端生效,回滚半径会被服务器拒绝:「后面的已经成真,不能只在屏幕上倒」。这时范围要改口为「屏幕回到失败步,并标明后续步在对端的命运」,而不是假装整串能倒。协同里别人基于你乐观过的标题开始写正文,你的标题回滚会变成别人的冲突,半径跨出了你的会话。批量「全选后归档」是一步,不是 N 步叠加;其中若干封失败是批量部分失败,用的是另一套范围(哪些条目),不要套用步骤闭包。
怎么落地
- 给连续乐观建依赖:后续步声明自己依赖哪一次未确认写入。失败时只回滚该写入及其依赖闭包。
- 在界面上让人能看出这一次回滚带走了哪几件(「标题未保存,已撤销基于该标题的两个标签」),不要只消失不计数。
- 禁止用「失败之后发生的一切」当地图。正交操作要活下来。
- 验证:乐观改标题、立刻乐观加两个标签、再乐观发评论。让「加标签」失败。标题和评论应仍在(若它们不依赖标签),两个标签应去掉。再让「改标题」失败:依赖标题文本的后续若存在应一起回滚;一条与标题无关的已发送评论不应消失。每次只失败一步,列出带走的对象,和依赖图对账。