simplified paxos added more tests
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Andre Henriques 2024-01-16 00:04:32 +00:00
parent 71472c8544
commit 0e1e13950d
3 changed files with 157 additions and 102 deletions

View File

@ -1,51 +1,3 @@
defmodule Ballot do
def init(name, number \\ 0) do
{name, number}
end
def inc(b, name \\ nil) do
{old_name, number} = b
{
if name == nil do
old_name
else
name
end,
number + 1
}
end
defp lexicographical_compare(a, b) do
cond do
a == b -> 0
a > b -> 1
true -> -1
end
end
defp diff({name1, number1}, {name2, number2}) do
diff = number1 - number2
if diff == 0 do
lexicographical_compare(name1, name2)
else
diff
end
end
def compare(b1, operator, b2) do
operator.(diff(b1, b2), 0)
end
end
# #
# #
# Possible actions # Possible actions
@ -76,7 +28,6 @@ defmodule Paxos do
processes: processes, processes: processes,
leader: nil, leader: nil,
instmap: %{}, instmap: %{},
other_values: %{},
decided: %{} decided: %{}
} }
@ -90,6 +41,7 @@ defmodule Paxos do
instmap = instmap =
Map.put(state.instmap, inst, %{ Map.put(state.instmap, inst, %{
value: value, value: value,
other_value: nil,
ballot: Ballot.init(state.name, 0), ballot: Ballot.init(state.name, 0),
aborted: false, aborted: false,
ballot_value: nil, ballot_value: nil,
@ -172,8 +124,6 @@ defmodule Paxos do
end end
{:rb_deliver, proc, {:other_propose, inst, value}} -> {:rb_deliver, proc, {:other_propose, inst, value}} ->
state = %{state | other_values: Map.put(state.other_values, inst, value)}
cond do cond do
has_finished(state, inst, true) -> has_finished(state, inst, true) ->
EagerReliableBroadcast.broadcast( EagerReliableBroadcast.broadcast(
@ -184,6 +134,7 @@ defmodule Paxos do
true -> true ->
state = has_or_create(state, inst) state = has_or_create(state, inst)
state = set_instmap(state, inst, fn map -> %{map | other_value: value} end)
prepare(state, inst) prepare(state, inst)
end end
@ -399,7 +350,7 @@ defmodule Paxos do
state.instmap[inst] == nil -> state.instmap[inst] == nil ->
state state
state.instmap[inst].value == nil and state.other_values[inst] == nil -> state.instmap[inst].value == nil and state.instmap[inst].other_value == nil ->
state state
state.instmap[inst] != nil and state.instmap[inst].has_sent_prepare -> state.instmap[inst] != nil and state.instmap[inst].has_sent_prepare ->
@ -449,7 +400,7 @@ defmodule Paxos do
a_val = a_val =
if a_val == nil do if a_val == nil do
if state.instmap[inst].value == nil do if state.instmap[inst].value == nil do
state.other_values[inst] state.instmap[inst].other_value
else else
state.instmap[inst].value state.instmap[inst].value
end end
@ -556,3 +507,47 @@ defmodule Paxos do
end end
end end
defmodule Ballot do
def init(name, number \\ 0) do
{name, number}
end
def inc(b, name \\ nil) do
{old_name, number} = b
{
if name == nil do
old_name
else
name
end,
number + 1
}
end
defp lexicographical_compare(a, b) do
cond do
a == b -> 0
a > b -> 1
true -> -1
end
end
defp diff({name1, number1}, {name2, number2}) do
diff = number1 - number2
if diff == 0 do
lexicographical_compare(name1, name2)
else
diff
end
end
def compare(b1, operator, b2) do
operator.(diff(b1, b2), 0)
end
end

View File

@ -59,6 +59,60 @@ defmodule PaxosTestAditional do
end end
end end
# Leader crashes, no concurrent ballots
def run_non_leader_send_propose_after_leader_elected(name, participants, val) do
{cpid, pid} = PaxosTest.init(name, participants, true)
send(cpid, :ready)
{status, val, a, spare} =
try do
receive do
:start ->
IO.puts("#{inspect(name)}: started")
[leader | spare] = Enum.sort(participants)
[new_leader | _] = spare
if name == new_leader do
# Wait a bit for the leader to be elected
Process.sleep(5000)
Paxos.propose(pid, 1, val, 10000)
end
if name in spare do
{status, val} = PaxosTest.wait_for_decision(pid, 1, 15000)
if status != :none,
do: IO.puts("#{name}: decided #{inspect(val)}"),
else: IO.puts("#{name}: No decision after 10 seconds")
{status, val, 10, spare}
else
{:killed, :none, -1, spare}
end
end
rescue
_ -> {:none, :none, 10, []}
end
send(cpid, :done)
receive do
:all_done ->
Process.sleep(100)
ql =
if name in spare do
IO.puts("#{name}: #{inspect(ql = Process.info(pid, :message_queue_len))}")
ql
else
{:message_queue_len, -1}
end
PaxosTest.kill_paxos(pid, name)
send(cpid, {:finished, name, pid, status, val, a, ql})
end
end
def run_leader_should_nack_simple(name, participants, val) do def run_leader_should_nack_simple(name, participants, val) do
{cpid, pid} = PaxosTest.init(name, participants, true) {cpid, pid} = PaxosTest.init(name, participants, true)

View File

@ -22,57 +22,63 @@ test_suite = [
# Use TestUtil.get_local_config(n) to generate a single-node configuration # Use TestUtil.get_local_config(n) to generate a single-node configuration
# consisting of n processes, all running on the same node. # consisting of n processes, all running on the same node.
# {&PaxosTest.run_simple/3, TestUtil.get_local_config(3), 10, {&PaxosTest.run_simple/3, TestUtil.get_local_config(3), 10,
# "No failures, no concurrent ballots, 3 local procs"}, "No failures, no concurrent ballots, 3 local procs"},
# {&PaxosTest.run_simple/3, TestUtil.get_dist_config(host, 3), 10, {&PaxosTest.run_simple/3, TestUtil.get_dist_config(host, 3), 10,
# "No failures, no concurrent ballots, 3 nodes"}, "No failures, no concurrent ballots, 3 nodes"},
# {&PaxosTest.run_simple/3, TestUtil.get_local_config(5), 10, {&PaxosTest.run_simple/3, TestUtil.get_local_config(5), 10,
# "No failures, no concurrent ballots, 5 local procs"}, "No failures, no concurrent ballots, 5 local procs"},
# {&PaxosTest.run_simple_2/3, TestUtil.get_dist_config(host, 3), 10, {&PaxosTest.run_simple_2/3, TestUtil.get_dist_config(host, 3), 10,
# "No failures, 2 concurrent ballots, 3 nodes"}, "No failures, 2 concurrent ballots, 3 nodes"},
# {&PaxosTest.run_simple_2/3, TestUtil.get_local_config(3), 10, {&PaxosTest.run_simple_2/3, TestUtil.get_local_config(3), 10,
# "No failures, 2 concurrent ballots, 3 local procs"}, "No failures, 2 concurrent ballots, 3 local procs"},
# {&PaxosTest.run_simple_3/3, TestUtil.get_local_config(3), 10, {&PaxosTest.run_simple_3/3, TestUtil.get_local_config(3), 10,
# "No failures, 2 concurrent instances, 3 local procs"}, "No failures, 2 concurrent instances, 3 local procs"},
# {&PaxosTest.run_simple_many_1/3, TestUtil.get_dist_config(host, 5), 10, {&PaxosTest.run_simple_many_1/3, TestUtil.get_dist_config(host, 5), 10,
# "No failures, many concurrent ballots 1, 5 nodes"}, "No failures, many concurrent ballots 1, 5 nodes"},
# {&PaxosTest.run_simple_many_1/3, TestUtil.get_local_config(5), 10, {&PaxosTest.run_simple_many_1/3, TestUtil.get_local_config(5), 10,
# "No failures, many concurrent ballots 1, 5 local procs"}, "No failures, many concurrent ballots 1, 5 local procs"},
# {&PaxosTest.run_simple_many_2/3, TestUtil.get_dist_config(host, 5), 10, {&PaxosTest.run_simple_many_2/3, TestUtil.get_dist_config(host, 5), 10,
# "No failures, many concurrent ballots 2, 5 nodes"}, "No failures, many concurrent ballots 2, 5 nodes"},
# {&PaxosTest.run_simple_many_2/3, TestUtil.get_local_config(5), 10, {&PaxosTest.run_simple_many_2/3, TestUtil.get_local_config(5), 10,
# "No failures, many concurrent ballots 2, 5 local procs"}, "No failures, many concurrent ballots 2, 5 local procs"},
# {&PaxosTest.run_non_leader_crash/3, TestUtil.get_dist_config(host, 3), 10, {&PaxosTest.run_non_leader_crash/3, TestUtil.get_dist_config(host, 3), 10,
# "One non-leader crashes, no concurrent ballots, 3 nodes"}, "One non-leader crashes, no concurrent ballots, 3 nodes"},
# {&PaxosTest.run_non_leader_crash/3, TestUtil.get_local_config(3), 10, {&PaxosTest.run_non_leader_crash/3, TestUtil.get_local_config(3), 10,
# "One non-leader crashes, no concurrent ballots, 3 local procs"}, "One non-leader crashes, no concurrent ballots, 3 local procs"},
# {&PaxosTest.run_minority_non_leader_crash/3, TestUtil.get_dist_config(host, 5), 10, {&PaxosTest.run_minority_non_leader_crash/3, TestUtil.get_dist_config(host, 5), 10,
# "Minority non-leader crashes, no concurrent ballots"}, "Minority non-leader crashes, no concurrent ballots"},
# {&PaxosTest.run_minority_non_leader_crash/3, TestUtil.get_local_config(5), 10, {&PaxosTest.run_minority_non_leader_crash/3, TestUtil.get_local_config(5), 10,
# "Minority non-leader crashes, no concurrent ballots"}, "Minority non-leader crashes, no concurrent ballots"},
# {&PaxosTest.run_leader_crash_simple/3, TestUtil.get_dist_config(host, 5), 10, {&PaxosTest.run_leader_crash_simple/3, TestUtil.get_dist_config(host, 5), 10,
# "Leader crashes, no concurrent ballots, 5 nodes"}, "Leader crashes, no concurrent ballots, 5 nodes"},
# {&PaxosTest.run_leader_crash_simple/3, TestUtil.get_local_config(5), 10, {&PaxosTest.run_leader_crash_simple/3, TestUtil.get_local_config(5), 10,
# "Leader crashes, no concurrent ballots, 5 local procs"}, "Leader crashes, no concurrent ballots, 5 local procs"},
# {&PaxosTest.run_leader_crash_simple_2/3, TestUtil.get_dist_config(host, 7), 10, {&PaxosTest.run_leader_crash_simple_2/3, TestUtil.get_dist_config(host, 7), 10,
# "Leader and some non-leaders crash, no concurrent ballots, 7 nodes"}, "Leader and some non-leaders crash, no concurrent ballots, 7 nodes"},
# {&PaxosTest.run_leader_crash_simple_2/3, TestUtil.get_local_config(7), 10, {&PaxosTest.run_leader_crash_simple_2/3, TestUtil.get_local_config(7), 10,
# "Leader and some non-leaders crash, no concurrent ballots, 7 local procs"}, "Leader and some non-leaders crash, no concurrent ballots, 7 local procs"},
# {&PaxosTest.run_leader_crash_complex/3, TestUtil.get_dist_config(host, 11), 10, {&PaxosTest.run_leader_crash_complex/3, TestUtil.get_dist_config(host, 11), 10,
# "Cascading failures of leaders and non-leaders, 11 nodes"}, "Cascading failures of leaders and non-leaders, 11 nodes"},
# {&PaxosTest.run_leader_crash_complex/3, TestUtil.get_local_config(11), 10, {&PaxosTest.run_leader_crash_complex/3, TestUtil.get_local_config(11), 10,
# "Cascading failures of leaders and non-leaders, 11 local procs"}, "Cascading failures of leaders and non-leaders, 11 local procs"},
# {&PaxosTest.run_leader_crash_complex_2/3, TestUtil.get_dist_config(host, 11), 10, {&PaxosTest.run_leader_crash_complex_2/3, TestUtil.get_dist_config(host, 11), 10,
# "Cascading failures of leaders and non-leaders, random delays, 7 nodes"}, "Cascading failures of leaders and non-leaders, random delays, 7 nodes"},
# {&PaxosTest.run_leader_crash_complex_2/3, TestUtil.get_local_config(11), 10, {&PaxosTest.run_leader_crash_complex_2/3, TestUtil.get_local_config(11), 10,
# "Cascading failures of leaders and non-leaders, random delays, 7 local procs"}, "Cascading failures of leaders and non-leaders, random delays, 7 local procs"},
# # Aditional Test functions # Aditional Test functions
# {&PaxosTestAditional.run_leader_crash_simple_before_decision/3, TestUtil.get_dist_config(host, 5), 10, {&PaxosTestAditional.run_leader_crash_simple_before_decision/3, TestUtil.get_dist_config(host, 5), 10,
# "Leader crashes right before decision, no concurrent ballots, 5 nodes"}, "Leader crashes right before decision, no concurrent ballots, 5 nodes"},
# {&PaxosTestAditional.run_leader_crash_simple_before_decision/3, TestUtil.get_local_config(5), 10, {&PaxosTestAditional.run_leader_crash_simple_before_decision/3, TestUtil.get_local_config(5), 10,
# "Leader crashes right before decision, no concurrent ballots, 5 local procs"}, "Leader crashes right before decision, no concurrent ballots, 5 local procs"},
{&PaxosTestAditional.run_non_leader_send_propose_after_leader_elected/3, TestUtil.get_dist_config(host, 5), 10,
"Non-Leader proposes after leader is elected, 5 nodes"},
{&PaxosTestAditional.run_non_leader_send_propose_after_leader_elected/3, TestUtil.get_local_config(5), 10,
"Non-Leader proposes after leader is elected, 5 local procs"},
{&PaxosTestAditional.run_leader_should_nack_simple/3, TestUtil.get_dist_config(host, 5), 10, {&PaxosTestAditional.run_leader_should_nack_simple/3, TestUtil.get_dist_config(host, 5), 10,
"Leader should nack before decision and then come to decision, no concurrent ballots, 5 nodes"}, "Leader should nack before decision and then come to decision, no concurrent ballots, 5 nodes"},