src/lake_heartbeat.erl

-module(lake_heartbeat).

-behaviour(gen_server).

-export([start_link/2]).
-export([init/1,
         handle_call/3,
         handle_cast/2,
         handle_info/2]).

start_link(Heartbeat, Connection) ->
    gen_server:start_link(?MODULE, [Heartbeat, Connection], []).

init([Heartbeat, Connection]) ->
    {ok, _} = timer:send_interval(Heartbeat * 1000, check_heartbeat),
    ok = gen_server:cast(Connection, heartbeat),
    {ok,
     #{heartbeat => Heartbeat,
       connection => Connection,
       last_heartbeat => undefined}}.

handle_call(_, _From, State) ->
    {reply, unknown, State}.

handle_cast({heartbeat}, State) ->
    {noreply, State#{last_heartbeat => erlang:monotonic_time(second)}}.

handle_info(check_heartbeat,
            State =
                #{heartbeat := Heartbeat,
                  last_heartbeat := LastHeartbeat,
                  connection := Connection}) ->
    Now = erlang:monotonic_time(second),
    if Now - 2 * Heartbeat > LastHeartbeat ->
           {stop, heartbeat_timeout, State};
       true ->
           ok = gen_server:cast(Connection, heartbeat),
           {noreply, State}
    end.