Compare commits
No commits in common. "7ba3b1cd4fa63d719db9a60792871b10679c8cfb" and "1550e896a072869fdd966d511ebc68d1b408ae50" have entirely different histories.
7ba3b1cd4f
...
1550e896a0
8 changed files with 93 additions and 121 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -14,4 +14,4 @@ erl_crash.dump
|
||||||
/config/*.secret.exs
|
/config/*.secret.exs
|
||||||
.elixir_ls/
|
.elixir_ls/
|
||||||
|
|
||||||
persist
|
### Elixir Patch ###
|
||||||
|
|
|
@ -1,25 +1,32 @@
|
||||||
defmodule Todo.Cache do
|
defmodule Todo.Cache do
|
||||||
use DynamicSupervisor
|
use GenServer
|
||||||
|
|
||||||
def start_link(init_arg) do
|
@impl GenServer
|
||||||
IO.puts("Starting #{__MODULE__}")
|
def init(_init_args) do
|
||||||
|
Todo.Database.start()
|
||||||
DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
|
{:ok, %{}}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl DynamicSupervisor
|
@impl GenServer
|
||||||
def init(_) do
|
def handle_call({:server_process, name}, _from, state) do
|
||||||
DynamicSupervisor.init(strategy: :one_for_one)
|
case Map.fetch(state, name) do
|
||||||
end
|
{:ok, process} ->
|
||||||
|
{:reply, process, state}
|
||||||
|
|
||||||
def server_process(todo_list_name) do
|
:error ->
|
||||||
case start_child(todo_list_name) do
|
{:ok, new_process} = Todo.Server.start(name)
|
||||||
{:ok, pid} -> pid
|
|
||||||
{:error, {:already_started, pid}} -> pid
|
new_state = Map.put(state, name, new_process)
|
||||||
|
|
||||||
|
{:reply, new_process, new_state}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp start_child(todo_list_name) do
|
def start() do
|
||||||
DynamicSupervisor.start_child(__MODULE__, {Todo.Server, todo_list_name})
|
GenServer.start(__MODULE__, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def server_process(pid, name) do
|
||||||
|
GenServer.call(pid, {:server_process, name})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,50 +1,67 @@
|
||||||
defmodule Todo.Database do
|
defmodule Todo.Database do
|
||||||
use Supervisor
|
alias Todo.DatabaseWorker
|
||||||
|
use GenServer
|
||||||
|
|
||||||
@db_folder "./persist"
|
@db_folder "./persist"
|
||||||
@pool_size 3
|
@num_of_workers 3
|
||||||
|
|
||||||
def start_link do
|
def start do
|
||||||
Supervisor.start_link(__MODULE__, nil)
|
GenServer.start(__MODULE__, nil, name: __MODULE__)
|
||||||
end
|
|
||||||
|
|
||||||
@impl Supervisor
|
|
||||||
def init(_) do
|
|
||||||
IO.puts("Starting #{__MODULE__}.")
|
|
||||||
|
|
||||||
File.mkdir_p!(@db_folder)
|
|
||||||
|
|
||||||
children = Enum.map(1..@pool_size, &worker_spec/1)
|
|
||||||
|
|
||||||
Supervisor.init(children, strategy: :one_for_one)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp worker_spec(worker_id) do
|
|
||||||
default_worker_spec = {Todo.DatabaseWorker, {@db_folder, worker_id}}
|
|
||||||
Supervisor.child_spec(default_worker_spec, id: worker_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def child_spec(_) do
|
|
||||||
%{
|
|
||||||
id: __MODULE__,
|
|
||||||
start: {__MODULE__, :start_link, []},
|
|
||||||
type: :supervisor
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def store(key, data) do
|
def store(key, data) do
|
||||||
key
|
GenServer.cast(__MODULE__, {:store, key, data})
|
||||||
|> choose_worker()
|
|
||||||
|> Todo.DatabaseWorker.store(key, data)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(key) do
|
def get(key) do
|
||||||
key
|
GenServer.call(__MODULE__, {:get, key})
|
||||||
|> choose_worker()
|
|
||||||
|> Todo.DatabaseWorker.get(key)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp choose_worker(key) do
|
@impl GenServer
|
||||||
:erlang.phash2(key, @pool_size) + 1
|
def init(_) do
|
||||||
|
File.mkdir_p!(@db_folder)
|
||||||
|
|
||||||
|
{:ok, nil, {:continue, :init}}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl GenServer
|
||||||
|
def handle_continue(:init, nil) do
|
||||||
|
File.mkdir_p!(@db_folder)
|
||||||
|
|
||||||
|
workers =
|
||||||
|
0..(@num_of_workers - 1)
|
||||||
|
|> Enum.map(fn i ->
|
||||||
|
{:ok, pid} = Todo.DatabaseWorker.start(@db_folder)
|
||||||
|
{i, pid}
|
||||||
|
end)
|
||||||
|
|> Map.new()
|
||||||
|
|
||||||
|
IO.inspect(workers)
|
||||||
|
{:noreply, workers}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl GenServer
|
||||||
|
def handle_cast({:store, key, data}, workers) do
|
||||||
|
workers
|
||||||
|
|> get_worker(key)
|
||||||
|
|> DatabaseWorker.store(key, data)
|
||||||
|
|
||||||
|
{:noreply, workers}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl GenServer
|
||||||
|
def handle_call({:get, key}, _, workers) do
|
||||||
|
data =
|
||||||
|
workers
|
||||||
|
|> get_worker(key)
|
||||||
|
|> DatabaseWorker.get(key)
|
||||||
|
|
||||||
|
{:reply, data, workers}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp choose_worker(workers, key) do
|
||||||
|
id = :erlang.phash2(key, @num_of_workers)
|
||||||
|
|
||||||
|
Map.fetch!(workers, id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,57 +1,48 @@
|
||||||
defmodule Todo.DatabaseWorker do
|
defmodule Todo.DatabaseWorker do
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
def start_link({_db_folder, worker_id} = state) do
|
def start(db_folder) do
|
||||||
GenServer.start_link(__MODULE__, state, name: via_tuple(worker_id))
|
GenServer.start(__MODULE__, db_folder)
|
||||||
end
|
end
|
||||||
|
|
||||||
def store(worker_id, key, data) do
|
def store(pid, key, data) do
|
||||||
worker_id
|
GenServer.cast(pid, {:store, key, data})
|
||||||
|> via_tuple()
|
|
||||||
|> GenServer.cast({:store, key, data})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(worker_id, key) do
|
def get(pid, key) do
|
||||||
worker_id
|
GenServer.call(pid, {:get, key})
|
||||||
|> via_tuple()
|
|
||||||
|> GenServer.call({:get, key})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp via_tuple(worker_id) do
|
|
||||||
Todo.ProcessRegistry.via_tuple({__MODULE__, worker_id})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl GenServer
|
@impl GenServer
|
||||||
def init({db_folder, worker_id} = state) do
|
def init(db_folder) do
|
||||||
IO.puts("Starting #{__MODULE__} #{worker_id} with db folder #{db_folder}.")
|
{:ok, db_folder}
|
||||||
|
|
||||||
{:ok, state}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl GenServer
|
@impl GenServer
|
||||||
def handle_cast({:store, key, data}, {db_folder, _worker_id} = state) do
|
def handle_cast({:store, key, data}, db_folder) do
|
||||||
{db_folder, key}
|
{db_folder, key}
|
||||||
|> file_name()
|
|> file_name()
|
||||||
|> File.write!(:erlang.term_to_binary(data))
|
|> File.write!(:erlang.term_to_binary(data))
|
||||||
|
|
||||||
{:noreply, state}
|
{:noreply, db_folder}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl GenServer
|
@impl GenServer
|
||||||
def handle_call({:get, key}, _, {db_folder, _worker_id} = state) do
|
def handle_call({:get, key}, _, db_folder) do
|
||||||
data =
|
data =
|
||||||
case File.read(file_name({db_folder, key})) do
|
case File.read(file_name({db_folder, key})) do
|
||||||
{:ok, contents} ->
|
{:ok, contents} ->
|
||||||
:erlang.binary_to_term(contents)
|
:erlang.binary_to_term(contents)
|
||||||
|
|
||||||
{:error, :enoent} ->
|
_ ->
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
{:reply, data, state}
|
{:reply, data, db_folder}
|
||||||
end
|
end
|
||||||
|
|
||||||
def file_name({db_folder, key}) do
|
def file_name({db_folder, key}) do
|
||||||
Path.join(db_folder, to_string(key))
|
Path.join(db_folder, to_string(key))
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
defmodule Todo.ProcessRegistry do
|
|
||||||
def start_link do
|
|
||||||
IO.puts("Starting #{__MODULE__}.")
|
|
||||||
Registry.start_link(keys: :unique, name: __MODULE__)
|
|
||||||
end
|
|
||||||
|
|
||||||
def via_tuple(key) do
|
|
||||||
{:via, Registry, {__MODULE__, key}}
|
|
||||||
end
|
|
||||||
|
|
||||||
def child_spec(_) do
|
|
||||||
Supervisor.child_spec(
|
|
||||||
Registry,
|
|
||||||
id: __MODULE__,
|
|
||||||
start: {__MODULE__, :start_link, []}
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,12 +1,8 @@
|
||||||
defmodule Todo.Server do
|
defmodule Todo.Server do
|
||||||
use GenServer, restart: :temporary
|
use GenServer
|
||||||
|
|
||||||
def start_link(name) do
|
def start(name) do
|
||||||
GenServer.start_link(__MODULE__, name, name: via_tuple(name))
|
GenServer.start(__MODULE__, name)
|
||||||
end
|
|
||||||
|
|
||||||
def via_tuple(name) do
|
|
||||||
Todo.ProcessRegistry.via_tuple({__MODULE__, name})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def add(pid, entry) do
|
def add(pid, entry) do
|
||||||
|
@ -27,8 +23,6 @@ defmodule Todo.Server do
|
||||||
|
|
||||||
@impl GenServer
|
@impl GenServer
|
||||||
def init(name) do
|
def init(name) do
|
||||||
IO.puts("Starting #{__MODULE__} for #{name}.")
|
|
||||||
|
|
||||||
{:ok, {name, nil}, {:continue, :init}}
|
{:ok, {name, nil}, {:continue, :init}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
defmodule Todo.System do
|
|
||||||
use Supervisor
|
|
||||||
|
|
||||||
def start_link do
|
|
||||||
Supervisor.start_link(__MODULE__, nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl Supervisor
|
|
||||||
def init(_) do
|
|
||||||
Supervisor.init(
|
|
||||||
[
|
|
||||||
Todo.ProcessRegistry,
|
|
||||||
Todo.Database,
|
|
||||||
Todo.Cache
|
|
||||||
],
|
|
||||||
strategy: :one_for_one
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
BIN
persist/a
Normal file
BIN
persist/a
Normal file
Binary file not shown.
Loading…
Reference in a new issue