我一直在尝试编写自己的 Javascript 编辑器,其功能类似于 Google Docs(允许多人同时处理)。我不明白的一件事:
假设您已经让用户 A 和用户 B 直接相互连接,网络延迟为 10 毫秒。我假设编辑器使用差异系统(正如我理解 Docs 所做的那样),其中编辑表示为“在索引 3 处插入'文本'”,并且差异带有时间戳并强制由所有客户端按时间顺序应用。
让我们从包含以下文本的文档开始:“xyz123”
用户 A 在文档开头的时间戳 001ms 处键入“abc”,而用户 B 在时间戳 005ms 处在“xyz”和“123”之间键入“hello”。
两个用户都希望结果是:“abcxyzhello123”,但是,考虑到网络延迟:
- 用户 B 将在 011 毫秒时收到用户 A 对“在索引 0 处插入 'abc'”的编辑。为了保持时间顺序,用户 B 将撤消用户 B 在索引 3 处的插入,在索引 0 处插入用户 A 的“abc”,然后在索引 3 处重新插入用户 B 的插入,现在位于“abc”和“xyz”之间”,因此给出“abchelloxyz123”
- 用户 A 将在 015 毫秒时收到用户 B 对“在索引 3 处插入 'hello'”的编辑。它将识别出用户 B 的插入是在用户 A 之后完成的,并且只需在索引 3 处插入“hello”(现在在“abc”和“xyz”之间),给出“abchelloxyz123”
当然,“ abchello xyz123”与“ abc xyz hello 123”不一样
除了从字面上为每个字符分配自己的唯一 ID 之外,我无法想象 Google 将如何有效地解决这个问题。
我想到的一些可能性:
- 跟踪插入点而不是发送带有差异的索引会起作用,但如果用户 B 在编辑前 1 毫秒移动了他的插入点,则会遇到完全相同的问题。
- 您可以让用户 B 发送一些带有他的差异的信息,例如“在 'xyz' 之后插入”,以便用户 A 可以智能地识别这已经发生,但是如果用户 A 插入文本“xyz”怎么办?
- 用户 B 可以识别这已经发生(当它接收到用户 A 的 diff 并看到它是冲突时),然后发送一个 diff 撤消用户 B 的编辑和一个插入用户 B 的 "hello" "abc".length 索引的新 diff正确的。这样做的问题是(1)用户 A 会在文本中看到“跳转”,并且(2)如果用户 A 继续编辑,那么用户 B 将不得不不断修复其差异 - 即使“修复器”差异也会关闭并需要修复,成倍增加复杂性。
- 用户 B 可以连同它的 diff 一起发送一个属性,它收到的最后一个时间戳 diff 是 -005ms 或其他东西,然后 A 可以识别 B 不知道它的变化(因为 A 的 diff 是在 001ms)然后进行冲突解决。问题是(1)考虑到大多数计算机时钟不精确到毫秒,所有用户的时间戳都会略有偏差;(2)如果有第三个用户 C 与用户 A 有 25 毫秒的延迟,但与用户 B 有 2 毫秒的延迟,并且用户 C 在 -003ms 在“x”和“y”之间添加了一些文本,然后用户 B 将使用用户 C 的编辑作为参考点,但用户 A 不知道用户 C 的编辑(因此用户 B 的参考点)直到 22 毫秒。我相信如果您使用通用服务器为所有编辑添加时间戳,则可以解决此问题,但这似乎相当复杂。
- 您可以给每个角色一个唯一的 ID,然后使用这些 ID 而不是索引,但这似乎有点矫枉过正......
我正在阅读http://www.waveprotocol.org/whitepapers/operational-transform,但很想听听解决此问题的所有方法。