I am trying to implement a counter using GenServer. The idea is to increment a counter whenever a client visits the server, and print it in the console. However, after every visit GenServer state reset or PID terminated. Is there a way to keep GenServer state persistent?
Here's the code:
counter file in lib/myapp_web/livecount.ex
defmodule Counter do
use GenServer
# Client
def start_link(integer \\ 0, opts \\ []) when is_integer(integer) do
GenServer.start_link(__MODULE__, integer, opts)
end
def add(pid) do
GenServer.call(pid, :add)
end
# Server
@impl true
def handle_call(:add, _from, state) do
value = state + 1
{:reply, "Dashboard hit: #{value}", value}
end
@impl true
def init(value) do
{:ok, value}
end
end
I added the following code in lib/myapp_web/endpoint.ex
def message(conn, _opts) do
Code.require_file("lib/myapp_web/livecount.ex")
{:ok, pid} = Counter.start_link()
message = Counter.add(pid)
IO.puts """
Message: #{inspect(message)}
"""
conn
end
Here is the message in the console:
[info] Sent 200 in 30ms
Message: "Dashboard hit: 1"
[info] CONNECTED TO Phoenix.LiveView.Socket in 143µs
Transport: :websocket
Serializer: Phoenix.Socket.V2.JSONSerializer
Parameters: %{"_csrf_token" => "YyUeC3lgIQ83AlU3VBdoSg9PAwgYGl4QVQW_1PMHOjfg1s72wvoYHW2T", "_mounts" => "0", "vsn" => "2.0.0"}
[info] CONNECTED TO Phoenix.LiveView.Socket in 105µs
Transport: :websocket
Serializer: Phoenix.Socket.V2.JSONSerializer
Parameters: %{"_csrf_token" => "TToLLhxmCAVIXXsJLVIJOjNYC2QIJy0bxNBzTVdB05HYH6VBKag5XjA_", "_mounts" => "0", "vsn" => "2.0.0"}
I want to print
Message: "Dashboard hit: 1"
Message: "Dashboard hit: 2"
Message: "Dashboard hit: 3"
whenever a client visits or reloads the page, and increase counter per all users visit.
There are many things to fix/improve here.
① one should not call
Code.require_file/1
from within runtime, unless absolutely sure it’s the only way; the regular file located inlib
is available by default② one should not start processes unmonitored (unsupervised) from within the regular code; start it within the supervision tree in
application.ex
③ give the process a name to avoid the necessity to track a
pid
④ you probably want to track each user’s visits separately; the current attempt would have a single counter per all users
That said, somewhat as the below would work (assuming the process has been started in
application.ex
within its supervision tree.)and
To track counters per user, one might hold a map
conn => counter
in the state, or like.