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
|
# 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
|
||||||
|
@ -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)
|
||||||
|
@ -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"},
|
||||||
|
Reference in New Issue
Block a user