Raft
This is a very abstraft model of Raft that considers only
operations on logs and not leader election.
this is the prefix order on logs
relation (X:log < Y:log)
instantiate prefix_strict_order(log,<)
a measure function on logs, totally ordered
type weight
relation (X:log < Y:log)
instantiate total_strict_order(weight,<)
individual meas(X:log) : weight
axiom (X:log < Y:log) -> (meas(X) < meas(Y))
axiom meas(X) = meas(Y) -> X = Y
protocol nodes and sets of nodes
type node
type node_set
relation mem(N:node,S:node_set)
is a set a majority?
two majorities must have a node in common
axiom maj(S1) & maj(S2) -> exists N. mem(N,S1) & mem(N,S2)
term numbers
type term
relation (X:term < Y:term)
instantiate total_strict_order_with_zero(term,<,0)
abstract operations on logs
type elem
object logs = {
individual get_term(l:log) : term
action extend(l:log,e:elem,t:term) returns(lp:log) = {
assert l < lp & get_term(lp) = term
}
}
object proto = {
individual node_logs(X:node) : log
individual node_terms(X:node) : term
relation leader(T:term,X:node)
init ~leader(T,X)
init node_terms(X) = 0
action extend(n:node, e:elem) = {
assume leader(node_terms(n),n);
node_logs(n) := logs.extend(node_logs(n),e,node_terms(n))
}
action send_log(leader:node, dominated:node_set) = {
assume maj(dominated);
assume mem(X,dominated) -> (meas(node_logs(X)) < meas(node_logs(leader))
| meas(node_logs(X)) = meas(node_logs(leader)));
call abs_net.send_log(leader,node_logs(leader))
}
action receive_log(dst:node, leader:node, l:log) = {
if meas(node_logs(dst)) < meas(l) {
node_logs(dst) := l;
call abs_net.send_ack(dst,l)
}
}
This means log L has been committed by majority S
relation committed(L:log,S:node_set)
init ~committed(L,S)
to commit a log, we mus have a majority of nodes
that agree with the leader
action commit(leader:node, dominated:node_set) = {
assume maj(dominated);
assume mem(X,dominated) -> node_logs(X) = node_logs(leader);
committed(node_logs(leader),dominated) := true
}
conjecture committed(L,S) -> maj(S)
conjecture committed(L,S) & mem(N,S) -> (L < node_logs(N) | L = node_logs(N))
conjecture committed(L:log,S:node_set) & mem(N:node,S) & ~(L < node_logs(M:node) | L = node_logs(M:node)) -> meas(node_logs(M:node)) < meas(node_logs(N:node))
conjecture ~(meas(node_logs(NO0)) < meas(LO1) & abs_net.sent(NO0,LO1))
conjecture committed(L:log,S:node_set) & mem(N:node,S) & abs_net.sent(M,L2) & ~(L < L2 | L = L2) -> meas(L2) < meas(node_logs(N:node))
}
object abs_net = {
this says leader N has sent log L
relation sent(N:node,L:log)
init ~(sent(N,L))
action send_log(leader:node, l:log) = {
sent(leader,l) := true
}
action send_ack(n:node, l:log) = {
TODO: fill this in
}
action receive_log(dst:node, leader:node, l:log) = {
assert sent(leader,l)
}
execute receive_log before proto.receive_log
}
export proto.extend
export proto.send_log
export proto.receive_log
export proto.commit
isolate iso_p = proto with abs_net,logs