第一部分
基本概念解释
The Part-Time Parliament 这篇文章一开始提出的问题便是:多个立法者为了形成一个决议,会发起多轮投票表决,在满足哪些条件的情况下,形成的决议不会发生不一致的情况,所谓不一致,就是立法者 A 记录的决议和立法者 B 记录的决议冲突。更进一步的,在投票表决形成决议的过程中,哪些信息需要记录在立法者的羊皮纸上,哪些信息只需要记在立法者的小纸片上(注:羊皮纸是立法者随身携带的,提供持久化记录的能力,而当立法者离开议院时小纸片就会丢失)。
在这个比喻中,立法者形成决议的过程可以看成是分布式数据提交事务日志的过程。羊皮纸可以看成是磁盘,小纸片则是内存。
显然,让所有的立法者持久地呆在一个地方投票表决是一个解决方案,但平凡的方案是没有太大价值的,因为在分布式数据库中,服务器发生故障被认为是一定会发生的事情,所以在这个最初的模型中,希望找到一个更弱的条件,使得某些立法者在可能临时缺席的情况下(Part-Time),依然得到上述期望的结果。也正因为允许立法者缺席,所以需要多轮投票表决才能形成最终的决议。
decree:决议,需要被投票决定,例如:禁止在神庙的墙上涂鸦,在数据库中,可以看成是一次事务日志的提交
ballot:一轮投票表决
vote:一次投票行为
priests:祭司,也可以理解成立法者或议员,在每一轮投票表决中,负责给一个决议投票或者不投票
quorum:法定人数
一轮投票表决是成功的,当且仅当 quorum 中的所有 priests 都做出了投票。注意:一轮不成功的投票表决也会有一个决议。
目前为止,这些概念和定义都是非常抽象的,因为它既没有给出决议来自于哪里,也没有说出形成决议的规则是什么。
数学上的定义
为了更好地刻画这个问题,要给出这个问题在数学上的定义是什么。
一轮投票表决
B
B
B由以下四个要素组成:
B
d
e
c
B_{dec}
Bdec:一轮投票表决最终形成的决议
B
q
r
m
B_{qrm}
Bqrm:一轮投票表决的 quorum,用一个非空集合表示,集合中的元素是 priests
B
v
o
t
B_{vot}
Bvot:一轮投票表决的 quorum 中,为某项决议做出投票的 priests 集合
B
b
a
l
B_{bal}
Bbal:一轮投票表决的编号
所以,一轮投票表决是成功的,当且仅当
B
q
r
m
⊆
B
v
o
t
B_{qrm}\subseteq B_{vot}
Bqrm⊆Bvot
一次投票
v
v
v由以下三个要素组成:
v
p
s
t
v_{pst}
vpst,
v
b
a
l
v_{bal}
vbal,
v
d
e
c
v_{dec}
vdec,表示在一轮编号为
v
b
a
l
v_{bal}
vbal的投票表决中,祭司
v
p
s
t
v_{pst}
vpst做了投票,并形成了这轮投票表决的决议
v
d
e
c
v_{dec}
vdec。
对于任意一个包含多轮投票表决的集合
B
\mathcal{B}
B,集合
V
o
t
e
s
(
B
)
Votes(\mathcal{B})
Votes(B)表示它包含了这样一些元素:所有的投票
v
v
v,只要
v
p
s
t
∈
B
v
o
t
v_{pst} \in B_{vot}
vpst∈Bvot,
v
b
a
l
=
B
b
a
l
v_{bal}=B_{bal}
vbal=Bbal,
v
d
e
c
=
B
d
e
c
v_{dec}=B_{dec}
vdec=Bdec。如果用
p
p
p表示一个 priest,
b
b
b表示一轮投票表决编号,
M
a
x
V
o
t
e
(
b
,
p
,
B
)
MaxVote(b,p,\mathcal{B})
MaxVote(b,p,B)表示由祭司
p
p
p投出的,所有在
V
o
t
e
s
(
B
)
Votes(\mathcal{B})
Votes(B)中的,并且
v
b
a
l
<
b
v_{bal}<b
vbal<b的,最大的那次投票(投票表决编号最大),用数学的形式表示就是:
v
∈
V
o
t
e
s
(
B
)
:
(
v
p
s
t
=
p
)
∧
(
v
b
a
l
<
b
)
∪
{
n
u
l
l
p
}
{v\in Votes(\mathcal{B})}:(v_{pst}=p)\land(v_{bal}<b)\cup \{null_p\}
v∈Votes(B):(vpst=p)∧(vbal<b)∪{nullp}中最大的那个。
n
u
l
l
p
null_p
nullp表示该祭司之前没有做过任何投票,并约定这种情况下
v
v
v的表达是:
v
p
s
t
=
p
,
v
b
a
l
=
−
∞
,
v
d
e
c
=
B
L
A
N
K
v_{pst}=p,v_{bal}=-\infty,v_{dec}=BLANK
vpst=p,vbal=−∞,vdec=BLANK
对于非空祭司集合
Q
\mathcal{Q}
Q,
M
a
x
V
o
t
e
(
b
,
Q
,
B
)
MaxVote(b,\mathcal{Q},\mathcal{B})
MaxVote(b,Q,B)表示对于所有
p
∈
Q
\mathcal{p}\in\mathcal{Q}
p∈Q中最大的
M
a
x
V
o
t
e
(
b
,
p
,
B
)
MaxVote(b,p,\mathcal{B})
MaxVote(b,p,B)
这里还有给出最大小的定义:当
v
b
a
l
<
v
b
a
l
′
v_{bal}<v'_{bal}
vbal<vbal′则
v
<
v
′
v<v'
v<v′,若
v
b
a
l
=
v
b
a
l
′
v_{bal}=v'_{bal}
vbal=vbal′,则无法比较
v
v
v和
v
′
v'
v′,也就是这里的大小是一个偏序。
有了这些,就可以定义这样三个条件:
B
1
(
B
)
≜
∀
B
,
B
′
∈
B
:
(
B
≠
B
′
)
⇒
(
B
b
a
l
≠
B
b
a
l
′
)
B1(\mathcal{B})\triangleq \forall B,B'\in \mathcal{B}:(B\ne B')\Rightarrow (B_{bal}\ne B'_{bal})
B1(B)≜∀B,B′∈B:(B=B′)⇒(Bbal=Bbal′)
解释:
B
\mathcal{B}
B中每一轮投票表决都有唯一的编号。
B
2
(
B
)
≜
∀
B
,
B
′
∈
B
:
B
q
r
m
∩
B
q
r
m
′
≠
∅
B2(\mathcal{B})\triangleq \forall B,B'\in \mathcal{B}:B_{qrm}\cap B'_{qrm}\ne \emptyset
B2(B)≜∀B,B′∈B:Bqrm∩Bqrm′=∅
解释:
B
\mathcal{B}
B中任意两轮投票表决的 quorum 中必有至少一个公共的 priest。
B
3
(
B
)
≜
∀
B
∈
B
:
B3(\mathcal{B})\triangleq \forall B\in \mathcal{B}:
B3(B)≜∀B∈B:
(
M
a
x
V
o
t
e
(
B
b
a
l
,
B
q
r
m
,
B
)
b
a
l
≠
−
∞
)
⇒
(
B
d
e
c
=
M
a
x
V
o
t
e
(
B
b
a
l
,
B
q
r
m
,
B
)
d
e
c
)
(MaxVote(B_{bal},B_{qrm},\mathcal{B})_{bal}\ne -\infty)\Rightarrow (B_{dec}=MaxVote(B_{bal},B_{qrm},\mathcal{B})_{dec})
(MaxVote(Bbal,Bqrm,B)bal=−∞)⇒(Bdec=MaxVote(Bbal,Bqrm,B)dec)
解释:
B
\mathcal{B}
B中任意一轮投票表决
B
B
B,其 quorum 中有任意一个或多个 priest 在
B
\mathcal{B}
B中更早的一轮投票表决中投过票,则
B
B
B这轮投票表决的决议等于那些 priest 在更早轮投票表决中最晚一轮投票表决的决议。
数学上的证明
有了上述定义,就可以提出下述引理:
如果满足
B
1
(
B
)
,
B
2
(
B
)
,
B
3
(
B
)
B1(\mathcal{B}),B2(\mathcal{B}),B3(\mathcal{B})
B1(B),B2(B),B3(B),则只要保证:
(
(
B
q
r
m
⊆
B
v
o
t
)
)
∧
(
B
b
a
l
′
>
B
b
a
l
)
((B_{qrm}\subseteq B_{vot}))\land(B'_{bal}>B_{bal})
((Bqrm⊆Bvot))∧(Bbal′>Bbal),就一定有:
(
B
d
e
c
′
=
B
d
e
c
)
(B'_{dec}=B_{dec})
(Bdec′=Bdec)
也就是说,只要有一轮投票表决中所有 quorum 中的 priest 都做出了投票,假设这一轮投票表决记为
B
B
B,那么后续轮次投票表决的决议都会永远等于
B
B
B的决议。
证明过程:
对于
B
\mathcal{B}
B中任意一轮投票表决
B
B
B,记集合
Ψ
(
B
,
(
B
)
)
\varPsi(B,\mathcal(B))
Ψ(B,(B))表示在集合
B
\mathcal{B}
B中晚于
B
B
B且和
B
B
B决议不同的那些投票表决。也就是:
Ψ
(
B
,
B
)
≜
{
B
′
∈
(
B
)
:
(
B
b
a
l
′
>
B
b
a
l
)
∧
(
B
d
e
c
′
≠
B
d
e
c
)
}
\varPsi(B,\mathcal{B})\triangleq \{B'\in \mathcal(B):(B'_{bal}>B_{bal})\land(B'_{dec}\ne B_{dec})\}
Ψ(B,B)≜{B′∈(B):(Bbal′>Bbal)∧(Bdec′=Bdec)}
如果我们可以证明:若
B
q
r
m
⊆
B
v
o
t
B_{qrm}\subseteq B_{vot}
Bqrm⊆Bvot成立,则
Ψ
(
B
,
B
)
\varPsi(B,\mathcal{B})
Ψ(B,B)为空集,那就意味着引理1 是成立的。这里用了反证法,也就是假设若
B
q
r
m
⊆
B
v
o
t
B_{qrm}\subseteq B_{vot}
Bqrm⊆Bvot成立,但
Ψ
(
B
,
B
)
≠
∅
\varPsi(B,\mathcal{B})\ne \emptyset
Ψ(B,B)=∅,然后试图找出矛盾,主要的思路是,如果
Ψ
\varPsi
Ψ中存在一轮编号最小的投票表决,想办法找出一个比它更小的投票表决,依然落在
Ψ
\varPsi
Ψ中,从而引出矛盾。作者是这样证明的:
- 既然 Ψ ( B , B ) \varPsi(B,\mathcal{B}) Ψ(B,B)非空,那肯定存在一个编号最小的元素,记为 C ∈ Ψ ( B , B ) : C b a l = m i n { B b a l ′ : B ′ ∈ Ψ ( B , B ) } C\in \varPsi(B,\mathcal{B}):C_{bal}=min\{ B'_{bal}:B'\in \varPsi(B,\mathcal{B})\} C∈Ψ(B,B):Cbal=min{Bbal′:B′∈Ψ(B,B)}
- 毫无疑问, C b a l > B b a l C_{bal}>B_{bal} Cbal>Bbal
-
∵
(
B
q
r
m
∩
C
q
r
m
≠
∅
)
∧
(
B
q
r
m
⊆
B
v
o
t
)
\because (B_{qrm}\cap C_{qrm}\ne \emptyset)\land(B_{qrm}\subseteq B_{vot})
∵(Bqrm∩Cqrm=∅)∧(Bqrm⊆Bvot)
∴ B v o t ∩ C q r m ≠ ∅ \therefore B_{vot}\cap C_{qrm}\ne \emptyset ∴Bvot∩Cqrm=∅
这里是说, C C C的 quorum 中,至少有一个 priest 在 B B B那轮投票表决中投过票 -
M
a
x
V
o
t
e
(
C
b
a
l
,
C
q
r
m
,
B
)
b
a
l
≥
B
b
a
l
MaxVote(C_{bal},C_{qrm},\mathcal{B})_{bal}\ge B_{bal}
MaxVote(Cbal,Cqrm,B)bal≥Bbal
这是因为,由于第 2 步, C C C这轮投票表决发生在 B B B这轮投票表决的后面,而且 C C C的 quorum 中至少有一个 priest 在 B B B那轮投票表决中投过票,那就说明由祭司 C q r m C_{qrm} Cqrm们之前投出的所有投票中,至少有一个人次是发生在 B B B这轮投票表决的,那么由祭司 C q r m C_{qrm} Cqrm们之前投出的所有投票中最大的那次投票所属的投票表决编号至少不会比 B B B这轮投票表决编号更小。 -
M
a
x
V
o
t
e
(
C
b
a
l
,
C
q
r
m
,
B
)
∈
V
o
t
e
(
B
)
MaxVote(C_{bal},C_{qrm},\mathcal{B})\in Vote(\mathcal{B})
MaxVote(Cbal,Cqrm,B)∈Vote(B)
这表示的是:由祭司 C q r m C_{qrm} Cqrm们之前投出的所有投票中最大的那次投票不是空的,根据第 3 和第 4 步,既然投过票,那肯定不是空的。 -
C
d
e
c
=
M
a
x
V
o
t
e
(
C
b
a
l
,
C
q
r
m
,
B
)
d
e
c
C_{dec}=MaxVote(C_{bal},C_{qrm},\mathcal{B})_{dec}
Cdec=MaxVote(Cbal,Cqrm,B)dec
根据 B 3 ( B ) B3(\mathcal{B}) B3(B)的定义,并且5,这是显然的 -
B
d
e
c
≠
M
a
x
V
o
t
e
(
C
b
a
l
,
C
q
r
m
,
B
)
d
e
c
B_{dec}\ne MaxVote(C_{bal},C_{qrm},\mathcal{B})_{dec}
Bdec=MaxVote(Cbal,Cqrm,B)dec
根据 Ψ ( B , B ) \varPsi(B,\mathcal{B}) Ψ(B,B)的定义:因为 C ∈ Ψ ( B , B ) C\in \varPsi(B,\mathcal{B}) C∈Ψ(B,B),所以 C d e c ≠ B d e c C_{dec}\ne B_{dec} Cdec=Bdec -
M
a
x
V
o
t
e
(
C
b
a
l
,
C
q
r
m
,
B
)
b
a
l
>
B
b
a
l
MaxVote(C_{bal},C_{qrm},\mathcal{B})_{bal}> B_{bal}
MaxVote(Cbal,Cqrm,B)bal>Bbal
第 7 说明两个决议不同,根据 B 1 ( B ) B1(\mathcal{B}) B1(B),如果决议不同,那么形成决议的投票表决编号肯定是不同的,否则一轮投票表决会形成两个决议, B d e c B_{dec} Bdec就没有办法定义了。既然不能相同,再根据第 4 步,那就只能大于了。 -
M
a
x
V
o
t
e
(
C
b
a
l
,
C
q
r
m
,
B
)
∈
V
o
t
e
(
Ψ
(
B
,
B
)
)
MaxVote(C_{bal},C_{qrm},\mathcal{B})\in Vote(\varPsi(B,\mathcal{B}))
MaxVote(Cbal,Cqrm,B)∈Vote(Ψ(B,B))
根据第 7 步和第 8 步, M a x V o t e ( C b a l , C q r m , B ) MaxVote(C_{bal},C_{qrm},\mathcal{B}) MaxVote(Cbal,Cqrm,B)这次投票所属的投票表决编号要比 B B B这轮投票表决编号大,而且两者的决议还不同,那么 M a x V o t e ( C b a l , C q r m , B ) MaxVote(C_{bal},C_{qrm},\mathcal{B}) MaxVote(Cbal,Cqrm,B)这次投票所属的投票表决就满足 Ψ ( B , B ) \varPsi(B,\mathcal{B}) Ψ(B,B)的定义。 -
M
a
x
V
o
t
e
(
C
b
a
l
,
C
q
r
m
,
B
)
b
a
l
<
C
b
a
l
MaxVote(C_{bal},C_{qrm},\mathcal{B})_{bal}< C_{bal}
MaxVote(Cbal,Cqrm,B)bal<Cbal
根据 M a x V o t e MaxVote MaxVote的定义,这个结论没有任何问题,于是就在集合 Ψ ( B , B ) \varPsi(B,\mathcal{B}) Ψ(B,B)找到了一个元素(一轮投票表决),它的编号比 C b a l C_{bal} Cbal更小,但前面第 1 步说过, C C C是这个集合中编号最小的,现在找到了一个比它编号更小的,就发生了矛盾。 - 找到了矛盾,证明完毕。
有了这个引理,可以得到定理 1:
如果满足
B
1
(
B
)
,
B
2
(
B
)
,
B
3
(
B
)
B1(\mathcal{B}),B2(\mathcal{B}),B3(\mathcal{B})
B1(B),B2(B),B3(B),则只要保证:
(
(
B
q
r
m
⊆
B
v
o
t
)
)
∧
(
(
B
’
q
r
m
⊆
B
‘
v
o
t
)
)
((B_{qrm}\subseteq B_{vot}))\land((B’_{qrm}\subseteq B‘_{vot}))
((Bqrm⊆Bvot))∧((B’qrm⊆B‘vot)),就一定有:
(
B
d
e
c
′
=
B
d
e
c
)
(B'_{dec}=B_{dec})
(Bdec′=Bdec)
证明很简单,若
B
b
a
l
=
B
b
a
l
′
B_{bal}=B'_{bal}
Bbal=Bbal′,则显然
B
d
e
c
=
B
d
e
c
′
B_{dec}=B'_{dec}
Bdec=Bdec′;若
B
b
a
l
≠
B
b
a
l
′
B_{bal}\ne B'_{bal}
Bbal=Bbal′,则无论
B
b
a
l
<
B
b
a
l
′
B_{bal}<B'_{bal}
Bbal<Bbal′还是
B
b
a
l
>
B
b
a
l
′
B_{bal}>B'_{bal}
Bbal>Bbal′,都可以由引理1 立即得到结论。
这个定理就说明了一个事实,只要遵守
B
1
(
B
)
,
B
2
(
B
)
,
B
3
(
B
)
B1(\mathcal{B}),B2(\mathcal{B}),B3(\mathcal{B})
B1(B),B2(B),B3(B),那些 quorum 中所有 priests 都参与投票了的投票表决轮次,他们的决议一定是一样的。
可以看到,定理1 保证了决议结果的一致性,也就是在不需要所有 priests 都出席的情况,只要满足一个较弱条件,依然可以保证不会出现不一致的决议。但是形成不了决议也是没有出现不一致决议的情况之一,因此还希望保证是能往前推进形成决议的,于是有了定理2:
在保证
B
1
(
B
)
,
B
2
(
B
)
,
B
3
(
B
)
B1(\mathcal{B}),B2(\mathcal{B}),B3(\mathcal{B})
B1(B),B2(B),B3(B)成立的前提下,对于
B
\mathcal{B}
B所有的
B
B
B,取
b
,
Q
,
B
′
b,Q,B'
b,Q,B′满足
b
>
B
b
a
l
,
Q
∩
B
q
r
m
≠
∅
,
B
b
a
l
′
=
b
,
B
q
r
m
′
=
B
v
o
t
=
Q
b>B_{bal},Q\cap B_{qrm}\ne \emptyset,B'_{bal}=b,B'_{qrm}=B_{vot}=Q
b>Bbal,Q∩Bqrm=∅,Bbal′=b,Bqrm′=Bvot=Q,则
B
1
(
B
∪
{
B
′
}
)
,
B
2
(
B
∪
{
B
′
}
)
,
B
3
(
B
∪
{
B
′
}
)
B1(\mathcal{B}\cup\{B'\}),B2(\mathcal{B}\cup\{B'\}),B3(\mathcal{B}\cup\{B'\})
B1(B∪{B′}),B2(B∪{B′}),B3(B∪{B′})依然是成立的。
定理2 说明的是,投票表决的过程是可扩展的,或者说可向前推进的。作者的证明过程如下:
- B b a l ′ B'_{bal} Bbal′比 B \mathcal{B} B中任意一个 B b a l B_{bal} Bbal都大,所以 B 1 ( B ∪ { B ′ } ) B1(\mathcal{B}\cup\{B'\}) B1(B∪{B′})是成立的
- B q r m ′ B'_{qrm} Bqrm′和 B \mathcal{B} B中任意一个 B q r m B_{qrm} Bqrm交集都非空,所以 B 2 ( B ∪ { B ′ } ) B2(\mathcal{B}\cup\{B'\}) B2(B∪{B′})是成立的
- 令 B d e c ′ = M a x V o t e ( b , Q , B ) d e c B'_{dec}=MaxVote(b,Q,\mathcal{B})_{dec} Bdec′=MaxVote(b,Q,B)dec,则 B 3 ( B ∪ { B ′ } ) B3(\mathcal{B}\cup\{B'\}) B3(B∪{B′})也是成立的
第二部分
初步协议
有了第一部分中的定义和证明,下一步就可以根据
B
1
(
B
)
,
B
2
(
B
)
,
B
3
(
B
)
B1(\mathcal{B}),B2(\mathcal{B}),B3(\mathcal{B})
B1(B),B2(B),B3(B)制定一份想要的协议了,祭司们只要按照这个协议工作,就能得到期望中的结果。
首先决议是由祭司初始化提出,提出决议的时候会确定投票表决编号、决议内容和 quorum,然后由 quorum 中每一名 priest 决定是否投票给这一轮投票表决,这些行为全部由
B
1
(
B
)
,
B
2
(
B
)
,
B
3
(
B
)
B1(\mathcal{B}),B2(\mathcal{B}),B3(\mathcal{B})
B1(B),B2(B),B3(B)来决定。
要保证
B
1
(
B
)
B1(\mathcal{B})
B1(B),首先需要提出决议的 priest 记下他所使用的每一次投票表决编号,就能保证每一次都不会重复,其次为了防止两个 priests 使用了相同的投票表决编号,只需要在编号中包含 priest 名字的整型数字即可。
要保证
B
2
(
B
)
B2(\mathcal{B})
B2(B),可以让每次投票表决的 quorum 都是 priests 的多数派,也就是人数大于 1/2,因为任意两个人数大于 1/2 的 quorum 是一定有交集的。
要保证
B
3
(
B
)
B3(\mathcal{B})
B3(B),提出决议的 priest 必须要知道
M
a
x
V
o
t
e
(
b
,
Q
,
B
)
d
e
c
MaxVote(b,Q,\mathcal{B})_{dec}
MaxVote(b,Q,B)dec,也就是对于
Q
Q
Q中任意一个
q
q
q,要知道
M
a
x
V
o
t
e
(
b
,
q
,
B
)
MaxVote(b,q,\mathcal{B})
MaxVote(b,q,B)。因此,协议的最初两步一定是这样的:
- Priest p p p选择一个投票表决编号 b b b,并向一些 priest 发送 N e x t B a l l o t ( b ) NextBallot(b) NextBallot(b)消息。
- Priest q q q收到消息后,响应一个 L a s t V o t e ( b , v ) LastVote(b,v) LastVote(b,v)消息给 p p p,其中 v v v是祭司 q q q所有投出的票中,投票表决编号小于 b b b中最大的那次投票表决的投票,如果没有投过票,则返回 n u l l q null_{q} nullq
在这里就要求祭司
q
q
q在他的羊皮纸的背面上记录所有 Ta 投过的票。
在上述过程中,有可能发生新的投票表决和投票行为,所以为了保证
B
3
(
B
)
B3(\mathcal{B})
B3(B),就必须要求祭司
q
q
q们不能在
v
b
a
l
v_{bal}
vbal和
b
b
b之间的投票表决上再发生投票,也就是说当
q
q
q发送
L
a
s
t
V
o
t
e
(
b
,
v
)
LastVote(b,v)
LastVote(b,v)时,就必须承诺不会再投这样的票(
q
q
q必须在 Ta 的羊皮纸上记一些信息),于是,协议的下两步必须要是:
- 祭司 p p p收到多数派(大于 1/2)祭司的 L a s t V o t e ( b , v ) LastVote(b,v) LastVote(b,v)后,将这些多数派祭司的集合确定为 Q Q Q,然后祭司 p p p根据 B 3 ( B ) B3(\mathcal{B}) B3(B)挑选出决议 d d d,向 Q Q Q中全部祭司发送 B e g i n B a l l o t ( b , d ) BeginBallot(b,d) BeginBallot(b,d)并记录在 Ta 的羊皮纸的背面上。
- 收到 B e g i n B a l l o t ( b , d ) BeginBallot(b,d) BeginBallot(b,d),祭司 q q q决定是否为其投票(如果违背了第二步的承诺,就可能发生不为其投票的行为),如果决定为其投票,则发送 V o t e d ( b , q ) Voted(b,q) Voted(b,q)给 p p p,并且将投票记录到羊皮纸的背面。
事实上,哪怕没有违背之前的承诺,第四步中的祭司也可能不为其投票,甚至这四个步骤中的任何一步都是不一定会发生的,比如某个祭司压根就没有收到某条消息,这样投票可能会进行不下去,即使如此,因为
B
1
(
B
)
,
B
2
(
B
)
,
B
3
(
B
)
B1(\mathcal{B}),B2(\mathcal{B}),B3(\mathcal{B})
B1(B),B2(B),B3(B)并没有违反,所以根据之前的证明,一致性是不会被破坏的,而仅仅是进行不下去。翻译成分布式数据库的语言就是,即使有服务器故障,也不会造成数据不一致。
除了第三步以外的步骤,如果发送了重复消息结果也是幂等的(这里作者没有解释为什么第三步不是幂等的,我的理解是如果发生了第三步的重复发送,可能造成第四步接受者第一次为其投票,第二次不为其投票,理由是在第一次和第二次收到消息间,这名祭司收到了新的、编号更大的
N
e
x
t
B
a
l
l
o
t
(
b
)
NextBallot(b)
NextBallot(b)并承诺了对方),可以通过第三步羊皮纸背面的记录来避免重复发送消息。
最后的投票表决的结果和宣布,如下:
- 当 p p p收到 quorum 中所有祭司的 V o t e d ( b , q ) Voted(b,q) Voted(b,q),它会把决议记录在 Ta 的羊皮纸上,并且发送 S u c c e s s ( d ) Success(d) Success(d)给所有的祭司
- 其他祭司收到 S u c c e s s ( d ) Success(d) Success(d)后,将决议 d d d记录在 Ta 们的羊皮纸上。
基本协议
为了让祭司们尽量少记录些信息,在初步协议的基础上,规定只需要记录如下信息并形成一个基本协议:
- l a s t T r i e d [ p ] lastTried[p] lastTried[p]:祭司 p p p最近一次提出投票表决的编号,如果从没有提出过则是 − ∞ -\infty −∞
- p r e v V o t e [ p ] prevVote[p] prevVote[p]:祭司 p p p所有投票中,投票表决编号最大的那次投票,如果从没有投过票则是 − ∞ -\infty −∞
- n e x t B a l [ p ] nextBal[p] nextBal[p]:祭司 p p p发出的所有的 L a s t V o t e ( b , v ) LastVote(b,v) LastVote(b,v)中,最大的那个 b b b
基本协议要求祭司不再可以并行地初始化提出多个投票表决,而是只能初始化一个投票表决——
l
a
s
t
T
r
i
e
d
[
p
]
lastTried[p]
lastTried[p],一旦初始化就忽略所有其他投票表决的消息,并且会把这轮投票表决的信息记录在小纸片上。
基本协议又要求祭司在第二步中给出更强的承诺,不仅仅是不能在
v
b
a
l
v_{bal}
vbal和
b
b
b之间投票表决上再发生投票,而是不能在小于
b
b
b的投票表决上再发生投票,这个更强的承诺可能会阻止祭司在第四步中做出投票。
于是在基本协议中,六个步骤就是这样的(
l
a
s
t
T
r
i
e
d
[
p
]
lastTried[p]
lastTried[p]、
p
r
e
v
V
o
t
e
[
p
]
prevVote[p]
prevVote[p]、
n
e
x
t
B
a
l
[
p
]
nextBal[p]
nextBal[p]记录在羊皮纸上,其他需要记录的信息都在小纸片上):
- p p p选择一个投票表决编号 b , ( b > l a s t T r i e d [ p ] ) b, (b>lastTried[p]) b,(b>lastTried[p]),并将 l a s t T r i e d [ p ] lastTried[p] lastTried[p]置为 b b b,向一些 priest 发送 N e x t B a l l o t ( b ) NextBallot(b) NextBallot(b)消息。
- 祭司
q
q
q收到
N
e
x
t
B
a
l
l
o
t
(
b
)
NextBallot(b)
NextBallot(b)消息后,如果:
a). b > n e x t B a l [ p ] b>nextBal[p] b>nextBal[p],则将 n e x t B a l [ p ] nextBal[p] nextBal[p]置为 b b b,发送 L a s t V o t e ( b , v ) LastVote(b,v) LastVote(b,v),其中 v = p r e v V o t e [ p ] v=prevVote[p] v=prevVote[p]
b). b ≤ n e x t B a l [ p ] b\le nextBal[p] b≤nextBal[p],则忽略 - 祭司 p p p收到多数派(大于 1/2)祭司的 L a s t V o t e ( b , v ) LastVote(b,v) LastVote(b,v)后,将这些多数派祭司的集合确定为 Q Q Q,如果 b = l a s t T r i e d [ p ] b=lastTried[p] b=lastTried[p],则祭司 p p p根据 B 3 ( B ) B3(\mathcal{B}) B3(B)挑选出决议 d d d,向 Q Q Q中全部祭司发送 B e g i n B a l l o t ( b , d ) BeginBallot(b,d) BeginBallot(b,d)。
- 收到
B
e
g
i
n
B
a
l
l
o
t
(
b
,
d
)
BeginBallot(b,d)
BeginBallot(b,d)如果:
a). b = n e x t B a l [ p ] b=nextBal[p] b=nextBal[p],则祭司 q q q为其投票,并将 p e r v V o t e [ p ] pervVote[p] pervVote[p]置为当前投票,并发送发送 V o t e d ( b , q ) Voted(b,q) Voted(b,q)给 p p p
b). b ≠ n e x t B a l [ p ] b\ne nextBal[p] b=nextBal[p],则忽略 - 当 p p p收到 quorum 中所有祭司的 V o t e d ( b , q ) Voted(b,q) Voted(b,q),并且 b = l a s t T r i e d [ p ] b=lastTried[p] b=lastTried[p],它会把决议 d d d记录在 Ta 的羊皮纸上,并且发送 S u c c e s s ( d ) Success(d) Success(d)给所有的祭司
- 其他祭司收到 S u c c e s s ( d ) Success(d) Success(d)后,将决议 d d d记录在 Ta 们的羊皮纸上。
由于基本协议是在初步协议的基础上加了更多限制条件,因此初步协议满足的一致性结论在基本协议上一样是满足的,严格的数学证明在原文的附录中。
完全协议
基本协议在初步协议上更近了一步,可以让祭司们记尽量少的记录,仍可以保证形成决议的一致性。但是基本协议只是陈述了祭司们可以做什么,没有要求祭司们必须做什么,所以不能保证投票表决的过程是往前走的。例如没有任何一个祭司初始化提出一个投票表决显然也是遵守基本协议的,但却不会推进任何决议的形成。再例如,如果某个祭司持续不间断的初始化提出多个投票表决,推高投票表决编号,那么按照基本协议,quorum 中的祭司会因为一直承诺不为更小编号的投票而导致决议一直无法成功,同样也会造成推进不了任何决议的形成。因此我们还需要一个完全协议来解决这个问题。
根据上面的情况,显然要求祭司不能太频繁地初始化一个投票表决,而必须等前一次投票表决成功后再发起下一个,因此这里做了一个约定:议院中传递消息的 messenger 至少会在议院中停留 4 分钟,priest 则是 7 分钟,因此当祭司
p
p
p通过 messenger 发送消息给祭司
q
q
q并收到 Ta 的回应,最多需要 22 分钟(7+4+7+4)。有了这个约定,祭司
p
p
p就可以判断应不应该发起(初始化)新的投票表决。超过 22 分钟意味着要么有 messenger/priest 离开了议院,要么有另一个祭司发起了一轮编号更大的投票表决(第四步中的祭司不能违背承诺,所以不能为其投票)。对于后者来说,可以要求祭司
q
q
q在第四步把收到更大编号的
n
e
x
t
B
a
l
[
p
’
]
nextBal[p’]
nextBal[p’]发给
p
p
p,这样
p
p
p就可以在下一步使用更大编号发起投票表决,这被称为学习。
因此就可以要求祭司
p
p
p如果在 22 分钟内没有办法执行第三步或第五步,那么就初始化一个新的投票表决。如果 55 分钟内没有祭司进出议院(这里暗指没有服务器发生故障和重启),那么一定可以形成一个决议,并且写到每一个祭司的羊皮纸上。接下来无非就是如何选举出一位总统(president)来初始化投票选举。