merge files and made set_instmap to func
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
0e1e13950d
commit
6bdb56755d
@ -1,63 +0,0 @@
|
|||||||
defmodule EagerReliableBroadcast do
|
|
||||||
def get_rb_name(name) do
|
|
||||||
String.to_atom(Atom.to_string(name) <> "_rb")
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_non_rb_name(name) do
|
|
||||||
String.to_atom(String.replace(Atom.to_string(name), "_rb", ""))
|
|
||||||
end
|
|
||||||
|
|
||||||
def start(name, processes) do
|
|
||||||
pid = spawn(EagerReliableBroadcast, :init, [name, processes])
|
|
||||||
Utils.register_name(get_rb_name(name), pid)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Init event must be the first
|
|
||||||
# one after the component is created
|
|
||||||
def init(parent, processes) do
|
|
||||||
state = %{
|
|
||||||
name: get_rb_name(parent),
|
|
||||||
parent: parent,
|
|
||||||
processes: Enum.map(processes, fn name -> get_rb_name(name) end),
|
|
||||||
# Use this data structure to remember IDs of the delivered messages
|
|
||||||
delivered: %{},
|
|
||||||
# Use this variable to remember the last sequence number used to identify a message
|
|
||||||
seq_no: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
run(state)
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(state) do
|
|
||||||
run(
|
|
||||||
receive do
|
|
||||||
# Handle the broadcast request event
|
|
||||||
{:broadcast, m} ->
|
|
||||||
data_msg = {:data, state.name, state.seq_no, m}
|
|
||||||
Utils.beb_broadcast(data_msg, state.processes)
|
|
||||||
%{state | seq_no: state.seq_no + 1}
|
|
||||||
|
|
||||||
{:data, proc, seq_no, m} ->
|
|
||||||
if not Map.has_key?(state.delivered, {proc, seq_no, m}) do
|
|
||||||
data_msg = {:data, proc, seq_no, m}
|
|
||||||
Utils.beb_broadcast(data_msg, state.processes)
|
|
||||||
|
|
||||||
Utils.unicast({:rb_deliver, get_non_rb_name(proc), m}, state.parent)
|
|
||||||
%{state | delivered: Map.put(state.delivered, {proc, seq_no, m}, 1)}
|
|
||||||
else
|
|
||||||
val = Map.get(state.delivered, {proc, seq_no, m})
|
|
||||||
|
|
||||||
if val < Enum.count(state.processes) do
|
|
||||||
%{state | delivered: Map.put(state.delivered, {proc, seq_no, m}, val + 1)}
|
|
||||||
else
|
|
||||||
%{state | delivered: Map.delete(state.delivered, {proc, seq_no, m})}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def broadcast(name, m) do
|
|
||||||
Utils.unicast({:broadcast, m}, get_rb_name(name))
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,76 +0,0 @@
|
|||||||
#
|
|
||||||
# Emits {:ele_trust, proc }
|
|
||||||
#
|
|
||||||
|
|
||||||
defmodule EventualLeaderElector do
|
|
||||||
def getEleName(name) do
|
|
||||||
String.to_atom(Atom.to_string(name) <> "_ele")
|
|
||||||
end
|
|
||||||
|
|
||||||
def start(name, processes) do
|
|
||||||
new_name = getEleName(name)
|
|
||||||
pid = spawn(EventualLeaderElector, :init, [new_name, name, processes])
|
|
||||||
|
|
||||||
Utils.register_name(new_name, pid)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Init event must be the first
|
|
||||||
# one after the component is created
|
|
||||||
def init(name, parent, processes) do
|
|
||||||
processes = Enum.map(processes, fn name -> getEleName(name) end)
|
|
||||||
|
|
||||||
state = %{
|
|
||||||
name: name,
|
|
||||||
parent: parent,
|
|
||||||
processes: processes,
|
|
||||||
timeout: 1000,
|
|
||||||
heard_back: MapSet.new(),
|
|
||||||
seq: 0,
|
|
||||||
last_trust: nil
|
|
||||||
}
|
|
||||||
|
|
||||||
run(request_heartbeats(state))
|
|
||||||
end
|
|
||||||
|
|
||||||
def request_heartbeats(state) do
|
|
||||||
state = %{state | heard_back: MapSet.new(), seq: state.seq + 1}
|
|
||||||
Utils.beb_broadcast({:heartbeat_request, state.name, state.seq}, state.processes)
|
|
||||||
|
|
||||||
Process.send_after(self(), {:timeout}, state.timeout)
|
|
||||||
state
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(state) do
|
|
||||||
run(
|
|
||||||
receive do
|
|
||||||
{:heartbeat_request, name, seq} ->
|
|
||||||
Utils.unicast({:heartbeat, state.parent, seq}, name)
|
|
||||||
state
|
|
||||||
|
|
||||||
{:heartbeat, name, seq} ->
|
|
||||||
if seq == state.seq do
|
|
||||||
%{state | heard_back: MapSet.put(state.heard_back, name)}
|
|
||||||
else
|
|
||||||
state
|
|
||||||
end
|
|
||||||
|
|
||||||
{:timeout} ->
|
|
||||||
state =
|
|
||||||
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)
|
|
||||||
|
|
||||||
if state.last_trust != to_trust do
|
|
||||||
Utils.unicast({:ele_trust, to_trust}, state.parent)
|
|
||||||
%{state | last_trust: to_trust}
|
|
||||||
else
|
|
||||||
state
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
request_heartbeats(state)
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
326
lib/paxos.ex
326
lib/paxos.ex
@ -1,3 +1,56 @@
|
|||||||
|
defmodule Utils do
|
||||||
|
def safecast(p, m) when p == nil, do: IO.puts('Trying to safecast #{m} with p as nil')
|
||||||
|
def safecast(p, m) when is_pid(p), do: send(p, m)
|
||||||
|
def safecast(p, m) do
|
||||||
|
case :global.whereis_name(p) do
|
||||||
|
pid when is_pid(pid) -> send(pid, m)
|
||||||
|
:undefined -> :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@deprecated
|
||||||
|
def unicast(m, p) do
|
||||||
|
case :global.whereis_name(p) do
|
||||||
|
pid when is_pid(pid) -> send(pid, m)
|
||||||
|
:undefined -> :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def beb_broadcast(m, dest), do: for(p <- dest, do: safecast(p, m))
|
||||||
|
|
||||||
|
def register_name(name, pid, link \\ true) do
|
||||||
|
case :global.re_register_name(name, pid) do
|
||||||
|
:yes ->
|
||||||
|
# Note this is running on the parent so we are linking the parent to the rb
|
||||||
|
# so that when we close the parent the rb also dies
|
||||||
|
if link do
|
||||||
|
Process.link(pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
pid
|
||||||
|
|
||||||
|
:no ->
|
||||||
|
Process.exit(pid, :kill)
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defmacro checkinst(val, do: expr) do
|
||||||
|
quote do
|
||||||
|
case var!(state).instmap[var!(inst)] != nil do
|
||||||
|
unquote(val) -> unquote(expr)
|
||||||
|
_ -> var!(state)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defmacro checkinst(do: expr) do
|
||||||
|
quote do
|
||||||
|
checkinst(true, expr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Possible actions
|
# Possible actions
|
||||||
@ -5,15 +58,26 @@
|
|||||||
# :increase_ballot_number - this makes it so that it does not propose but jump simply increases the number of the current ballot
|
# :increase_ballot_number - this makes it so that it does not propose but jump simply increases the number of the current ballot
|
||||||
# this is usefull when forcing a nack
|
# this is usefull when forcing a nack
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
defmodule Paxos do
|
defmodule Paxos do
|
||||||
|
require Utils
|
||||||
|
import Utils
|
||||||
|
|
||||||
|
defmacro set_instmap(do: expr) do
|
||||||
|
quote do
|
||||||
|
var!(map) = var!(state).instmap[var!(inst)]
|
||||||
|
new_instmap = Map.put(var!(state).instmap, var!(inst), unquote(expr))
|
||||||
|
var!(state) = %{var!(state) | instmap: new_instmap }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Starts the Paxos replica with a specific name and some processes
|
||||||
def start(name, processes) do
|
def start(name, processes) do
|
||||||
IO.puts("Starting paxos for #{name}")
|
IO.puts("Starting paxos for #{name}")
|
||||||
|
|
||||||
pid = spawn(Paxos, :init, [name, name, processes])
|
pid = spawn(Paxos, :init, [name, name, processes])
|
||||||
|
|
||||||
Utils.register_name(name, pid, false)
|
register_name(name, pid, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Init event must be the first
|
# Init event must be the first
|
||||||
@ -34,10 +98,9 @@ defmodule Paxos do
|
|||||||
run(state)
|
run(state)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Guarantees that a specific state exists
|
||||||
def has_or_create(state, inst, value \\ nil, pid_to_inform \\ nil, action \\ nil) do
|
def has_or_create(state, inst, value \\ nil, pid_to_inform \\ nil, action \\ nil) do
|
||||||
if Map.has_key?(state.instmap, inst) do
|
checkinst false do
|
||||||
state
|
|
||||||
else
|
|
||||||
instmap =
|
instmap =
|
||||||
Map.put(state.instmap, inst, %{
|
Map.put(state.instmap, inst, %{
|
||||||
value: value,
|
value: value,
|
||||||
@ -96,7 +159,9 @@ defmodule Paxos do
|
|||||||
# Inform the pid with timeout right way
|
# Inform the pid with timeout right way
|
||||||
send(pid_to_inform, {:timeout, inst});
|
send(pid_to_inform, {:timeout, inst});
|
||||||
|
|
||||||
set_instmap(state, inst, fn map -> %{map| ballot: Ballot.inc(map.ballot)} end)
|
set_instmap do
|
||||||
|
%{map| ballot: Ballot.inc(map.ballot)}
|
||||||
|
end
|
||||||
|
|
||||||
not Map.has_key?(state.instmap, inst) ->
|
not Map.has_key?(state.instmap, inst) ->
|
||||||
EagerReliableBroadcast.broadcast(state.name, {:other_propose, inst, value})
|
EagerReliableBroadcast.broadcast(state.name, {:other_propose, inst, value})
|
||||||
@ -108,15 +173,15 @@ defmodule Paxos do
|
|||||||
EagerReliableBroadcast.broadcast(state.name, {:other_propose, inst, value})
|
EagerReliableBroadcast.broadcast(state.name, {:other_propose, inst, value})
|
||||||
Process.send_after(self(), {:timeout, inst}, t)
|
Process.send_after(self(), {:timeout, inst}, t)
|
||||||
|
|
||||||
prepare(
|
set_instmap do
|
||||||
set_instmap(state, inst, fn map -> %{
|
%{ map |
|
||||||
map
|
value: value,
|
||||||
| value: value,
|
|
||||||
pid_to_inform: pid_to_inform,
|
pid_to_inform: pid_to_inform,
|
||||||
action: action,
|
action: action,
|
||||||
} end),
|
}
|
||||||
inst
|
end
|
||||||
)
|
|
||||||
|
prepare(state, inst)
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
EagerReliableBroadcast.broadcast(state.name, {:other_propose, inst, value})
|
EagerReliableBroadcast.broadcast(state.name, {:other_propose, inst, value})
|
||||||
@ -134,7 +199,9 @@ 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)
|
set_instmap do
|
||||||
|
%{map | other_value: value}
|
||||||
|
end
|
||||||
prepare(state, inst)
|
prepare(state, inst)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -148,31 +215,28 @@ defmodule Paxos do
|
|||||||
not Map.has_key?(state.instmap, inst) ->
|
not Map.has_key?(state.instmap, inst) ->
|
||||||
state = has_or_create(state, inst)
|
state = has_or_create(state, inst)
|
||||||
|
|
||||||
Utils.unicast(
|
safecast(proc, {:prepared, inst, ballot, state.instmap[inst].accepted_ballot, state.instmap[inst].accepted_value});
|
||||||
{:prepared, inst, ballot, state.instmap[inst].accepted_ballot,
|
|
||||||
state.instmap[inst].accepted_value},
|
|
||||||
proc
|
|
||||||
)
|
|
||||||
|
|
||||||
set_instmap(state, inst, fn map -> %{
|
set_instmap do
|
||||||
map
|
%{ map
|
||||||
| ballot: ballot
|
| ballot: ballot
|
||||||
} end)
|
}
|
||||||
|
end
|
||||||
|
|
||||||
Ballot.compare(ballot, &>/2, state.instmap[inst].ballot) ->
|
Ballot.compare(ballot, &>/2, state.instmap[inst].ballot) ->
|
||||||
Utils.unicast(
|
safecast(proc,
|
||||||
{:prepared, inst, ballot, state.instmap[inst].accepted_ballot,
|
{:prepared, inst, ballot, state.instmap[inst].accepted_ballot,
|
||||||
state.instmap[inst].accepted_value},
|
state.instmap[inst].accepted_value}
|
||||||
proc
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set_instmap(state, inst, fn map -> %{
|
set_instmap do
|
||||||
map
|
%{ map
|
||||||
| ballot: ballot
|
| ballot: ballot
|
||||||
} end)
|
}
|
||||||
|
end
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
Utils.unicast({:nack, inst, ballot}, proc)
|
safecast(proc, {:nack, inst, ballot})
|
||||||
state
|
state
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -197,12 +261,13 @@ defmodule Paxos do
|
|||||||
|
|
||||||
EagerReliableBroadcast.broadcast(state.name, {:abort, inst, ballot})
|
EagerReliableBroadcast.broadcast(state.name, {:abort, inst, ballot})
|
||||||
|
|
||||||
set_instmap(state, inst, fn map -> %{
|
set_instmap do
|
||||||
map | has_sent_accept: false,
|
%{ map | has_sent_accept: false,
|
||||||
has_sent_prepare: false,
|
has_sent_prepare: false,
|
||||||
ballot: Ballot.inc(map.ballot),
|
ballot: Ballot.inc(map.ballot),
|
||||||
aborted: true,
|
aborted: true,
|
||||||
} end)
|
}
|
||||||
|
end
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
state
|
state
|
||||||
@ -234,11 +299,12 @@ defmodule Paxos do
|
|||||||
state
|
state
|
||||||
|
|
||||||
Ballot.compare(ballot, &==/2, state.instmap[inst].ballot) ->
|
Ballot.compare(ballot, &==/2, state.instmap[inst].ballot) ->
|
||||||
state =
|
set_instmap do
|
||||||
set_instmap(state, inst, fn map -> %{
|
%{
|
||||||
map
|
map
|
||||||
| prepared_values: map.prepared_values ++ [{accepted_ballot, accepted_value}]
|
| prepared_values: map.prepared_values ++ [{accepted_ballot, accepted_value}]
|
||||||
} end)
|
}
|
||||||
|
end
|
||||||
|
|
||||||
prepared(state, inst)
|
prepared(state, inst)
|
||||||
|
|
||||||
@ -262,17 +328,18 @@ defmodule Paxos do
|
|||||||
if Ballot.compare(ballot, &>=/2, state.instmap[inst].ballot) do
|
if Ballot.compare(ballot, &>=/2, state.instmap[inst].ballot) do
|
||||||
IO.puts("#{state.name} - accept #{inspect(inst)} #{inspect(ballot)} #{inspect(value)}")
|
IO.puts("#{state.name} - accept #{inspect(inst)} #{inspect(ballot)} #{inspect(value)}")
|
||||||
|
|
||||||
Utils.unicast({:accepted, inst, ballot}, proc)
|
safecast(proc, {:accepted, inst, ballot})
|
||||||
|
|
||||||
set_instmap(state, inst, fn map -> %{
|
set_instmap do
|
||||||
map
|
%{ map
|
||||||
| ballot: ballot,
|
| ballot: ballot,
|
||||||
accepted_value: value,
|
accepted_value: value,
|
||||||
accepted_ballot: ballot
|
accepted_ballot: ballot
|
||||||
} end)
|
}
|
||||||
|
end
|
||||||
else
|
else
|
||||||
IO.puts("#{state.name} -> #{proc} nack")
|
IO.puts("#{state.name} -> #{proc} nack")
|
||||||
Utils.unicast({:nack, inst, ballot}, proc)
|
safecast(proc, {:nack, inst, ballot})
|
||||||
state
|
state
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -284,14 +351,14 @@ defmodule Paxos do
|
|||||||
has_finished(state, inst) ->
|
has_finished(state, inst) ->
|
||||||
state
|
state
|
||||||
|
|
||||||
state.leader == state.name and Ballot.compare(state.instmap[inst].ballot, &==/2, ballot) ->
|
state.leader == state.name and state.instmap[inst].ballot == ballot ->
|
||||||
accepted(
|
set_instmap do
|
||||||
set_instmap(state, inst, fn map -> %{
|
%{ map |
|
||||||
map
|
accepted: map.accepted + 1
|
||||||
| accepted: map.accepted + 1
|
}
|
||||||
} end),
|
end
|
||||||
inst
|
|
||||||
)
|
accepted( state, inst)
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
state
|
state
|
||||||
@ -334,11 +401,6 @@ defmodule Paxos do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_instmap(state, inst, set_instmap) do
|
|
||||||
new_instmap = Map.put(state.instmap, inst, set_instmap.(state.instmap[inst]))
|
|
||||||
%{state | instmap: new_instmap}
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Puts process in the preapre state
|
# Puts process in the preapre state
|
||||||
#
|
#
|
||||||
@ -364,7 +426,8 @@ defmodule Paxos do
|
|||||||
IO.puts("#{state.name} - sending all prepare #{inst} #{inspect(ballot)}")
|
IO.puts("#{state.name} - sending all prepare #{inst} #{inspect(ballot)}")
|
||||||
EagerReliableBroadcast.broadcast(state.name, {:prepare, state.name, inst, ballot})
|
EagerReliableBroadcast.broadcast(state.name, {:prepare, state.name, inst, ballot})
|
||||||
|
|
||||||
set_instmap(state, inst, fn map -> %{
|
set_instmap do
|
||||||
|
%{
|
||||||
map
|
map
|
||||||
| prepared_values: [],
|
| prepared_values: [],
|
||||||
accepted: 0,
|
accepted: 0,
|
||||||
@ -372,7 +435,8 @@ defmodule Paxos do
|
|||||||
ballot_value: nil,
|
ballot_value: nil,
|
||||||
has_sent_prepare: true,
|
has_sent_prepare: true,
|
||||||
has_sent_accept: false
|
has_sent_accept: false
|
||||||
} end)
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -413,11 +477,12 @@ defmodule Paxos do
|
|||||||
{:accept, inst, state.instmap[inst].ballot, a_val}
|
{:accept, inst, state.instmap[inst].ballot, a_val}
|
||||||
)
|
)
|
||||||
|
|
||||||
set_instmap(state, inst, fn map -> %{
|
set_instmap do
|
||||||
map
|
%{ map |
|
||||||
| ballot_value: a_val,
|
ballot_value: a_val,
|
||||||
has_sent_accept: true
|
has_sent_accept: true
|
||||||
} end)
|
}
|
||||||
|
end
|
||||||
else
|
else
|
||||||
state
|
state
|
||||||
end
|
end
|
||||||
@ -551,3 +616,144 @@ defmodule Ballot do
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defmodule EagerReliableBroadcast do
|
||||||
|
def get_rb_name(name) do
|
||||||
|
String.to_atom(Atom.to_string(name) <> "_rb")
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_non_rb_name(name) do
|
||||||
|
String.to_atom(String.replace(Atom.to_string(name), "_rb", ""))
|
||||||
|
end
|
||||||
|
|
||||||
|
def start(name, processes) do
|
||||||
|
pid = spawn(EagerReliableBroadcast, :init, [name, processes])
|
||||||
|
Utils.register_name(get_rb_name(name), pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Init event must be the first
|
||||||
|
# one after the component is created
|
||||||
|
def init(parent, processes) do
|
||||||
|
state = %{
|
||||||
|
name: get_rb_name(parent),
|
||||||
|
parent: parent,
|
||||||
|
processes: Enum.map(processes, fn name -> get_rb_name(name) end),
|
||||||
|
# Use this data structure to remember IDs of the delivered messages
|
||||||
|
delivered: %{},
|
||||||
|
# Use this variable to remember the last sequence number used to identify a message
|
||||||
|
seq_no: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
run(state)
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(state) do
|
||||||
|
run(
|
||||||
|
receive do
|
||||||
|
# Handle the broadcast request event
|
||||||
|
{:broadcast, m} ->
|
||||||
|
data_msg = {:data, state.name, state.seq_no, m}
|
||||||
|
Utils.beb_broadcast(data_msg, state.processes)
|
||||||
|
%{state | seq_no: state.seq_no + 1}
|
||||||
|
|
||||||
|
{:data, proc, seq_no, m} ->
|
||||||
|
if not Map.has_key?(state.delivered, {proc, seq_no, m}) do
|
||||||
|
data_msg = {:data, proc, seq_no, m}
|
||||||
|
Utils.beb_broadcast(data_msg, state.processes)
|
||||||
|
|
||||||
|
Utils.unicast({:rb_deliver, get_non_rb_name(proc), m}, state.parent)
|
||||||
|
%{state | delivered: Map.put(state.delivered, {proc, seq_no, m}, 1)}
|
||||||
|
else
|
||||||
|
val = Map.get(state.delivered, {proc, seq_no, m})
|
||||||
|
|
||||||
|
if val < Enum.count(state.processes) do
|
||||||
|
%{state | delivered: Map.put(state.delivered, {proc, seq_no, m}, val + 1)}
|
||||||
|
else
|
||||||
|
%{state | delivered: Map.delete(state.delivered, {proc, seq_no, m})}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def broadcast(name, m) do
|
||||||
|
Utils.unicast({:broadcast, m}, get_rb_name(name))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Emits {:ele_trust, proc }
|
||||||
|
#
|
||||||
|
|
||||||
|
defmodule EventualLeaderElector do
|
||||||
|
def getEleName(name) do
|
||||||
|
String.to_atom(Atom.to_string(name) <> "_ele")
|
||||||
|
end
|
||||||
|
|
||||||
|
def start(name, processes) do
|
||||||
|
new_name = getEleName(name)
|
||||||
|
pid = spawn(EventualLeaderElector, :init, [new_name, name, processes])
|
||||||
|
|
||||||
|
Utils.register_name(new_name, pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Init event must be the first
|
||||||
|
# one after the component is created
|
||||||
|
def init(name, parent, processes) do
|
||||||
|
processes = Enum.map(processes, fn name -> getEleName(name) end)
|
||||||
|
|
||||||
|
state = %{
|
||||||
|
name: name,
|
||||||
|
parent: parent,
|
||||||
|
processes: processes,
|
||||||
|
timeout: 1000,
|
||||||
|
heard_back: MapSet.new(),
|
||||||
|
seq: 0,
|
||||||
|
last_trust: nil
|
||||||
|
}
|
||||||
|
|
||||||
|
run(request_heartbeats(state))
|
||||||
|
end
|
||||||
|
|
||||||
|
def request_heartbeats(state) do
|
||||||
|
state = %{state | heard_back: MapSet.new(), seq: state.seq + 1}
|
||||||
|
Utils.beb_broadcast({:heartbeat_request, state.name, state.seq}, state.processes)
|
||||||
|
|
||||||
|
Process.send_after(self(), {:timeout}, state.timeout)
|
||||||
|
state
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(state) do
|
||||||
|
run(
|
||||||
|
receive do
|
||||||
|
{:heartbeat_request, name, seq} ->
|
||||||
|
Utils.unicast({:heartbeat, state.parent, seq}, name)
|
||||||
|
state
|
||||||
|
|
||||||
|
{:heartbeat, name, seq} ->
|
||||||
|
if seq == state.seq do
|
||||||
|
%{state | heard_back: MapSet.put(state.heard_back, name)}
|
||||||
|
else
|
||||||
|
state
|
||||||
|
end
|
||||||
|
|
||||||
|
{:timeout} ->
|
||||||
|
state =
|
||||||
|
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)
|
||||||
|
|
||||||
|
if state.last_trust != to_trust do
|
||||||
|
Utils.unicast({:ele_trust, to_trust}, state.parent)
|
||||||
|
%{state | last_trust: to_trust}
|
||||||
|
else
|
||||||
|
state
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
request_heartbeats(state)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
17
lib/test.ex
Normal file
17
lib/test.ex
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
defmodule Test do
|
||||||
|
|
||||||
|
defmacro createfuncBase(name) do
|
||||||
|
quote do
|
||||||
|
def unquote(name)(true), do: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
defmodule Test2 do
|
||||||
|
require Test
|
||||||
|
|
||||||
|
def test(), do: false
|
||||||
|
|
||||||
|
Test.createfuncBase(:test)
|
||||||
|
end
|
@ -1,7 +1,4 @@
|
|||||||
# Replace with your own implementation source files
|
# Replace with your own implementation source files
|
||||||
IEx.Helpers.c("utils.ex", ".")
|
|
||||||
IEx.Helpers.c("eager_reliable_broadcast.ex", ".")
|
|
||||||
IEx.Helpers.c("leader_elector.ex", ".")
|
|
||||||
IEx.Helpers.c("paxos.ex", ".")
|
IEx.Helpers.c("paxos.ex", ".")
|
||||||
|
|
||||||
# Do not modify the following ##########
|
# Do not modify the following ##########
|
||||||
|
27
lib/utils.ex
27
lib/utils.ex
@ -1,27 +0,0 @@
|
|||||||
defmodule Utils do
|
|
||||||
def unicast(m, p) do
|
|
||||||
case :global.whereis_name(p) do
|
|
||||||
pid when is_pid(pid) -> send(pid, m)
|
|
||||||
:undefined -> :ok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def beb_broadcast(m, dest), do: for(p <- dest, do: unicast(m, p))
|
|
||||||
|
|
||||||
def register_name(name, pid, link \\ true) do
|
|
||||||
case :global.re_register_name(name, pid) do
|
|
||||||
:yes ->
|
|
||||||
# Note this is running on the parent so we are linking the parent to the rb
|
|
||||||
# so that when we close the parent the rb also dies
|
|
||||||
if link do
|
|
||||||
Process.link(pid)
|
|
||||||
end
|
|
||||||
|
|
||||||
pid
|
|
||||||
|
|
||||||
:no ->
|
|
||||||
Process.exit(pid, :kill)
|
|
||||||
:error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Reference in New Issue
Block a user