2. Adding a GenServer - abarr/remote GitHub Wiki

Requirements

The following requirements focus on the application starting a GenServer.

  1. :remote app launches GenServer when the application starts
  2. Holds state %{max_number: :integer, timestamp: :etc_datetime}
  • :timestamp starts as nil and is updated each time someone queries the GenServer
  • :timestamp represents the last time someone queried the GenServer
  1. Every 60 seconds, the GenServer rund a job
  • The GenServer updates every user's points value with a random value between 0..100
  • The Genserver updates :max_number in the state to a new random number between 0..100
  1. The GenServer accepts a call to return Users with points greater than :max_number with a limit of 2 and the :timestamp from the previous call.

Assumptions

I have made the following assumptions:

  • The GenServer will be called :user_server
  • The Remote.Supervisor will supervise the :user_server using the default strategy detailed in application.ex
  • The :name of the :user_server will default to __MODULE__ but can be passed in to simplify testing
  • The interval will be defined in configuration and default to 60 seconds
  • The min and max values will be defined in configuration and default to 0 and 100, respectively
  • The limit for records returned will be defined in configuration and default to 2

See feedback

Steps to Add GenServer

  1. Add the GenServer to the Users context directory
"/remote/users/user_server.ex"

defmodule Remote.Users.UserServer do
  @moduledoc false
  use GenServer

  @update_interval Application.get_env(:remote, :update_interval) || 60_000

  def start_link(name: name) do
    GenServer.start_link(__MODULE__, nil, name: name)
  end

  def start_link(_) do
    GenServer.start_link(__MODULE__, nil, name: __MODULE__)
  end

  @impl true
  def init(_) do
    schedule_update(@update_interval)
    {:ok, %{max_number: Enum.random(0..100), timestamp: nil}}
  end

  @impl true
  def handle_info(:update_user_points, state) do
    time = DateTime.utc_now() |> DateTime.to_time()
    state.max_number |> IO.inspect(label: "#{time} - Updating points, current max_number: ")
    schedule_update(@update_interval)
    {:noreply, %{state | max_number: Enum.random(0..100)}}
  end

  # set timer
  defp schedule_update(interval) do
    Process.send_after(self(), :update_user_points, interval)
  end
end
  1. Update the configuration to provide the interval for the GenServer to update the Users table
"/config/config.exs"

config :remote,
  ...
  update_interval: 60_000
  1. Add the GenServer to the application.ex to be started under the supervision of Remote.Supervisor
"/lib/application.ex"

def start(_type, _args) do
 ... 
 children = [
   ...
   # Start the Remote.Users.UserServer
   Remote.Users.UserServer
   ...
 ]
 ...
end

With these changes, I run the server and look for a message after 50 secs and every 60 secs that follow.


$ mix phx.server

...
[info] Running RemoteWeb.Endpoint with cowboy 2.9.0 at 127.0.0.1:4000 (http)
[info] Access RemoteWeb.Endpoint at http://localhost:4000
06:53:54.106414 - Updating points, current max_number: : 65
06:54:54.123611 - Updating points, current max_number: : 54
...