Operational Transformation算法图解

Operational Transformation算法解决的问题是如何merge基于相同的状态产生的不同的操作序列。如下图所示,从上往下看,基于相同的起点,左右有两个操作OP1和OP2.为了merge两个操作为一体,我们可以从两个方向入手,一个方向是从OP1入手,在执行完OP1后,执行OP2;另一个方向是从OP2入手,在执行完OP2后,执行OP1.但是,简单的将操作执行,并不正确,以OP1为例,在执行完OP1后,数据的状态发生了变化,而OP2是基于初始的状态,所以不能直接执行OP2,而需要将操作做一个变换,以OT(OPA,OPB)作为记号。使用OT算法后,必须保证,左边的执行序列OP1,OT(OP2,OP1)执行后的结果,与右边的执行序列OP2,OT(OP1,OP2)执行后的结果相同。Operational Transformation算法图解_第1张图片

上图是对一个【元操作】的定义。真是的场景中,左边和右边不可能仅仅有一个操作,而是有多个操作。我们先考虑左边有两个操作,右边只有一个操作。如下图所示。

Operational Transformation算法图解_第2张图片

OP1和OP3最先merge。图中到达中轴线的节点。之后,需要将OP2merge进去,必须执行OT(OP2,OP3‘)== OT(OP2, OT(OP3, OP1))

再考虑左边有三个操作,右边只有一个操作。如下图所示。

Operational Transformation算法图解_第3张图片

按照左上-右下的辅助线,把merge过程切割成若干步骤。第一步是在右操作为OP4时mergeOP1,第二步是在右操作为OP4‘时mergeOP2.第三步是在右操作为OP4‘’时mergeOP3。以此类推,可以将merge【左N右1】过程理解成一个递归算法。


上文求解的是左边有若干操作,右边只有一个操作的情形。我们继续扩展,考虑左边有若干操作,右边也有若干操作的情形。如下图所示。


Operational Transformation算法图解_第4张图片

按照右上-左下的辅助线,我们可以将这种求解分割成若干步骤的merge【左N右1】。第一步,左N指的是OP1,OP2,右1指的是OP3.第二步,左N指的是OP1‘,OP2’,右1指的是OP4.以此类推,我们可以将merge【左N右M】的过程理解成一个递归算法。


至此为止,我们将最通用的merge【左N右M】的算法一步步分解下来。下面,我们再考虑一个基本问题,OT(OPA,OPB)指的是基于OPB,对OPA做转换,转换的结果是OPA‘。如果转换的结果不是一个操作OPA’,而是一个操作序列「OPA1,OPA2……」呢?如下图所示。


Operational Transformation算法图解_第5张图片

OT(OP4,OP1)的结果是「OP41,OP42」。求解步骤,首先是求解出OP1‘,将问题缩小为左边「OP2,OP3」,右边为「OP41,OP42」。然后将问题抽象为【左2右2】,先求解【左2右1】,得到「OP2’,OP3‘」;然后将问题缩小为【左2右1】,左为「OP2‘,OP3’」,右为OP42.最终求出结果。

现在,我们分析了所有的问题种类。我们可以分析一个具体问题。

在真实场景中,操作OPA和操作OPB可能是互斥的。因此,OT算法往往是biased OT,即以某一个方向为倾向,假设我们的OT是向右方向biased,为了保证对于互斥的操作,OT算法的正确性,我们可以定义如图所示的【元操作】。


Operational Transformation算法图解_第6张图片

因为现在的OT是向右方向倾斜的,所以对于互斥的操作,OP2更优先,所以,只能将OP1变换成空操作(null),而OT(OP2,OP1)必须撤销OP1的影响,并施加OP2的影响。所以我们引入Reverse()操作,即求一个操作的逆操作。

在merge【左N右M】的情形下,出现互斥的情况,就可以使用上述的元操作进行解决。

行文至此,我们通过引入Reverse()操作,解决了互斥操作的OT问题。我们继续加以引申,既然我们接受并引入了Reverse()操作,OT算法本身就变得足够灵活,可以应对更复杂的操作对的OT.考虑操作OPA和OPB。有属性如下:若OPA已执行,则OPB不可执行;若OPB已执行,则OPA可执行。正常的OT示意图如下图所示:


Operational Transformation算法图解_第7张图片

显然,该OT组合是错误的。为了使OT组合正确,我们可以使用Reverse()功能。


Operational Transformation算法图解_第8张图片

如图所示,我们可以发现,只要求出了右方向的OPA’,那么我们就可以通过Reverse()操作和该OPA',组合成操作序列{Reverse(OPA), OPB, OPA'},该序列就是左方向的OT(OPB, OPA)的结果.

将该结论推广开,我们完全只需要提供单个方向的OT变换方法,就可以自动得到另一个方向的OT变换方法。但是,代价也是高昂的,那就是,普通的OT变换结果还是一个操作,而上述方法的结果是三个操作,导致performance会有成倍的下降。实践中,这种tradeoff往往发生在某个OT方法过于复杂时。

OT算法自诞生以来已经超过了三十年,在协同编辑的产品中广泛应用,各种开源实现在GitHub上都能找到,Google spreadsheet也是基于这项技术。

先有Operation,再有Operational Transformation。OT算法实现的优劣,首先依赖于Operation设计的优劣。对于协同编辑产品的操作,我以为,应该是面向OT的设计,即在设计时,本着更有利于OT算法的实现的原则为第一,有利于减少网络传输量的原则为第二。当然,在设计时,更要遵从基本原则,即操作A与操作B应该是正交关系,要尽量避免操作定义中的冗余信息。

OT算法是一个计算密集型的算法,而应用在协同编辑产品中,协同编辑又讲究high responsiveness,这就造成了一个天然的矛盾。尤其在mobile的协同编辑产品中,mobile的计算能力相比桌面产品更加有限。所以,我以为,OT算法的实践尤其需要调优。我们测试了iOS版本的Google spreadsheet产品,在长度为8的操作序列与长度为8的操作序列merge时,就可以出现明显的UI卡顿。

另外,对Google spreadsheet的测试,还可以发现Google的优化思路。协同编辑产品都支持撤销与重做,而撤销与重做的操作也是需要进行OT变换的,这意味着需要一定的CPU消耗。Google spreadsheet 对于撤销操作的OT明显是onDemand,即在用户点击撤销按钮时,才会对于撤销操作执行OT,这样可以避免执行过多的OT操作,因为用户完全有可能并不执行任何撤销操作。





你可能感兴趣的:(Operational Transformation算法图解)