6.多Master的operation transfer系统
我们转向研究多master的operation-transfer系统。Operation-transfer系统比state-transfer系统要复杂许多,因为副本为了能达到一致必须在操作集以及操作顺序上达成一致。一个operation-transfer系统工作的基本流程:
1)副本从一个公共的初始状态开始
2)每个Master节点维护一个log来记录本地提交的更新操作。除此之外,每个节点(无论是否为Master节点)从所有Master节点收集操作日志,存放在multi-log文件。6.1节将会详细讨论logging算法。
(思考:上面第二点表明每个Master节点将会有一个log文件和一个multi-log文件,非Master节点将只会有multi-log文件)
3)Master从本地用户节点后更新操作,本地应用,并将他们添加到log和multi-log文件。一个副本节点从远端Masters接收操作,应用他们,并添加到multi-log中。6.2节将更加详细地讨论更新传播。
(思考:log和multi-log中记录的操作是否记录修改的内容?这一点很重要。
如果记录修改的内容,那么进行重新执行就可以恢复到文件的任何状态。
如果不记录修改的内容,当根据log进行更新时,会存在operation对应的文件其数据已经变化,不再是这个operation所做的修改。)
4)正确的更新应用顺序,叫做schedule,被副本节点逐渐的了解,通常与更新接收的顺序并不一致。当一个站点添加请求到multi-log,它将重新计算schedule(调整到它所知的最好结果),执行schedule, 可能会取消或重做已经应用到副本上的操作,从而使得其达到下一个短暂的一致状态,。从第七章开始到7.3节将会讨论调度算法。
(思考:节点接收更新,进行调度,然后执行。这三步是一个整体,一个周期,循环执行,还是说这三个步骤各自独立,可分别独立运行?无论是一个整体,还是各个部分独立,不同的副本节点收集到的更新都会是不同的。所以副本节点所执行的调度仅仅是它所知的最好调度结果。
调度还是有一定意义,比如文件大量的创建后紧跟着这些文件的删除,如果没有调度,所有这些操作的执行是十分的耗时的。)
5)选择性,当发现操作冲突,节点将会解决他们。第七章讨论冲突处理。
(思考: 发现操作冲突?是发现操作之间的冲突。因为前面提到了节点会将本地进行的更新也记录到multi-log中,加上multi-log还会拥有其他收集其他所有节点的更新,这样multi-log将会拥有所有的更新,只用发现这些操作之间的冲突就是全部的冲突了。
说明:我们的Robin记录了操作,但并不去发现这些操作之间的冲突。因为这个冲突发现十分复杂,计算量很大。这里的冲突发现计算,等效于在一个N个元素的集合中,发现任意m个元素是冲突的,m从2取值到N,这个计算是不可行的。)
6)当所有节点对一个操作集合的schedule达成一致,他们将把操作提交到state,从multi-log中移除这些操作的记录,然后重头执行这个算法。7.5节将讨论提交算法。
(思考: 所有的节点对一个操作集合的schedule达成一致,这在一个节点频繁加入离开的网络不能。同时这也是矛盾的,因为每个节点能收集到的operation是不一样的啊,如果operation的集合元素都不一样,又怎么能在这些元素的schedule上获得一致呢?
前文中提到epidemic 传播,Any site communicates with any other and transfers both its own updates and those received from other sites. This protocol eventually broadcasts every update to all sites whatever the network topology, including when connectivity is poor or incomplete.
虽然能让每个节点都获得所有的更新,但这是最终将获得所有的更新,中间状态不是。所以中间状态的调度很难实现所有的节点能达成一致。
“重头执行这个算法”让我认为这里operation-transfer系统是基于这是一个同步系统的假设。这对我们一点也不适用。我们需要处理的是异步的系统。)
(思考:从目前来看这篇文章来看应该讨论的是异步系统,但是operation-transfer的同步过程却十分像同步系统,这一点让我非常困惑。我们思考的角度应该是异步系统,采用基于队列的方法来获取一致性。)
6.1构造一个日志
一个log可以被看做一个过程,将共享对象从初始状态产生出另一个暂时的一致状态。当设计logging算法时从三个方面进行选择:
1) 记录日志的抽象层次
2) 是否清理冗余的操作
3) 是否记录或重建用户操作
第一个选择是日志记录操作的抽象层次。两种极端情况,一种日志仅记录字节区域的读与写,基本不包含语义信息。一种记录高层的用户操作,富于表达并简明清晰,但对于推理却十分复杂。建议记录简单的操作(便于推理),聚合起来表达高层次的意图(表现力)。
日志如果不含冗余操作,被称为clean;没有更短的日志,可以从初始状态产生中间状态。一个clean log不仅是否有效并容易推理。考虑一个写作的锯齿谜题。假设用户A将X放到某个位置。同时用户B将X放到不同的位置,过会儿他改变了想法并移除它。如果B的log是clean的,它将不会包含对X的移动,系统将会很容易判断这些操作是没有冲突的。
最后选择,是否将操作按他们被期望的方式记录,以及是否根据当前状态和初始状态的不同来重建一个可能的操作历史。
(思考:这一点很重要,我们需要这样做,因为操作log不能无限增长啊。长时间未同步的节点必须通过状态比对来构建可能的操作历史)
通过拦截恰当层次的用户行为,可以记录任意层次的操作日志,但它必须在一个独立干净通路上实现。与此相反,diffing可以在对象编辑者之外实现,同时它的日志自然是clean的;但它需要十分清楚编辑者的修改意图。
6.2使用Timestamp向量的更新传播
Operation-transfer系统必须将所有的更新传播到所有的节点,即使在某一时刻并不是所有节点都可达(比如,epidemic更新传播,在第1章中已定义)。一个朴素的解决办法:让每个节点周期性的发送他整个multi-log到其它节点。假设有足够的时间,让站点进行至少间接的通信,这个方法最终将传播所有的更新到所有的节点。它有一个明显的不足,无法控制的磁盘开销和网络带宽消耗,因为它将聚集不断增多的更新。
在Operation-transfer系统中,解决方法时采用timestamp向量。站点i记录M个元素的时间戳向量TVi。TVi[i]保存在站点i发生的操作数,i.e.,即站点i的log数。TVi[j]表示站点i接收到的来自站点j的最新更新。TVs的不同,可以准确的标记出需要进行交换的更新集合,从而使他们一致。
Fig.6. 副本节点一致采用timestamp向量。接收端先调用发送者的“SendUpdate”过程并传入自己的timestamp向量。发送者发送更新给接收者,接收者在“ReceiveUpdates”过程中处理他们。
图6表示采用timestamp向量的更新传播过程。从站点i传播更新到站点j,站点i向接收j的timestamp向量,TVj。站点i一个元素一个元素地比较TVi和TVj。If ∃k,TVi[k] > TVj[k],站点i从它的multi-log中抽出发生在站点k的时间戳大于TVj[K]的操作,并发他们发送给j。这种方法保证站点j能收到所有保存在站点i的更新,同时站点j将不会收到它已经拥有的更新。然后交换角色,让站点i接收来自站点j的更新,这样两个站点将会收到相同的更新集合。