Packages

(BETA) Distributed shared dictionary using master/slave replication and slave to master failover

Current section

Files

Jump to
distributed_attribute_server_application lib distributed_attribute_server master.ex
Raw

lib/distributed_attribute_server/master.ex

defmodule DistributedAttributeServer.Master do
defmacro __using__(_opts) do
quote do
alias DistributedAttributeServer.States.MasterState, as: MasterState
alias DistributedAttributeServer.States.SlaveState, as: SlaveState
def run_for_master key do
if :global.set_lock {key, self}, Elixir.Node.list ++ [Elixir.Node.self], 0 do
true
else
if master_assigned? do
# if another master was assigned, we did not win the race for the lock
false
else
# keep doing this until we become master or someone else becomes master
run_for_master key
end
end
end
def init_master do
SlaveState.remove_component __MODULE__
MasterState.mark_component __MODULE__
log "SELF: promoting to master"
{:ok, pid} = :gen_server.start_link(__MODULE__, %{}, [])
unless registered_locally? do
Process.register pid, __MODULE__
end
:global.register_name(__MODULE__, pid)
receive_pid = spawn fn -> master_listen end
:global.register_name(master_receive_pid_name, receive_pid)
{:ok, pid}
end
def master_listen do
if master? do
receive do
{client, :subscribe} ->
log "MASTER LISTENER: got `subscribe` from #{:i.i client}"
MasterState.append_slave __MODULE__, %{client => 1}
log "MASTER: sending `set_all` #{:i.i all} to #{:i.i client}"
send client, {:set_all, all}
{client, :set, attr, value} ->
log "MASTER LISTENER: got `set` #{:i.i attr}=#{:i.i value} from #{:i.i client}"
set(attr, value)
{client, :set_all, attributes} ->
log "MASTER LISTENER: got `set_all` #{:i.i attributes} from #{:i.i client}"
set_all attributes
{client, :get, attr} ->
log "MASTER LISTENER: got `get` #{:i.i attr} from #{:i.i client}"
send(client, {:get_response, get(attr)})
{client, :all} ->
log "MASTER LISTENER: got `all` from #{:i.i client}"
send(client, {:all_response, all})
{client, :clear} ->
log "MASTER LISTENER: got `clear` from #{:i.i client}"
clear
{client, :size} ->
log "MASTER LISTENER: got `size` from #{:i.i client}"
send(client, {:size_response, size})
_ ->
IO.puts :stderr, "Unexpected message received"
end
master_listen
end
end
def master_broadcast(payload) do
slaves = (MasterState.get(__MODULE__) || %{})[:slaves] || %{}
Enum.each slaves, fn({client, client_info}) ->
log "MASTER: sending #{:i.i payload} to #{:i.i client}"
send client, payload
end
end
def master_receive_pid_name do
((__MODULE__ |> to_string) <> "." <> "Master" <> ".Receive") |> String.to_atom
end
def master_receive_pid do
:global.whereis_name master_receive_pid_name
end
def master? do
MasterState.marked? __MODULE__
end
defp master_assigned? do
Enum.find :global.registered_names, fn(name) ->
name == __MODULE__
end
end
end
end
end