defmodule Guesswork.Telemetry.QueryRun do @moduledoc """ Represents a single 'run', or resolution, of a query. """ defstruct [:query_id, :run_id, :start_time, :end_time, :duration_ms, :error] @type t() :: %__MODULE__{ query_id: String.t(), run_id: String.t(), start_time: DateTime.t(), end_time: DateTime.t() | nil, duration_ms: integer() | nil, error: nil | term() } @doc """ Attempts to build a new query run entry using the needed ids and the start time provided by telemetry (assumes a `:native` unix time). """ @spec new(String.t(), String.t(), integer()) :: {:ok, t()} | {:error, atom()} def new(query_id, run_id, start_time) do case DateTime.from_unix(start_time, :native) do {:ok, start_time} -> {:ok, %__MODULE__{ query_id: query_id, run_id: run_id, start_time: start_time, end_time: nil, duration_ms: nil, error: nil }} {:error, e} -> {:error, e} end end @doc """ Returns a key used for storage in ets. """ @spec ets_key(t()) :: {String.t(), String.t()} def ets_key(%__MODULE__{query_id: query_id, run_id: run_id}) do {query_id, run_id} end @doc """ Takes a query run and the `duration` of the run then completes the record by calculating and inserting the `end_time` and `duration`. `duration` is assumed to be `:native` unix times. """ @spec finish(t(), integer()) :: {:ok, t()} | {:error, atom()} def finish( %__MODULE__{start_time: start_time, end_time: nil, duration_ms: nil, error: nil} = run, duration ) do duration_ms = System.convert_time_unit(duration, :native, :millisecond) {:ok, %__MODULE__{ run | end_time: DateTime.add(start_time, duration_ms, :millisecond), duration_ms: duration_ms }} end def finish(%__MODULE__{}, _duration), do: {:error, :already_finished} @doc """ Takes a query run the `duration` and the error that failed the query, and then completes the record by calculating and inserting the `end_time` and `duration`. `duration` is assumed to be `:native` unix times. """ @spec finish(t(), integer(), term()) :: {:ok, t()} | {:error, atom()} def finish( %__MODULE__{start_time: start_time, end_time: nil, duration_ms: nil, error: nil} = run, duration, error ) do duration_ms = System.convert_time_unit(duration, :native, :millisecond) {:ok, %__MODULE__{ run | end_time: DateTime.add(start_time, duration_ms, :millisecond), duration_ms: duration_ms, error: error }} end def finish(%__MODULE__{}, _duration, _error), do: {:error, :already_finished} end