simplified paxos added more tests
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
71472c8544
commit
0e1e13950d
101
lib/paxos.ex
101
lib/paxos.ex
@ -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
|
||||
@ -76,7 +28,6 @@ defmodule Paxos do
|
||||
processes: processes,
|
||||
leader: nil,
|
||||
instmap: %{},
|
||||
other_values: %{},
|
||||
decided: %{}
|
||||
}
|
||||
|
||||
@ -90,6 +41,7 @@ defmodule Paxos do
|
||||
instmap =
|
||||
Map.put(state.instmap, inst, %{
|
||||
value: value,
|
||||
other_value: nil,
|
||||
ballot: Ballot.init(state.name, 0),
|
||||
aborted: false,
|
||||
ballot_value: nil,
|
||||
@ -172,8 +124,6 @@ defmodule Paxos do
|
||||
end
|
||||
|
||||
{:rb_deliver, proc, {:other_propose, inst, value}} ->
|
||||
state = %{state | other_values: Map.put(state.other_values, inst, value)}
|
||||
|
||||
cond do
|
||||
has_finished(state, inst, true) ->
|
||||
EagerReliableBroadcast.broadcast(
|
||||
@ -184,6 +134,7 @@ defmodule Paxos do
|
||||
|
||||
true ->
|
||||
state = has_or_create(state, inst)
|
||||
state = set_instmap(state, inst, fn map -> %{map | other_value: value} end)
|
||||
prepare(state, inst)
|
||||
end
|
||||
|
||||
@ -399,7 +350,7 @@ defmodule Paxos do
|
||||
state.instmap[inst] == nil ->
|
||||
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.instmap[inst] != nil and state.instmap[inst].has_sent_prepare ->
|
||||
@ -449,7 +400,7 @@ defmodule Paxos do
|
||||
a_val =
|
||||
if a_val == nil do
|
||||
if state.instmap[inst].value == nil do
|
||||
state.other_values[inst]
|
||||
state.instmap[inst].other_value
|
||||
else
|
||||
state.instmap[inst].value
|
||||
end
|
||||
@ -556,3 +507,47 @@ defmodule Paxos do
|
||||
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
|
||||
|
@ -59,6 +59,60 @@ defmodule PaxosTestAditional do
|
||||
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
|
||||
{cpid, pid} = PaxosTest.init(name, participants, true)
|
||||
|
@ -22,57 +22,63 @@ test_suite = [
|
||||
# Use TestUtil.get_local_config(n) to generate a single-node configuration
|
||||
# consisting of n processes, all running on the same node.
|
||||
|
||||
# {&PaxosTest.run_simple/3, TestUtil.get_local_config(3), 10,
|
||||
# "No failures, no concurrent ballots, 3 local procs"},
|
||||
# {&PaxosTest.run_simple/3, TestUtil.get_dist_config(host, 3), 10,
|
||||
# "No failures, no concurrent ballots, 3 nodes"},
|
||||
# {&PaxosTest.run_simple/3, TestUtil.get_local_config(5), 10,
|
||||
# "No failures, no concurrent ballots, 5 local procs"},
|
||||
# {&PaxosTest.run_simple_2/3, TestUtil.get_dist_config(host, 3), 10,
|
||||
# "No failures, 2 concurrent ballots, 3 nodes"},
|
||||
# {&PaxosTest.run_simple_2/3, TestUtil.get_local_config(3), 10,
|
||||
# "No failures, 2 concurrent ballots, 3 local procs"},
|
||||
# {&PaxosTest.run_simple_3/3, TestUtil.get_local_config(3), 10,
|
||||
# "No failures, 2 concurrent instances, 3 local procs"},
|
||||
# {&PaxosTest.run_simple_many_1/3, TestUtil.get_dist_config(host, 5), 10,
|
||||
# "No failures, many concurrent ballots 1, 5 nodes"},
|
||||
# {&PaxosTest.run_simple_many_1/3, TestUtil.get_local_config(5), 10,
|
||||
# "No failures, many concurrent ballots 1, 5 local procs"},
|
||||
# {&PaxosTest.run_simple_many_2/3, TestUtil.get_dist_config(host, 5), 10,
|
||||
# "No failures, many concurrent ballots 2, 5 nodes"},
|
||||
# {&PaxosTest.run_simple_many_2/3, TestUtil.get_local_config(5), 10,
|
||||
# "No failures, many concurrent ballots 2, 5 local procs"},
|
||||
# {&PaxosTest.run_non_leader_crash/3, TestUtil.get_dist_config(host, 3), 10,
|
||||
# "One non-leader crashes, no concurrent ballots, 3 nodes"},
|
||||
# {&PaxosTest.run_non_leader_crash/3, TestUtil.get_local_config(3), 10,
|
||||
# "One non-leader crashes, no concurrent ballots, 3 local procs"},
|
||||
# {&PaxosTest.run_minority_non_leader_crash/3, TestUtil.get_dist_config(host, 5), 10,
|
||||
# "Minority non-leader crashes, no concurrent ballots"},
|
||||
# {&PaxosTest.run_minority_non_leader_crash/3, TestUtil.get_local_config(5), 10,
|
||||
# "Minority non-leader crashes, no concurrent ballots"},
|
||||
# {&PaxosTest.run_leader_crash_simple/3, TestUtil.get_dist_config(host, 5), 10,
|
||||
# "Leader crashes, no concurrent ballots, 5 nodes"},
|
||||
# {&PaxosTest.run_leader_crash_simple/3, TestUtil.get_local_config(5), 10,
|
||||
# "Leader crashes, no concurrent ballots, 5 local procs"},
|
||||
# {&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"},
|
||||
# {&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"},
|
||||
# {&PaxosTest.run_leader_crash_complex/3, TestUtil.get_dist_config(host, 11), 10,
|
||||
# "Cascading failures of leaders and non-leaders, 11 nodes"},
|
||||
# {&PaxosTest.run_leader_crash_complex/3, TestUtil.get_local_config(11), 10,
|
||||
# "Cascading failures of leaders and non-leaders, 11 local procs"},
|
||||
# {&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"},
|
||||
# {&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"},
|
||||
{&PaxosTest.run_simple/3, TestUtil.get_local_config(3), 10,
|
||||
"No failures, no concurrent ballots, 3 local procs"},
|
||||
{&PaxosTest.run_simple/3, TestUtil.get_dist_config(host, 3), 10,
|
||||
"No failures, no concurrent ballots, 3 nodes"},
|
||||
{&PaxosTest.run_simple/3, TestUtil.get_local_config(5), 10,
|
||||
"No failures, no concurrent ballots, 5 local procs"},
|
||||
{&PaxosTest.run_simple_2/3, TestUtil.get_dist_config(host, 3), 10,
|
||||
"No failures, 2 concurrent ballots, 3 nodes"},
|
||||
{&PaxosTest.run_simple_2/3, TestUtil.get_local_config(3), 10,
|
||||
"No failures, 2 concurrent ballots, 3 local procs"},
|
||||
{&PaxosTest.run_simple_3/3, TestUtil.get_local_config(3), 10,
|
||||
"No failures, 2 concurrent instances, 3 local procs"},
|
||||
{&PaxosTest.run_simple_many_1/3, TestUtil.get_dist_config(host, 5), 10,
|
||||
"No failures, many concurrent ballots 1, 5 nodes"},
|
||||
{&PaxosTest.run_simple_many_1/3, TestUtil.get_local_config(5), 10,
|
||||
"No failures, many concurrent ballots 1, 5 local procs"},
|
||||
{&PaxosTest.run_simple_many_2/3, TestUtil.get_dist_config(host, 5), 10,
|
||||
"No failures, many concurrent ballots 2, 5 nodes"},
|
||||
{&PaxosTest.run_simple_many_2/3, TestUtil.get_local_config(5), 10,
|
||||
"No failures, many concurrent ballots 2, 5 local procs"},
|
||||
{&PaxosTest.run_non_leader_crash/3, TestUtil.get_dist_config(host, 3), 10,
|
||||
"One non-leader crashes, no concurrent ballots, 3 nodes"},
|
||||
{&PaxosTest.run_non_leader_crash/3, TestUtil.get_local_config(3), 10,
|
||||
"One non-leader crashes, no concurrent ballots, 3 local procs"},
|
||||
{&PaxosTest.run_minority_non_leader_crash/3, TestUtil.get_dist_config(host, 5), 10,
|
||||
"Minority non-leader crashes, no concurrent ballots"},
|
||||
{&PaxosTest.run_minority_non_leader_crash/3, TestUtil.get_local_config(5), 10,
|
||||
"Minority non-leader crashes, no concurrent ballots"},
|
||||
{&PaxosTest.run_leader_crash_simple/3, TestUtil.get_dist_config(host, 5), 10,
|
||||
"Leader crashes, no concurrent ballots, 5 nodes"},
|
||||
{&PaxosTest.run_leader_crash_simple/3, TestUtil.get_local_config(5), 10,
|
||||
"Leader crashes, no concurrent ballots, 5 local procs"},
|
||||
{&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"},
|
||||
{&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"},
|
||||
{&PaxosTest.run_leader_crash_complex/3, TestUtil.get_dist_config(host, 11), 10,
|
||||
"Cascading failures of leaders and non-leaders, 11 nodes"},
|
||||
{&PaxosTest.run_leader_crash_complex/3, TestUtil.get_local_config(11), 10,
|
||||
"Cascading failures of leaders and non-leaders, 11 local procs"},
|
||||
{&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"},
|
||||
{&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"},
|
||||
|
||||
# # Aditional Test functions
|
||||
# Aditional Test functions
|
||||
|
||||
# {&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"},
|
||||
# {&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"},
|
||||
{&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"},
|
||||
{&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"},
|
||||
|
||||
{&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,
|
||||
"Leader should nack before decision and then come to decision, no concurrent ballots, 5 nodes"},
|
||||
|
Reference in New Issue
Block a user