77 lines
2.2 KiB
Elixir
77 lines
2.2 KiB
Elixir
defmodule WhoNeedHelp.TrackingPresenceCleanup do
|
|
@moduledoc false
|
|
|
|
use GenServer
|
|
|
|
alias WhoNeedHelp.Tracking
|
|
alias WhoNeedHelpWeb.Presence
|
|
|
|
def start_link(options), do: GenServer.start_link(__MODULE__, options, name: __MODULE__)
|
|
|
|
def maybe_stop(key, assignment_id, user_id, tracking_session_id) do
|
|
GenServer.cast(
|
|
__MODULE__,
|
|
{:maybe_stop, key, assignment_id, user_id, tracking_session_id}
|
|
)
|
|
end
|
|
|
|
@impl GenServer
|
|
def init(_options) do
|
|
# Phoenix 1.8.9's locked browser client can wait up to 5 seconds between
|
|
# reconnect attempts. Waiting for that same interval prevents a serving
|
|
# node replacement from being mistaken for the user closing the last tab.
|
|
grace_ms =
|
|
Application.fetch_env!(:who_need_help, :tracking_presence_cleanup_grace_ms)
|
|
|
|
if not is_integer(grace_ms) or grace_ms < 0 do
|
|
raise ArgumentError,
|
|
":tracking_presence_cleanup_grace_ms must be a non-negative integer"
|
|
end
|
|
|
|
{:ok, %{grace_ms: grace_ms, pending: %{}}}
|
|
end
|
|
|
|
@impl GenServer
|
|
def handle_cast(
|
|
{:maybe_stop, key, assignment_id, user_id, tracking_session_id},
|
|
state
|
|
) do
|
|
cleanup_key = {key, assignment_id, user_id, tracking_session_id}
|
|
|
|
case Map.get(state.pending, cleanup_key) do
|
|
%{timer_ref: timer_ref} -> Process.cancel_timer(timer_ref)
|
|
nil -> :ok
|
|
end
|
|
|
|
token = make_ref()
|
|
|
|
timer_ref =
|
|
Process.send_after(
|
|
self(),
|
|
{:maybe_stop_after_grace, cleanup_key, token},
|
|
state.grace_ms
|
|
)
|
|
|
|
pending = Map.put(state.pending, cleanup_key, %{timer_ref: timer_ref, token: token})
|
|
|
|
{:noreply, %{state | pending: pending}}
|
|
end
|
|
|
|
@impl GenServer
|
|
def handle_info({:maybe_stop_after_grace, cleanup_key, token}, state) do
|
|
case Map.get(state.pending, cleanup_key) do
|
|
%{token: ^token} ->
|
|
{key, assignment_id, user_id, tracking_session_id} = cleanup_key
|
|
|
|
if Presence.get_by_key(Presence.tracking_topic(), key) == [] do
|
|
Tracking.stop_browser_session(assignment_id, user_id, tracking_session_id)
|
|
end
|
|
|
|
{:noreply, %{state | pending: Map.delete(state.pending, cleanup_key)}}
|
|
|
|
_stale_or_cancelled ->
|
|
{:noreply, state}
|
|
end
|
|
end
|
|
end
|