Mix.install([
{:dgen, path: "../dgen"},
{:erlfdb, "~> 0.3"}
])Regular GenServer
A simple chat server that stores messages in memory.
defmodule Message do
defstruct [:id, :name, :text]
end
defmodule Chat do
use GenServer
defstruct idx: 0, messages: []
def start(), do: GenServer.start(__MODULE__, [])
def restart(pid) do
GenServer.stop(pid)
start()
end
def new_message(pid, name, text), do: GenServer.call(pid, {:new_message, name, text})
def history(pid), do: GenServer.call(pid, :history)
@impl true
def init([]) do
{:ok, %Chat{}}
end
@impl true
def handle_call({:new_message, name, text}, _from, state=%Chat{}) do
%{messages: messages, idx: idx} = state
message = %Message{id: "#{idx+1}", name: name, text: text}
resp_text = "Thanks for the message"
response = %Message{id: "#{idx+2}", name: "system", text: resp_text}
{:reply, resp_text, %{state | idx: idx+2, messages: messages ++ [message, response]}}
end
@impl true
def handle_call(:history, _from, state=%Chat{}) do
%{messages: messages} = state
{:reply, messages, state}
end
end{:ok, pid} = Chat.start()Send a message and get a response.
Chat.new_message(pid, "Alice", "Hello there")Chat.history(pid)Now restart the server.
{:ok, pid} = Chat.restart(pid)The history is lost — regular GenServers don't persist state.
Chat.history(pid)Durable GenServer
The same chat server, but using DGen.Server for automatic persistence.
defmodule DChat do
use DGen.Server
defstruct idx: 0, messages: []
def start(tenant), do: DGen.Server.start(__MODULE__, [], [tenant: tenant])
def restart(pid, tenant) do
GenServer.stop(pid)
start(tenant)
end
def new_message(pid, name, text), do: DGen.Server.call(pid, {:new_message, name, text})
def history(pid), do: DGen.Server.call(pid, :history)
@impl true
def init([]) do
{:ok, %DChat{}}
end
@impl true
def handle_call({:new_message, name, text}, _from, state=%DChat{}) do
%{messages: messages, idx: idx} = state
message = %Message{id: "#{idx+1}", name: name, text: text}
resp_text = "Thanks for the message"
response = %Message{id: "#{idx+2}", name: "system", text: resp_text}
{:reply, resp_text, %{state | idx: idx+2, messages: messages ++ [message, response]}}
end
@impl true
def handle_call(:history, _from, state=%DChat{}) do
%{messages: messages} = state
{:reply, messages, state}
end
endb = :dgen_config.backend()
tenant = b.sandbox_open("DGen.Livebook", "Intro")Create a tenant to isolate this server's data.
{:ok, pid} = DChat.start(tenant)DChat.new_message(pid, "Bob", "Greetings")DChat.history(pid)Restart the durable server.
{:ok, pid} = DChat.restart(pid, tenant)The history persists — DGen.Server automatically saves and restores state.
DChat.history(pid)