ELIXIR / CHAPTER 12

Serialized state with GenServer

Advanced

Keep shared logical state owned by one process.

By the end of this chapter

  • Separate client calls from callbacks
  • Update and reply in one server operation
  • Avoid blocking a state owner with unrelated long work

The idea

A GenServer provides a process that owns state and handles requests through callbacks. A synchronous call expects a reply, while a cast sends an asynchronous request without that direct reply contract. The callback returns both its response behavior and the next state, making state transitions explicit. Clients interact through a small API rather than reading a shared mutable variable.

Operations that must be atomic should be expressed as one request. A client-side read followed by a separate write permits another request to interleave between them. Server callbacks are serialized, so lengthy blocking work can delay all clients; move suitable independent work elsewhere while retaining a clear ownership protocol. Test state transitions and request ordering instead of relying on timing guesses.

Elixir example
defmodule CounterServer do
  use GenServer
  def init(n), do: {:ok, n}
  def handle_call(:next, _from, n), do: {:reply, n + 1, n + 1}
end
{:ok, pid} = GenServer.start_link(CounterServer, 0)
IO.inspect(GenServer.call(pid, :next))
IO.inspect(GenServer.call(pid, :next))
Result
1
2

Each call increments and replies in one serialized callback, and the returned next state is used by the following request.

Try it yourself

Implement a reserve operation that checks available capacity and subtracts it in one callback.

Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.

CHECK YOUR UNDERSTANDING

Chapter checkpoint

Why combine checking a balance and decrementing it in one server request?