diff --git a/lib/leader_elector.ex b/lib/leader_elector.ex index 17c8e07..3393854 100644 --- a/lib/leader_elector.ex +++ b/lib/leader_elector.ex @@ -56,7 +56,7 @@ defmodule EventualLeaderElector do {:timeout} -> state = - if MapSet.size(state.heard_back) == 0 do + if MapSet.size(state.heard_back) < floor(length(state.processes)/2) + 1 do state else to_trust = Enum.at(Enum.sort(MapSet.to_list(state.heard_back)), 0) diff --git a/lib/paxos.ex b/lib/paxos.ex index cbe2af2..2404172 100644 --- a/lib/paxos.ex +++ b/lib/paxos.ex @@ -42,6 +42,7 @@ defmodule Paxos do pid_to_inform: pid_to_inform, has_sent_accept: false, action: action, + has_sent_prepare: false, }) %{state | instmap: instmap} @@ -146,7 +147,7 @@ defmodule Paxos do } end) true -> - Utils.unicast({:nack, inst, ballot}, proc) + Utils.unicast({:nack, inst, ballot, state.instmap[inst].ballot}, proc) state end @@ -157,8 +158,8 @@ defmodule Paxos do state - {:nack, inst, ballot} -> - IO.puts("#{state.name} - nack #{inspect(inst)} #{inspect(ballot)}") + {:nack, inst, ballot, new_ballot} -> + IO.puts("#{state.name} - nack #{inspect(inst)} #{inspect(ballot)} #{inspect(new_ballot)}") cond do has_finished(state, inst) -> @@ -169,14 +170,35 @@ defmodule Paxos do send(state.instmap[inst].pid_to_inform, {:abort, inst}) end + EagerReliableBroadcast.broadcast(state.name, {:abort, inst, ballot, new_ballot}) + set_instmap(state, inst, fn map -> %{ - map | has_sent_accept: false + map | has_sent_accept: false, + ballot: new_ballot + 1, + has_sent_prepare: false, } end) true -> state end + {:rb_deliver, _proc, {:abort, inst, ballot}} -> + cond do + has_finished(state, inst) -> + state + + state.instmap[inst].ballot == ballot -> + if Map.has_key?(state.instmap, inst) and state.instmap[inst].pid_to_inform != nil do + send(state.instmap[inst].pid_to_inform, {:abort, inst}) + end + + state + + true -> + state + + end + {:prepared, inst, ballot, accepted_ballot, accepted_value} -> IO.puts( "#{state.name} - prepared #{inspect(inst)} #{inspect(ballot)} #{inspect(accepted_ballot)} #{inspect(accepted_value)}" @@ -225,7 +247,7 @@ defmodule Paxos do } end) else IO.puts("#{state.name} -> #{proc} nack") - Utils.unicast({:nack, inst, ballot}, proc) + Utils.unicast({:nack, inst, ballot, state.instmap[inst].ballot}, proc) state end end @@ -233,10 +255,11 @@ defmodule Paxos do {:accepted, inst, ballot} -> IO.puts("#{state.name} accepted #{inspect(inst)} #{inspect(ballot)}") - if has_finished(state, inst) do - state - else - if state.leader == state.name and state.instmap[inst].ballot == ballot do + cond do + has_finished(state, inst) -> + state + + state.leader == state.name and state.instmap[inst].ballot == ballot -> accepted( set_instmap(state, inst, fn map -> %{ map @@ -244,9 +267,9 @@ defmodule Paxos do } end), inst ) - else + + true -> state - end end {:get_value, inst, pid_to_inform, t} -> @@ -302,6 +325,9 @@ defmodule Paxos do Map.get(state.instmap, inst) == nil and Map.get(state.other_values, inst) == nil -> state + Map.get(state.instmap, inst) != nil and state.instmap[inst].has_sent_prepare -> + state + Map.get(state.instmap, inst) != nil and state.instmap[inst].has_sent_accept -> state @@ -315,6 +341,7 @@ defmodule Paxos do | prepared_values: [], accepted: 0, ballot_value: nil, + has_sent_prepare: true, has_sent_accept: false } end) end @@ -330,11 +357,14 @@ defmodule Paxos do not state.instmap[inst].has_sent_accept do {_, a_val} = Enum.reduce(state.instmap[inst].prepared_values, {0, nil}, fn {bal, val}, - {a_bal, a_val} -> - if a_bal > bal do - {a_bal, a_val} - else - {bal, val} + {acc_bal, acc_val} -> + cond do + val == nil -> + {acc_bal, acc_val} + acc_bal > bal -> + {acc_bal, acc_val} + true -> + {bal, val} end end) diff --git a/lib/test_script.exs b/lib/test_script.exs index f434c31..36e917b 100755 --- a/lib/test_script.exs +++ b/lib/test_script.exs @@ -22,42 +22,42 @@ 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_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, @@ -69,10 +69,10 @@ test_suite = [ # 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"}, ] Node.stop()