2. Adding a GenServer - abarr/remote GitHub Wiki
Requirements
The following requirements focus on the application starting a GenServer.
:remoteapp launchesGenServerwhen the application starts- Holds state %{max_number: :integer, timestamp: :etc_datetime}
:timestampstarts asniland is updated each time someone queries theGenServer:timestamprepresents the last time someone queried theGenServer
- Every 60 seconds, the
GenServerrund a job
- The
GenServerupdates every user's points value with a random value between 0..100 - The
Genserverupdates:max_numberin the state to a new random number between 0..100
- The
GenServeraccepts a call to return Users with points greater than:max_numberwith a limit of2and the:timestampfrom the previous call.
Assumptions
I have made the following assumptions:
- The
GenServerwill be called:user_server - The
Remote.Supervisorwill supervise the:user_serverusing the default strategy detailed inapplication.ex - The
:nameof the:user_serverwill default to__MODULE__but can be passed in to simplify testing - The interval will be defined in configuration and default to 60 seconds
- The
minandmaxvalues will be defined in configuration and default to0and100, respectively - The limit for records returned will be defined in configuration and default to
2
Steps to Add GenServer
- Add the
GenServerto theUserscontext 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
GenServerto update theUserstable
"/config/config.exs"
config :remote,
...
update_interval: 60_000
- Add the
GenServerto theapplication.exto 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
...