defmodule Vela.Cache.Entry do @moduledoc """ Represents a single item stored in the cache. """ @type t :: %__MODULE__{ key: term(), value: term(), inserted_at: integer(), expires_at: integer() | :infinity, version: non_neg_integer(), touched_at: integer(), node: node(), tags: [atom()] } defstruct [ :key, :value, :inserted_at, :expires_at, :version, :touched_at, :node, tags: [] ] @doc """ Creates a new Entry. `ttl` is in milliseconds, or `:infinity`. """ @spec new(term(), term(), keyword()) :: t() def new(key, value, opts \\ []) do now = System.monotonic_time(:millisecond) ttl = Keyword.get(opts, :ttl, :infinity) expires_at = case ttl do :infinity -> :infinity ms when is_integer(ms) -> now + ms end %__MODULE__{ key: key, value: value, inserted_at: now, expires_at: expires_at, version: Keyword.get(opts, :version, 1), touched_at: now, node: node(), tags: Keyword.get(opts, :tags, []) } end @doc """ Returns true if this entry has expired. """ @spec expired?(t()) :: boolean() def expired?(%__MODULE__{expires_at: :infinity}), do: false def expired?(%__MODULE__{expires_at: expires_at}) do System.monotonic_time(:millisecond) >= expires_at end @doc """ Returns remaining TTL in milliseconds, or :infinity. """ @spec remaining_ttl(t()) :: non_neg_integer() | :infinity def remaining_ttl(%__MODULE__{expires_at: :infinity}), do: :infinity def remaining_ttl(%__MODULE__{expires_at: expires_at}) do remaining = expires_at - System.monotonic_time(:millisecond) max(0, remaining) end end