Compare commits
8 Commits
3cbf7b99af
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d5383e67b2 | |||
| 05c09d10f3 | |||
| 134d958be8 | |||
| 0433210920 | |||
| 7ff5414eba | |||
| 074944ccb4 | |||
| 5740077954 | |||
| c803a3bb2b |
@@ -23,7 +23,7 @@ The server provides the following API:
|
||||
|
||||
## Important Concepts
|
||||
|
||||
The `game_id` is a number and is an identifier for the game. This can be a number that paxos guarantees that there cannot be two games with same number, as the server would have learned about that decision and would have not incremented the number.
|
||||
The `game_id` is a number and is an identifier for the game. This can be a number as paxos guarantees that there cannot be two games with same number, as the server would have learned about that decision and would have incremented the number.
|
||||
|
||||
The `game_state` is a representation of a game state that corresponds to an array of numbers, and some special atoms, where the number represents the atomic number. The maximum length of this array is 16, so when an array gets bigger than that the game is over.
|
||||
|
||||
@@ -123,7 +123,7 @@ The server will answer with one of the following:
|
||||
|
||||
- If a game exists, then there was a process that creates it.
|
||||
- Eventually, all processes will be in the same game state
|
||||
- A player cannot make a move in a game state unless it's not updated to the last game state
|
||||
- A player cannot make a move in a game state unless it's updated to the last game state
|
||||
- If two players make a move at the same time in the same game, only one of the moves happens and the other player eventually gets notified
|
||||
- If a player makes a valid move it will be evetually played
|
||||
|
||||
|
||||
41
lib/paxos.ex
41
lib/paxos.ex
@@ -168,7 +168,7 @@ defmodule Paxos do
|
||||
end
|
||||
|
||||
# Guarantees that a specific state exists for a specific instance
|
||||
defp has_or_create(state, inst, value \\ nil, pid_to_inform \\ nil, action \\ nil) do
|
||||
defp has_or_create(state, inst, value \\ nil, pid_to_inform \\ nil) do
|
||||
or_state state.instmap[inst] == nil do
|
||||
instmap =
|
||||
Map.put(state.instmap, inst, %{
|
||||
@@ -183,7 +183,6 @@ defmodule Paxos do
|
||||
accepted_value: nil,
|
||||
pid_to_inform: pid_to_inform,
|
||||
has_sent_accept: false,
|
||||
action: action,
|
||||
has_sent_prepare: false,
|
||||
})
|
||||
|
||||
@@ -206,7 +205,7 @@ defmodule Paxos do
|
||||
# This is the run/recieve function
|
||||
# All the messages that are handled by this function are:
|
||||
# {:ele_trust, proc} ->
|
||||
# {:propose, inst, value, pid_to_inform, action} ->
|
||||
# {:propose, inst, value, pid_to_inform} ->
|
||||
# {:rb_deliver, proc, {:other_propose, inst, value}} ->
|
||||
# {:rb_deliver, proc, {:prepare, proc, inst, ballot}} ->
|
||||
# {:nack, inst, ballot} ->
|
||||
@@ -225,9 +224,9 @@ defmodule Paxos do
|
||||
prepare(st, inst)
|
||||
end)
|
||||
|
||||
# Handles a proposal from the parent process
|
||||
{:propose, inst, value, pid_to_inform, action} ->
|
||||
log("#{state.name} - Propose #{inspect(inst)} with action #{inspect(action)}")
|
||||
# Handles a proposal message from the parent process
|
||||
{:propose, inst, value, pid_to_inform} ->
|
||||
log("#{state.name} - Propose #{inspect(inst)}")
|
||||
|
||||
cond do
|
||||
has_finished(state, inst, true) ->
|
||||
@@ -235,19 +234,9 @@ defmodule Paxos do
|
||||
send(pid_to_inform, {:decision, inst, state.decided[inst]})
|
||||
state
|
||||
|
||||
action == :increase_ballot_number ->
|
||||
log("#{state.name} - Got request to increase ballot number for inst #{inst}")
|
||||
state = has_or_create(state, inst)
|
||||
|
||||
set_instmap do
|
||||
%{map| ballot: Ballot.inc(map.ballot)}
|
||||
end
|
||||
|
||||
state
|
||||
|
||||
not Map.has_key?(state.instmap, inst) ->
|
||||
EagerReliableBroadcast.broadcast(state.name, {:other_propose, inst, value})
|
||||
state = has_or_create(state, inst, value, pid_to_inform, action)
|
||||
state = has_or_create(state, inst, value, pid_to_inform)
|
||||
prepare(state, inst)
|
||||
|
||||
state.instmap[inst].value == nil ->
|
||||
@@ -257,7 +246,6 @@ defmodule Paxos do
|
||||
%{ map |
|
||||
value: value,
|
||||
pid_to_inform: pid_to_inform,
|
||||
action: action,
|
||||
}
|
||||
end
|
||||
|
||||
@@ -320,7 +308,7 @@ defmodule Paxos do
|
||||
state
|
||||
end
|
||||
|
||||
# Handles a nack
|
||||
# Handles a nack message
|
||||
{:nack, inst, ballot} ->
|
||||
log("#{state.name} - nack #{inspect(inst)} #{inspect(ballot)}")
|
||||
|
||||
@@ -348,7 +336,7 @@ defmodule Paxos do
|
||||
state
|
||||
end
|
||||
|
||||
# Handles an abort
|
||||
# Handles an abort message
|
||||
{:rb_deliver, _proc, {:abort, inst, _}} ->
|
||||
cond do
|
||||
has_finished(state, inst) ->
|
||||
@@ -392,7 +380,7 @@ defmodule Paxos do
|
||||
state
|
||||
end
|
||||
|
||||
# Handles a accept
|
||||
# Handles an accept message
|
||||
{:rb_deliver, proc, {:accept, inst, ballot, value}} ->
|
||||
cond do
|
||||
has_finished(state, inst) ->
|
||||
@@ -421,7 +409,7 @@ defmodule Paxos do
|
||||
end
|
||||
end
|
||||
|
||||
# Handles a accept
|
||||
# Handles an accept message
|
||||
# Sends out accept when a decide when a quoeom is met
|
||||
{:accepted, inst, ballot} ->
|
||||
log("#{state.name} - accepted #{inspect(inst)} #{inspect(ballot)}")
|
||||
@@ -553,11 +541,6 @@ defmodule Paxos do
|
||||
or_state state.instmap[inst].accepted >= floor(length(state.processes) / 2) + 1 do
|
||||
value = state.instmap[inst].ballot_value
|
||||
|
||||
if state.instmap[inst].action == :kill_before_decision do
|
||||
log("#{state.name} - Leader has action to die before decision #{inspect({:decide, inst, value})}")
|
||||
Process.exit(self(), :kill)
|
||||
end
|
||||
|
||||
EagerReliableBroadcast.broadcast(
|
||||
state.name,
|
||||
{:decide, inst, value}
|
||||
@@ -582,8 +565,8 @@ defmodule Paxos do
|
||||
@doc """
|
||||
Send the propose message to the paxos replica and waits for a response from the correct instance
|
||||
"""
|
||||
def propose(pid, inst, value, t, action \\ nil) do
|
||||
send(pid, {:propose, inst, value, self(), action})
|
||||
def propose(pid, inst, value, t) do
|
||||
send(pid, {:propose, inst, value, self()})
|
||||
|
||||
propose_loop(inst, t)
|
||||
end
|
||||
|
||||
@@ -66,25 +66,25 @@ 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_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"},
|
||||
{&PaxosTestAditional.run_leader_should_nack_simple/3, TestUtil.get_local_config(5), 10,
|
||||
"Leader should nack before decision and then come to decision, 5 local procs"},
|
||||
|
||||
{&PaxosTestAditional.run_non_leader_should_nack_simple/3, TestUtil.get_dist_config(host, 5), 10,
|
||||
"Non-Leader should nack before decision and then come to decision, no concurrent ballots, 5 nodes"},
|
||||
{&PaxosTestAditional.run_non_leader_should_nack_simple/3, TestUtil.get_local_config(5), 10,
|
||||
"Non-Leader should nack before decision and then come to decision, 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"},
|
||||
# {&PaxosTestAditional.run_leader_should_nack_simple/3, TestUtil.get_local_config(5), 10,
|
||||
# "Leader should nack before decision and then come to decision, 5 local procs"},
|
||||
#
|
||||
# {&PaxosTestAditional.run_non_leader_should_nack_simple/3, TestUtil.get_dist_config(host, 5), 10,
|
||||
# "Non-Leader should nack before decision and then come to decision, no concurrent ballots, 5 nodes"},
|
||||
# {&PaxosTestAditional.run_non_leader_should_nack_simple/3, TestUtil.get_local_config(5), 10,
|
||||
# "Non-Leader should nack before decision and then come to decision, 5 local procs"},
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user