2. Adding a GenServer - abarr/remote GitHub Wiki
Requirements
The following requirements focus on the application starting a GenServer
.
:remote
app launchesGenServer
when the application starts- Holds state %{max_number: :integer, timestamp: :etc_datetime}
:timestamp
starts asnil
and is updated each time someone queries theGenServer
:timestamp
represents the last time someone queried theGenServer
- 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
- The
GenServer
accepts a call to return Users with points greater than:max_number
with a limit of2
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 inapplication.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
andmax
values will be defined in configuration and default to0
and100
, respectively - The limit for records returned will be defined in configuration and default to
2
Steps to Add GenServer
- Add the
GenServer
to theUsers
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
- Update the configuration to provide the interval for the
GenServer
to update theUsers
table
"/config/config.exs"
config :remote,
...
update_interval: 60_000
- Add the
GenServer
to theapplication.ex
to be started under the supervision ofRemote.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
...