69、支持撤销操作的可扩展 XML 协同编辑

支持撤销操作的可扩展 XML 协同编辑

1. 引言

在云或对等网络等大规模基础设施中,数据会被复制以确保可用性、效率和容错性。数据作为信息系统的核心,副本的一致性是关键问题。传统确保强一致性(如线性或原子一致性)的机制扩展性不佳,因此现代大规模基础设施依赖最终一致性。

可交换复制数据类型(CRDT)是一种新算法,能确保复制数据的可扩展一致性,无需复杂的并发控制,已成功应用于文本协同编辑,但尚未用于半结构化数据类型。可扩展标记语言(XML)广泛用于信息系统,是数据交换的标准格式。

协同编辑有诸多优势,如获取不同观点、缩短任务完成时间、得到更准确的最终结果。如今,协同编辑已成为日常生活的一部分,例如维基百科用户在几年内生成了 1500 万篇文章。

撤销操作是协同编辑的重要特性,可帮助用户从错误和破坏行为中恢复,解决编辑冲突。然而,设计撤销功能并非易事,它需支持全局选择性撤销,且从用户角度看应是正确的,即系统要回到未执行该操作的状态。

我们提出一种用于协同编辑的 XML CRDT,能处理 XML 树的元素子节点和属性。元素子节点列表的顺序处理方式与线性结构 CRDT 相同,元素属性采用最后写入者获胜规则。通过记录属性的先前值和元素上的操作,并计算并发撤销和重做操作来实现撤销功能,同时还提出了垃圾回收机制来清理旧操作。

2. 技术现状
  • 操作转换(OT)方法 :是一种基于操作的复制机制,依赖通用集成算法和特定于复制数据类型的转换函数。一些集成机制使用状态向量(存在撤销时使用上下文向量)来检测操作间的并发,不适合大规模基础设施。
  • Ignat 等人的方法 :将使用反熵的集成机制与特定转换函数结合以实现 P2P XML 协作,但该方法用墓碑替换编辑文档中删除的元素以确保一致性,导致文档无限制增长,且不支持撤销。
  • Martin 等人的方法 :提出的 XML 树协调机制与 CRDT 类似,并发操作无需转换即可交换,但不处理 XML 元素属性,使用状态向量限制了可扩展性,且无撤销功能。
  • 数据管理领域的相关工作 :部分工作假定存在确保复制内容一致性的协议,但未明确定义;还有工作提出的并发修改合并算法仅适用于集中式环境。
3. 无撤销功能的 XML CRDT

在协同编辑器中,为确保本地修改的可扩展性和高响应性,数据需复制。这种复制是乐观的,本地修改会立即执行,允许副本在短时间内出现差异,但系统最终要保证一致性,即所有修改交付后,副本内容相同。

CRDT 是一种数据类型,所有并发操作可交换,无论操作交付顺序如何,结果文档都相同,从而确保最终一致性。

我们将 XML 协同编辑器视为一组网络节点,每个节点最多托管一个共享 XML 文档的副本。本地修改立即执行并传播到其他副本,假设每个副本最终都会收到所有修改。

将 XML 树视为边 e,包含三个元素:
- e.identifier :边的唯一标识符(时间戳)。
- e.children :边的子节点(边的集合)。
- e.attributes :边的属性(字符串到值的映射)。映射的键是属性名,值 av 包含 av.value (属性的当前值)和 av.timestamp (属性的当前时间戳)。

影响 XML 树的基本操作如下:
- Add(idp,id) :在边 idp 下添加标识符为 id 的边,该边为空,无标签名、子节点或属性。
- Del(id) :删除标识符为 id 的边。
- SetAttr(id,attr,val,ts) :将值 val 和时间戳 ts 设置到标识符为 id 的边的属性 attr 上,将属性值设置为 nil 表示删除该属性。

为使 Add Del 操作可交换,使用唯一时间戳标识符。每个副本由唯一标识符 s 标识,每个操作由时钟 hs (逻辑时钟或挂钟)标识,标识符 id 是对 (hs : s)

为使 SetAttr 操作可交换,采用最后写入者获胜技术,为每个属性关联时间戳 ts ,仅当远程 SetAttr 的时间戳高于属性关联的时间戳时才应用。时间戳是全序的,时钟松散同步。

特殊属性:
- @tag @position :包含边的标签名和位置,不能为 nil @position 用于对节点的子节点排序,需唯一、全序且密集。
- @text :用于建模文本边,若该属性有值 v ,则边被视为内容为 v 的文本边。

相关算法如下:

