Compare commits

...

2 Commits

Author SHA1 Message Date
Moritz Böhme 1550e896a0
feat: add database workers 2024-08-07 00:48:49 +02:00
Moritz Böhme a408a38ffb
feat: add database 2024-08-07 00:09:00 +02:00
5 changed files with 161 additions and 47 deletions

View File

@ -3,6 +3,7 @@ defmodule Todo.Cache do
@impl GenServer @impl GenServer
def init(_init_args) do def init(_init_args) do
Todo.Database.start()
{:ok, %{}} {:ok, %{}}
end end
@ -13,7 +14,7 @@ defmodule Todo.Cache do
{:reply, process, state} {:reply, process, state}
:error -> :error ->
new_process = Todo.Server.start() {:ok, new_process} = Todo.Server.start(name)
new_state = Map.put(state, name, new_process) new_state = Map.put(state, name, new_process)

67
lib/todo/database.ex Normal file
View File

@ -0,0 +1,67 @@
defmodule Todo.Database do
alias Todo.DatabaseWorker
use GenServer
@db_folder "./persist"
@num_of_workers 3
def start do
GenServer.start(__MODULE__, nil, name: __MODULE__)
end
def store(key, data) do
GenServer.cast(__MODULE__, {:store, key, data})
end
def get(key) do
GenServer.call(__MODULE__, {:get, key})
end
@impl GenServer
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

View File

@ -0,0 +1,48 @@
defmodule Todo.DatabaseWorker do
use GenServer
def start(db_folder) do
GenServer.start(__MODULE__, db_folder)
end
def store(pid, key, data) do
GenServer.cast(pid, {:store, key, data})
end
def get(pid, key) do
GenServer.call(pid, {:get, key})
end
@impl GenServer
def init(db_folder) do
{:ok, db_folder}
end
@impl GenServer
def handle_cast({:store, key, data}, db_folder) do
{db_folder, key}
|> file_name()
|> File.write!(:erlang.term_to_binary(data))
{:noreply, db_folder}
end
@impl GenServer
def handle_call({:get, key}, _, db_folder) do
data =
case File.read(file_name({db_folder, key})) do
{:ok, contents} ->
:erlang.binary_to_term(contents)
_ ->
nil
end
{:reply, data, db_folder}
end
def file_name({db_folder, key}) do
Path.join(db_folder, to_string(key))
end
end

View File

@ -1,52 +1,8 @@
defmodule Todo.Server do defmodule Todo.Server do
use GenServer use GenServer
@impl GenServer def start(name) do
def init(%_{} = todo_list) do GenServer.start(__MODULE__, name)
{:ok, todo_list}
end
@impl GenServer
def init(entries) do
{:ok, Todo.List.new(entries)}
end
@impl GenServer
def handle_call({:entries, date}, _from, todo_list) do
entries = Todo.List.entries(todo_list, date)
{:reply, entries, todo_list}
end
@impl GenServer
def handle_cast({:add, entry}, todo_list) do
new_todo_list = Todo.List.add(todo_list, entry)
{:noreply, new_todo_list}
end
@impl GenServer
def handle_cast({:update, id, update_fun}, todo_list) do
new_todo_list = Todo.List.update(todo_list, id, update_fun)
{:noreply, new_todo_list}
end
@impl GenServer
def handle_cast({:delete, id}, todo_list) do
new_todo_list = Todo.List.delete(todo_list, id)
{:noreply, new_todo_list}
end
def start(entries \\ [])
def start(%_{} = todo_list) do
GenServer.start(__MODULE__, todo_list)
end
def start(entries) do
GenServer.start(__MODULE__, entries)
end end
def add(pid, entry) do def add(pid, entry) do
@ -64,4 +20,46 @@ defmodule Todo.Server do
def delete(pid, id) do def delete(pid, id) do
GenServer.cast(pid, {:delete, id}) GenServer.cast(pid, {:delete, id})
end end
@impl GenServer
def init(name) do
{:ok, {name, nil}, {:continue, :init}}
end
@impl GenServer
def handle_continue(:init, {name, nil}) do
todo_list = Todo.Database.get(name) || Todo.List.new()
{:noreply, {name, todo_list}}
end
@impl GenServer
def handle_call({:entries, date}, _from, {name, todo_list}) do
entries = Todo.List.entries(todo_list, date)
{:reply, entries, {name, todo_list}}
end
@impl GenServer
def handle_cast({:add, entry}, {name, todo_list}) do
new_todo_list = Todo.List.add(todo_list, entry)
Todo.Database.store(name, new_todo_list)
{:noreply, {name, new_todo_list}}
end
@impl GenServer
def handle_cast({:update, id, update_fun}, {name, todo_list}) do
new_todo_list = Todo.List.update(todo_list, id, update_fun)
Todo.Database.store(name, new_todo_list)
{:noreply, {name, new_todo_list}}
end
@impl GenServer
def handle_cast({:delete, id}, {name, todo_list}) do
new_todo_list = Todo.List.delete(todo_list, id)
Todo.Database.store(name, new_todo_list)
{:noreply, {name, new_todo_list}}
end
end end

BIN
persist/a Normal file

Binary file not shown.