From 1c197381b7f1d7351656a02e1bb26a75359a5aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20B=C3=B6hme?= Date: Wed, 7 Aug 2024 10:03:31 +0200 Subject: [PATCH] feat: first supervision tree --- .gitignore | 2 +- lib/todo/cache.ex | 4 +- lib/todo/database.ex | 90 ++++++++++++++--------------------- lib/todo/database_worker.ex | 34 ++++++++----- lib/todo/process_registry.ex | 18 +++++++ lib/todo/server.ex | 4 +- lib/todo/system.ex | 7 ++- persist/a | Bin 198 -> 0 bytes 8 files changed, 85 insertions(+), 74 deletions(-) create mode 100644 lib/todo/process_registry.ex delete mode 100644 persist/a diff --git a/.gitignore b/.gitignore index 0aa57d7..47940df 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,4 @@ erl_crash.dump /config/*.secret.exs .elixir_ls/ -### Elixir Patch ### +persist diff --git a/lib/todo/cache.ex b/lib/todo/cache.ex index b774a1c..32b48c8 100644 --- a/lib/todo/cache.ex +++ b/lib/todo/cache.ex @@ -5,8 +5,6 @@ defmodule Todo.Cache do def init(_init_args) do IO.puts("Starting #{__MODULE__}") - Todo.Database.start() - {:ok, %{}} end @@ -17,7 +15,7 @@ defmodule Todo.Cache do {:reply, process, state} :error -> - {:ok, new_process} = Todo.Server.start(name) + {:ok, new_process} = Todo.Server.start_link(name) new_state = Map.put(state, name, new_process) diff --git a/lib/todo/database.ex b/lib/todo/database.ex index 2660223..9d67f38 100644 --- a/lib/todo/database.ex +++ b/lib/todo/database.ex @@ -1,68 +1,50 @@ defmodule Todo.Database do - alias Todo.DatabaseWorker - use GenServer + use Supervisor @db_folder "./persist" - @num_of_workers 3 + @pool_size 3 - def start do - GenServer.start(__MODULE__, nil, name: __MODULE__) + def start_link do + Supervisor.start_link(__MODULE__, nil) + 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 def store(key, data) do - GenServer.cast(__MODULE__, {:store, key, data}) + key + |> choose_worker() + |> Todo.DatabaseWorker.store(key, data) end def get(key) do - GenServer.call(__MODULE__, {:get, key}) + key + |> choose_worker() + |> Todo.DatabaseWorker.get(key) end - @impl GenServer - def init(_) do - IO.puts("Starting #{__MODULE__}") - - 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() - - {:noreply, workers} - end - - @impl GenServer - def handle_cast({:store, key, data}, workers) do - workers - |> choose_worker(key) - |> DatabaseWorker.store(key, data) - - {:noreply, workers} - end - - @impl GenServer - def handle_call({:get, key}, _, workers) do - data = - workers - |> choose_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) + defp choose_worker(key) do + :erlang.phash2(key, @pool_size) end end diff --git a/lib/todo/database_worker.ex b/lib/todo/database_worker.ex index 4ad902a..8005d0c 100644 --- a/lib/todo/database_worker.ex +++ b/lib/todo/database_worker.ex @@ -1,36 +1,44 @@ defmodule Todo.DatabaseWorker do use GenServer - def start(db_folder) do - GenServer.start(__MODULE__, db_folder) + def start_link({_db_folder, worker_id} = state) do + GenServer.start_link(__MODULE__, state, name: via_tuple(worker_id)) end - def store(pid, key, data) do - GenServer.cast(pid, {:store, key, data}) + def store(worker_id, key, data) do + worker_id + |> via_tuple() + |> GenServer.cast({:store, key, data}) end - def get(pid, key) do - GenServer.call(pid, {:get, key}) + def get(worker_id, key) do + worker_id + |> via_tuple() + |> GenServer.call({:get, key}) + end + + defp via_tuple(worker_id) do + Todo.ProcessRegistry.via_tuple({__MODULE__, worker_id}) end @impl GenServer - def init(db_folder) do - IO.puts("Starting #{__MODULE__} with db folder #{db_folder}.") + def init({db_folder, worker_id} = state) do + IO.puts("Starting #{__MODULE__} #{worker_id} with db folder #{db_folder}.") - {:ok, db_folder} + {:ok, state} end @impl GenServer - def handle_cast({:store, key, data}, db_folder) do + def handle_cast({:store, key, data}, {db_folder, _worker_id} = state) do {db_folder, key} |> file_name() |> File.write!(:erlang.term_to_binary(data)) - {:noreply, db_folder} + {:noreply, state} end @impl GenServer - def handle_call({:get, key}, _, db_folder) do + def handle_call({:get, key}, _, {db_folder, _worker_id} = state) do data = case File.read(file_name({db_folder, key})) do {:ok, contents} -> @@ -40,7 +48,7 @@ defmodule Todo.DatabaseWorker do nil end - {:reply, data, db_folder} + {:reply, data, state} end def file_name({db_folder, key}) do diff --git a/lib/todo/process_registry.ex b/lib/todo/process_registry.ex new file mode 100644 index 0000000..37e8060 --- /dev/null +++ b/lib/todo/process_registry.ex @@ -0,0 +1,18 @@ +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 diff --git a/lib/todo/server.ex b/lib/todo/server.ex index a881bc6..21b0dba 100644 --- a/lib/todo/server.ex +++ b/lib/todo/server.ex @@ -1,8 +1,8 @@ defmodule Todo.Server do use GenServer - def start(name) do - GenServer.start(__MODULE__, name) + def start_link(name) do + GenServer.start_link(__MODULE__, name) end def add(pid, entry) do diff --git a/lib/todo/system.ex b/lib/todo/system.ex index c9ff243..4955c93 100644 --- a/lib/todo/system.ex +++ b/lib/todo/system.ex @@ -5,9 +5,14 @@ defmodule Todo.System do Supervisor.start_link(__MODULE__, nil) end + @impl Supervisor def init(_) do Supervisor.init( - [Todo.Cache], + [ + Todo.ProcessRegistry, + Todo.Database, + Todo.Cache + ], strategy: :one_for_one ) end diff --git a/persist/a b/persist/a deleted file mode 100644 index dca63f37477f53429f5e68755f2497b2cc76cffd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 198 zcmZ9G!3x4K5JaanrA04-|IwdN4}#!9?_t|rXdq1?U7mHF1L`lMbWBBp>eS1azM0O`kTwNb)FxQ|07EP}dH?_b