deliver (Add(idp,id),t) :
    edge p = find(idp,t), e = new edge(id);
    if p ̸= nil then p.children = p.children ∪{e};
end
deliver (Del(id),t) :
    edge p = findFather(id,t), e = find(id, p);
    if p ̸= nil then p.children = p.children\{e};
end
deliver (SetAttr(id,attr,val,ts),t) :
    edge e = find(id,t);
    if e ̸= nil and (e.attributes[attr] = nil or e.attributes[attr].timestamp < ts) then
        e.attributes[attr].value = val;
        e.attributes[attr].timestamp = ts;
    endif
end
4. 支持撤销功能的 XML CRDT

从用户角度实现正确的撤销功能并非易事。例如,用户添加元素,然后删除该元素,接着撤销添加操作,同时有两个不同用户撤销删除操作。最终,由于添加和删除操作都被撤销,节点应不可见,且在每个副本上无论操作交付顺序如何都应如此。若使用 Del 撤销 Add ,根据操作接收顺序会得到不同结果,这违反了最终一致性。

为实现满意的撤销功能,我们记录每个边的所有操作信息,并计算操作的效果计数器:1 减去撤销次数加上重做次数。若效果计数器大于 0,则操作有效果。元素可见的条件是添加操作的效果计数器大于 0,且没有效果计数器大于 0 的删除操作。属性的值由效果计数器大于 0 的最近值决定。

有撤销功能时,边属性 e.attributes[attr] 变为值 v 的有序列表,每个值包含三个元素:
- v.value :属性的值(字符串)。
- v.timestamp :与该值关联的时间戳。
- v.effect :该值的效果计数器(整数)。

列表按时间戳排序, @add 特殊属性只有一个与边标识符相等的时间戳关联的值, @del 特殊属性存储应用于边的删除操作的时间戳列表。

相关算法如下:

deliver (Add(idp,id),t) :
    edge p = find(t,idp), e = new edge(id);
    p.children = p.children ∪{e}
    add(e.attributes[@add], new value (nil,id,1));
end
deliver (Del(id,ts),t) :
    edge e = find(t,id);
    add(e.attributes[@del], new value (nil,ts,1));
end
deliver (SetAttr(id,attr,val,ts),t) :
    edge e = find(t,id);
    add(e.attributes[attr], new value (val,ts,1));
end
deliver (Undo(Add(idp,id)),t) :
    increment(t,id,@add,id,−1);
end
deliver (Undo(Del(id,ts)),t) :
    increment(t,id,@del,ts,−1);
end
deliver (Undo(SetAttr(id,attr,val,ts)),t) :
    increment(t,id,attr,ts,−1);
end
function increment(t,id,attr,ts,delta)
    edge e = find(t,id);
    value v = get(e.attributes[attr],ts);
    v. effect += delta;
end

在示例中,每个副本上添加和删除操作的效果计数器都小于等于 0,因此这些操作对 XML 树无影响,边不可见。

由于上述模型包含墓碑和操作信息,不能直接供应用程序使用。应用程序不应看到墓碑,每个属性只能有一个值。节点可见的条件是效果计数器满足一定要求。

以下是一个简单的流程图,展示了基本操作的处理流程:

graph TD;
    A[开始] --> B{操作类型};
    B -->|Add| C[查找父节点];
    B -->|Del| D[查找父节点和边];
    B -->|SetAttr| E[查找边];
    C --> F[添加边到父节点];
    D --> G[从父节点移除边];
    E --> H{属性条件};
    H -->|满足| I[更新属性值和时间戳];
    F --> J[结束];
    G --> J;
    I --> J;
    H -->|不满足| J;

下面是一个表格,总结了不同方法的特点:
| 方法 | 并发检测方式 | 处理元素属性 | 支持撤销 | 扩展性 |
| ---- | ---- | ---- | ---- | ---- |
| OT 方法 | 状态向量或上下文向量 | 可能支持 | 可能不支持 | 不佳 |
| Ignat 等人的方法 | 反熵 | 可能支持 | 不支持 | 一般 |
| Martin 等人的方法 | 状态向量 | 不支持 | 不支持 | 受限 |
| 我们的方法 | 唯一时间戳 | 支持 | 支持 | 较好 |

5. 撤销操作的详细分析

撤销操作在协同编辑中起着至关重要的作用,它能够帮助用户纠正错误、解决冲突,提升编辑体验。为了更深入地理解撤销操作的实现机制,我们对其进行详细分析。

5.1 效果计数器的作用

