concept GenServer in category elixir

appears as: GenServer, GenServer
Elixir in Action

This is an excerpt from Manning's book Elixir in Action.

Listing 1.2 Elixir-based server process that adds two numbers
defmodule SumServer do
  use GenServer

  def start do
    GenServer.start(__MODULE__, nil)
  end

  def sum(server, a, b) do
    GenServer.call(server, {:sum, a, b})
  end

  def handle_call({:sum, a, b}, _from, state) do
    {:reply, a + b, state}
  end
end

6.2.2 Plugging into GenServer

Using GenServer is roughly similar to using ServerProcess. There are some differences in the format of the returned values, but the basic idea is the same.

The GenServer behaviour requires seven callback functions, but frequently you’ll need only a subset of those. You can get some sensible default implementations of all required callback functions if you use the GenServer module:

iex(1)> defmodule KeyValueStore do
          use GenServer
        end

The use macro is a language feature you haven’t seen previously. During compilation, when this instruction is encountered, the specific macro from the GenServer module is invoked. That macro then injects a bunch of functions into the calling module (KeyValueStore, in this case). You can verify this in the shell:

It’s important to always be aware of how GenServer-powered processes tick and where (in which process) various functions are executed. Let’s do a quick recap by looking at figure 6.1, which shows the lifecycle of a typical server process.

A client process starts the server by calling GenServer.start and providing the callback module (1). This creates the new server process, which is powered by the GenServer behaviour.

Requests can be issued by client processes using various GenServer functions or plain send. When a message is received, GenServer invokes callback functions to handle it. Therefore, callback functions are always executed in the server process.

The process state is maintained in the GenServer loop but is defined and manipulated by the callback functions. It starts with init/1, which defines the initial state that’s then passed to subsequent handle_* callbacks (2). Each of these callbacks receives the current state and must return its new version, which is used by the GenServer loop in place of the old one.

Figure 6.1 Lifecycle of a GenServer-powered process

lifecycle.tif

There isn’t enough space in this book to treat every possible OTP-compliant abstraction, so you’ll need to do some research of your own. But it’s worth pointing out that most such abstractions follow the ideas of GenServer. Except for the Task module, all of the OTP abstractions mentioned in this section are internally implemented on top of GenServer. Therefore, in my personal opinion, GenServer is likely the most important part of OTP. If you properly understand the principles of GenServer, most other abstractions should be much easier to grasp.

The Little Elixir & OTP Guidebook

This is an excerpt from Manning's book The Little Elixir & OTP Guidebook.

Listing 1.1. Example GenServer
defmodule WeatherService do
  use GenServer # <- This brings in GenServer behavior
  def handle_call({:temperature, city}, _from, state) do                #1
    # ...
  end
  def handle_cast({:email_weather_report, email}, state) do             #2
    # ...
  end
end

In this book, you’ll work with two of the most-used behaviors: GenServer and Supervisor. Once you’re comfortable with them, learning to use other behaviors will be straightforward. You could roll your own Supervisor behavior, but there’s no good reason to do so 99.999999999% of the time. The implementers have thought long and hard about the features that need to be included in most client-server programs, and they’ve also accounted for concurrency errors and all sorts of edge cases.

How do you use an OTP behavior? The following listing shows a minimal implementation of a weather service that uses GenServer.

Listing 1.1. Example GenServer
defmodule WeatherService do
  use GenServer # <- This brings in GenServer behavior
  def handle_call({:temperature, city}, _from, state) do                #1
    # ...
  end
  def handle_cast({:email_weather_report, email}, state) do             #2
    # ...
  end
end

Once you understand the core principles of OTP, you’ll learn about one of the most important and common OTP behaviors: GenServer. Short for Generic Server, the GenServer behavior is an abstraction of client/server functionality. You’ll take Metex, the temperature-reporting application that you built in chapter 3, and turn it into a GenServer. By then, you’ll have a firm grasp of how to implement your own GenServers.

There are also other benefits to using a GenServer behavior. When you’re building a server application, for example, how do you know you’ve covered all the necessary edge cases and concurrency issues that may crop up? The truth is you probably won’t, even with all your tests. GenServer (and the other behaviors, for that matter) are production-tested and battle-hardened.

It also wouldn’t be fun to have to understand multiple different implementations of server logic. Consider worker.ex in the Metex example. In my programs that don’t use the GenServer behavior, I usually name the main loop, well, loop. But nothing is stopping me from naming it await, recur, or something ridiculous like while_1_true. Using the GenServer behavior releases me (and naming-challenged developers) from the burden of having to think about these trivialities by enforcing standard naming conventions via its callbacks.

Elixir in Action

This is an excerpt from Manning's book Elixir in Action.

Listing 1.2. Elixir-based server process that adds two numbers

In total, the gen_server behaviour requires six callback functions, but frequently you’ll need only a subset of those. To simplify the implementation, you can reach for Elixir’s GenServer module. You can get some sensible default implementations of all required callback functions if you use the GenServer module:

iex(1)> defmodule KeyValueStore do
          use GenServer
        end

The use macro is a language construct you haven’t seen previously. During compilation, when this instruction is encountered, the specific macro from the GenServer module is invoked. That macro then injects a bunch of functions into the calling module (KeyValueStore, in this case). You can verify this in the shell:

sitemap

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage
test yourself with a liveTest