效果计数器是实现撤销操作的核心概念。它记录了每个操作的实际影响,通过计算“1 减去撤销次数加上重做次数”来确定操作是否生效。以下是不同操作效果计数器的具体影响:
- 添加操作(Add) :当添加操作的效果计数器大于 0 时,元素才会在 XML 树中可见。例如,若添加操作的效果计数器初始为 1,执行一次撤销操作后,效果计数器变为 0,此时元素将不再可见。
- 删除操作(Del) :若删除操作的效果计数器大于 0,且添加操作的效果计数器不大于 0,元素将被从 XML 树中移除。
- 属性设置操作(SetAttr) :属性的值由效果计数器大于 0 的最近值决定。这意味着,若一个属性有多个设置操作,只有效果计数器大于 0 的最新操作所设置的值才会生效。

5.2 撤销操作的执行流程

撤销操作的执行流程主要包括以下几个步骤:
1. 识别操作 :根据操作的类型(Add、Del、SetAttr)和相关标识符(id、attr、ts 等),确定要撤销的具体操作。
2. 更新效果计数器 :调用 increment 函数,将对应操作的效果计数器减 1。例如,撤销添加操作时,调用 increment(t,id,@add,id,−1)
3. 重新计算元素可见性和属性值 :根据更新后的效果计数器,重新判断元素是否可见以及属性的值。若添加操作的效果计数器变为 0,元素将不可见;若属性设置操作的效果计数器变为 0,需根据其他效果计数器大于 0 的操作来确定属性值。

以下是撤销操作执行流程的 mermaid 流程图:

graph TD;
    A[开始] --> B{操作类型};
    B -->|Add| C[识别添加操作];
    B -->|Del| D[识别删除操作];
    B -->|SetAttr| E[识别属性设置操作];
    C --> F[更新添加操作效果计数器];
    D --> G[更新删除操作效果计数器];
    E --> H[更新属性设置操作效果计数器];
    F --> I[重新计算元素可见性];
    G --> I;
    H --> J[重新计算属性值];
    I --> K[结束];
    J --> K;
6. 垃圾回收机制

在支持撤销操作的系统中,随着操作的不断进行,会产生大量的历史操作信息,这些信息会占用大量的存储空间。为了优化系统性能,我们引入了垃圾回收机制,用于清理旧的操作信息。

6.1 垃圾回收的触发条件

垃圾回收机制在以下情况下触发:
- 存储空间达到阈值 :当系统的存储空间使用量达到预设的阈值时,触发垃圾回收操作,以释放空间。
- 操作历史记录过长 :当某个边的操作历史记录数量超过一定限制时,对该边的操作记录进行清理。

6.2 垃圾回收的执行步骤

垃圾回收的执行步骤如下:
1. 遍历操作记录 :对每个边的操作记录进行遍历,检查每个操作的效果计数器。
2. 筛选无效操作 :将效果计数器为 0 的操作标记为无效操作,这些操作对当前 XML 树的状态没有影响。
3. 删除无效操作 :从操作记录中删除标记为无效的操作,释放存储空间。

以下是垃圾回收机制的伪代码:

function garbageCollection(t)
    for each edge e in t
        for each attr in e.attributes
            for each value v in e.attributes[attr]
                if v.effect == 0
                    remove v from e.attributes[attr];
                end
            end
        end
    end
end
7. 总结与展望

本文提出了一种支持撤销操作的可扩展 XML 协同编辑方法,通过引入 CRDT 数据类型和效果计数器机制,实现了高效的协同编辑和正确的撤销功能。以下是对本文方法的总结:
- 创新性 :采用唯一时间戳标识符和最后写入者获胜技术,确保操作的可交换性和最终一致性;引入效果计数器机制,实现了满意的撤销功能。
- 优势 :具有良好的扩展性,适用于大规模基础设施;支持全局选择性撤销,满足用户的多样化需求。

未来的研究方向可以包括:
- 性能优化 :进一步优化算法,减少存储空间的使用和操作的处理时间,提高系统的性能。
- 安全性增强 :加强系统的安全性,防止数据泄露和恶意操作,保障协同编辑的可靠性。
- 多模态支持 :支持更多类型的协同编辑操作,如多媒体数据的编辑和共享,提升系统的功能和适用性。

以下是一个列表,总结了本文方法的主要特点和未来研究方向:
- 主要特点
- 可扩展的协同编辑
- 支持撤销操作
- 最终一致性保证
- 未来研究方向
- 性能优化
- 安全性增强
- 多模态支持

通过不断的研究和改进,我们相信这种支持撤销操作的可扩展 XML 协同编辑方法将在实际应用中发挥更大的作用,为用户提供更加高效、便捷的协同编辑体验。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值