mirror of
https://github.com/plausible/analytics.git
synced 2024-12-23 01:22:15 +03:00
Replace caching engine (#3878)
* Dependencies: swap Cachex for ConCache * Implement Cache adapter wrapping ConCache * Implement cache stats tracker, for metrics * Use Cache.Adapter in Plausible.Cache Marking the test as not slow anymore * Use Cache Adapter when tracking sessions * Use Cache Adapter for UA parsing * Rename child identifiers - cachex is obsolete now * Test stats tracking * Update grafana metrics * Put all caches under common child specification * Try less * Shorten the function delegation path
This commit is contained in:
parent
bb8a272a36
commit
437a3350ff
@ -10,6 +10,7 @@ defmodule Plausible.Application do
|
|||||||
on_full_build(do: Plausible.License.ensure_valid_license())
|
on_full_build(do: Plausible.License.ensure_valid_license())
|
||||||
|
|
||||||
children = [
|
children = [
|
||||||
|
Plausible.Cache.Stats,
|
||||||
Plausible.Repo,
|
Plausible.Repo,
|
||||||
Plausible.ClickhouseRepo,
|
Plausible.ClickhouseRepo,
|
||||||
Plausible.IngestRepo,
|
Plausible.IngestRepo,
|
||||||
@ -23,13 +24,15 @@ defmodule Plausible.Application do
|
|||||||
Supervisor.child_spec(Plausible.Event.WriteBuffer, id: Plausible.Event.WriteBuffer),
|
Supervisor.child_spec(Plausible.Event.WriteBuffer, id: Plausible.Event.WriteBuffer),
|
||||||
Supervisor.child_spec(Plausible.Session.WriteBuffer, id: Plausible.Session.WriteBuffer),
|
Supervisor.child_spec(Plausible.Session.WriteBuffer, id: Plausible.Session.WriteBuffer),
|
||||||
ReferrerBlocklist,
|
ReferrerBlocklist,
|
||||||
Supervisor.child_spec({Cachex, name: :user_agents, limit: 10_000, stats: true},
|
Plausible.Cache.Adapter.child_spec(:user_agents, :cache_user_agents,
|
||||||
id: :cachex_user_agents
|
ttl_check_interval: :timer.seconds(5),
|
||||||
|
global_ttl: :timer.minutes(60)
|
||||||
),
|
),
|
||||||
Supervisor.child_spec({Cachex, name: :sessions, limit: nil, stats: true},
|
Plausible.Cache.Adapter.child_spec(:sessions, :cache_sessions,
|
||||||
id: :cachex_sessions
|
ttl_check_interval: :timer.seconds(1),
|
||||||
|
global_ttl: :timer.minutes(30)
|
||||||
),
|
),
|
||||||
{Plausible.Site.Cache, []},
|
{Plausible.Site.Cache, ttl_check_interval: false},
|
||||||
{Plausible.Cache.Warmer,
|
{Plausible.Cache.Warmer,
|
||||||
[
|
[
|
||||||
child_name: Plausible.Site.Cache.All,
|
child_name: Plausible.Site.Cache.All,
|
||||||
@ -44,7 +47,7 @@ defmodule Plausible.Application do
|
|||||||
interval: :timer.seconds(30),
|
interval: :timer.seconds(30),
|
||||||
warmer_fn: :refresh_updated_recently
|
warmer_fn: :refresh_updated_recently
|
||||||
]},
|
]},
|
||||||
{Plausible.Shield.IPRuleCache, []},
|
{Plausible.Shield.IPRuleCache, ttl_check_interval: false},
|
||||||
{Plausible.Cache.Warmer,
|
{Plausible.Cache.Warmer,
|
||||||
[
|
[
|
||||||
child_name: Plausible.Shield.IPRuleCache.All,
|
child_name: Plausible.Shield.IPRuleCache.All,
|
||||||
@ -59,7 +62,7 @@ defmodule Plausible.Application do
|
|||||||
interval: :timer.seconds(35),
|
interval: :timer.seconds(35),
|
||||||
warmer_fn: :refresh_updated_recently
|
warmer_fn: :refresh_updated_recently
|
||||||
]},
|
]},
|
||||||
{Plausible.Shield.CountryRuleCache, []},
|
{Plausible.Shield.CountryRuleCache, ttl_check_interval: false},
|
||||||
{Plausible.Cache.Warmer,
|
{Plausible.Cache.Warmer,
|
||||||
[
|
[
|
||||||
child_name: Plausible.Shield.CountryRuleCache.All,
|
child_name: Plausible.Shield.CountryRuleCache.All,
|
||||||
@ -168,16 +171,6 @@ defmodule Plausible.Application do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def report_cache_stats() do
|
|
||||||
case Cachex.stats(:user_agents) do
|
|
||||||
{:ok, stats} ->
|
|
||||||
Logger.info("User agent cache stats: #{inspect(stats)}")
|
|
||||||
|
|
||||||
e ->
|
|
||||||
IO.puts("Unable to show cache stats: #{inspect(e)}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp setup_opentelemetry() do
|
defp setup_opentelemetry() do
|
||||||
OpentelemetryPhoenix.setup()
|
OpentelemetryPhoenix.setup()
|
||||||
OpentelemetryEcto.setup([:plausible, :repo])
|
OpentelemetryEcto.setup([:plausible, :repo])
|
||||||
|
@ -8,10 +8,10 @@ defmodule Plausible.Cache do
|
|||||||
# - Optionally override `unwrap_cache_keys/1`
|
# - Optionally override `unwrap_cache_keys/1`
|
||||||
# - Populate the cache with `Plausible.Cache.Warmer`
|
# - Populate the cache with `Plausible.Cache.Warmer`
|
||||||
|
|
||||||
Serves as a thin wrapper around Cachex, but the underlying
|
Serves as a wrapper around `Plausible.Cache.Adapter`, where the underlying
|
||||||
implementation can be transparently swapped.
|
implementation can be transparently swapped.
|
||||||
|
|
||||||
Even though the Cachex process is started, cache access is disabled
|
Even though normally the relevant Adapter processes are started, cache access is disabled
|
||||||
during tests via the `:plausible, #{__MODULE__}, enabled: bool()` application env key.
|
during tests via the `:plausible, #{__MODULE__}, enabled: bool()` application env key.
|
||||||
This can be overridden on case by case basis, using the child specs options.
|
This can be overridden on case by case basis, using the child specs options.
|
||||||
|
|
||||||
@ -61,29 +61,33 @@ defmodule Plausible.Cache do
|
|||||||
@behaviour Plausible.Cache
|
@behaviour Plausible.Cache
|
||||||
@modes [:all, :updated_recently]
|
@modes [:all, :updated_recently]
|
||||||
|
|
||||||
|
alias Plausible.Cache.Adapter
|
||||||
|
|
||||||
@spec get(any(), Keyword.t()) :: any() | nil
|
@spec get(any(), Keyword.t()) :: any() | nil
|
||||||
def get(key, opts \\ []) do
|
def get(key, opts \\ []) when is_list(opts) do
|
||||||
cache_name = Keyword.get(opts, :cache_name, name())
|
cache_name = Keyword.get(opts, :cache_name, name())
|
||||||
force? = Keyword.get(opts, :force?, false)
|
force? = Keyword.get(opts, :force?, false)
|
||||||
|
|
||||||
if Plausible.Cache.enabled?() or force? do
|
if Plausible.Cache.enabled?() or force? do
|
||||||
case Cachex.get(cache_name, key) do
|
Adapter.get(cache_name, key)
|
||||||
{:ok, nil} ->
|
|
||||||
nil
|
|
||||||
|
|
||||||
{:ok, item} ->
|
|
||||||
item
|
|
||||||
|
|
||||||
{:error, e} ->
|
|
||||||
Logger.error("Error retrieving key from '#{inspect(cache_name)}': #{inspect(e)}")
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
get_from_source(key)
|
get_from_source(key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_or_store(any(), (-> any()), Keyword.t()) :: any() | nil
|
||||||
|
def get_or_store(key, fallback_fn, opts \\ [])
|
||||||
|
when is_function(fallback_fn, 0) and is_list(opts) do
|
||||||
|
cache_name = Keyword.get(opts, :cache_name, name())
|
||||||
|
force? = Keyword.get(opts, :force?, false)
|
||||||
|
|
||||||
|
if Plausible.Cache.enabled?() or force? do
|
||||||
|
Adapter.get(cache_name, key, fallback_fn)
|
||||||
|
else
|
||||||
|
get_from_source(key) || fallback_fn.()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def unwrap_cache_keys(items), do: items
|
def unwrap_cache_keys(items), do: items
|
||||||
defoverridable unwrap_cache_keys: 1
|
defoverridable unwrap_cache_keys: 1
|
||||||
|
|
||||||
@ -117,10 +121,10 @@ defmodule Plausible.Cache do
|
|||||||
def merge_items(new_items, opts) do
|
def merge_items(new_items, opts) do
|
||||||
new_items = unwrap_cache_keys(new_items)
|
new_items = unwrap_cache_keys(new_items)
|
||||||
cache_name = Keyword.get(opts, :cache_name, name())
|
cache_name = Keyword.get(opts, :cache_name, name())
|
||||||
true = Cachex.put_many!(cache_name, new_items)
|
:ok = Adapter.put_many(cache_name, new_items)
|
||||||
|
|
||||||
if Keyword.get(opts, :delete_stale_items?, true) do
|
if Keyword.get(opts, :delete_stale_items?, true) do
|
||||||
{:ok, old_keys} = Cachex.keys(cache_name)
|
old_keys = Adapter.keys(cache_name)
|
||||||
|
|
||||||
new = MapSet.new(Enum.into(new_items, [], fn {k, _} -> k end))
|
new = MapSet.new(Enum.into(new_items, [], fn {k, _} -> k end))
|
||||||
old = MapSet.new(old_keys)
|
old = MapSet.new(old_keys)
|
||||||
@ -128,7 +132,7 @@ defmodule Plausible.Cache do
|
|||||||
old
|
old
|
||||||
|> MapSet.difference(new)
|
|> MapSet.difference(new)
|
||||||
|> Enum.each(fn k ->
|
|> Enum.each(fn k ->
|
||||||
Cachex.del(cache_name, k)
|
Adapter.delete(cache_name, k)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -139,11 +143,7 @@ defmodule Plausible.Cache do
|
|||||||
def child_spec(opts) do
|
def child_spec(opts) do
|
||||||
cache_name = Keyword.get(opts, :cache_name, name())
|
cache_name = Keyword.get(opts, :cache_name, name())
|
||||||
child_id = Keyword.get(opts, :child_id, child_id())
|
child_id = Keyword.get(opts, :child_id, child_id())
|
||||||
|
Adapter.child_spec(cache_name, child_id, opts)
|
||||||
Supervisor.child_spec(
|
|
||||||
{Cachex, name: cache_name, limit: nil, stats: true},
|
|
||||||
id: child_id
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
@ -154,7 +154,7 @@ defmodule Plausible.Cache do
|
|||||||
@spec ready?(atom()) :: boolean
|
@spec ready?(atom()) :: boolean
|
||||||
def ready?(cache_name \\ name()) do
|
def ready?(cache_name \\ name()) do
|
||||||
case size(cache_name) do
|
case size(cache_name) do
|
||||||
n when n > 0 ->
|
n when is_integer(n) and n > 0 ->
|
||||||
true
|
true
|
||||||
|
|
||||||
0 ->
|
0 ->
|
||||||
@ -165,21 +165,8 @@ defmodule Plausible.Cache do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec size() :: non_neg_integer()
|
defdelegate size(cache_name \\ name()), to: Plausible.Cache.Adapter
|
||||||
def size(cache_name \\ name()) do
|
defdelegate hit_rate(cache_name \\ name()), to: Plausible.Cache.Stats
|
||||||
case Cachex.size(cache_name) do
|
|
||||||
{:ok, size} -> size
|
|
||||||
_ -> 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec hit_rate() :: number()
|
|
||||||
def hit_rate(cache_name \\ name()) do
|
|
||||||
case Cachex.stats(cache_name) do
|
|
||||||
{:ok, stats} -> Map.get(stats, :hit_rate, 0)
|
|
||||||
_ -> 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec telemetry_event_refresh(atom(), atom()) :: list(atom())
|
@spec telemetry_event_refresh(atom(), atom()) :: list(atom())
|
||||||
def telemetry_event_refresh(cache_name \\ name(), mode) when mode in @modes do
|
def telemetry_event_refresh(cache_name \\ name(), mode) when mode in @modes do
|
||||||
|
107
lib/plausible/cache/adapter.ex
vendored
Normal file
107
lib/plausible/cache/adapter.ex
vendored
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
defmodule Plausible.Cache.Adapter do
|
||||||
|
@moduledoc """
|
||||||
|
Interface for the underlying cache implementation.
|
||||||
|
Currently: ConCache
|
||||||
|
|
||||||
|
Using the Adapter module directly, the user must ensure that the relevant
|
||||||
|
processes are available to use, which is normally done via the child specification.
|
||||||
|
"""
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
@spec child_spec(atom(), atom(), Keyword.t()) :: Supervisor.child_spec()
|
||||||
|
def child_spec(name, child_id, opts \\ [])
|
||||||
|
when is_atom(name) and is_atom(child_id) and is_list(opts) do
|
||||||
|
cache_name = Keyword.get(opts, :cache_name, name)
|
||||||
|
child_id = Keyword.get(opts, :child_id, child_id)
|
||||||
|
ttl_check_interval = Keyword.get(opts, :ttl_check_interval, false)
|
||||||
|
|
||||||
|
opts =
|
||||||
|
opts
|
||||||
|
|> Keyword.put(:name, cache_name)
|
||||||
|
|> Keyword.put(:ttl_check_interval, ttl_check_interval)
|
||||||
|
|
||||||
|
Supervisor.child_spec(
|
||||||
|
{ConCache, opts},
|
||||||
|
id: child_id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec size(atom()) :: non_neg_integer() | nil
|
||||||
|
def size(cache_name) do
|
||||||
|
ConCache.size(cache_name)
|
||||||
|
catch
|
||||||
|
:exit, _ -> nil
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get(atom(), any()) :: any()
|
||||||
|
def get(cache_name, key) do
|
||||||
|
cache_name
|
||||||
|
|> ConCache.get(key)
|
||||||
|
|> Plausible.Cache.Stats.track(cache_name)
|
||||||
|
catch
|
||||||
|
:exit, _ ->
|
||||||
|
Logger.error("Error retrieving key from '#{inspect(cache_name)}'")
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get(atom(), any(), (-> any())) :: any()
|
||||||
|
def get(cache_name, key, fallback_fn) do
|
||||||
|
cache_name
|
||||||
|
|> ConCache.get_or_store(key, fn ->
|
||||||
|
{:from_fallback, fallback_fn.()}
|
||||||
|
end)
|
||||||
|
|> Plausible.Cache.Stats.track(cache_name)
|
||||||
|
catch
|
||||||
|
:exit, _ ->
|
||||||
|
Logger.error("Error retrieving key from '#{inspect(cache_name)}'")
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec put(atom(), any(), any()) :: any()
|
||||||
|
def put(cache_name, key, value) do
|
||||||
|
:ok = ConCache.put(cache_name, key, value)
|
||||||
|
value
|
||||||
|
catch
|
||||||
|
:exit, _ ->
|
||||||
|
Logger.error("Error putting a key to '#{cache_name}'")
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec put_many(atom(), [any()]) :: :ok
|
||||||
|
def put_many(cache_name, items) when is_list(items) do
|
||||||
|
true = :ets.insert(ConCache.ets(cache_name), items)
|
||||||
|
:ok
|
||||||
|
catch
|
||||||
|
:exit, _ ->
|
||||||
|
Logger.error("Error putting keys to '#{cache_name}'")
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec delete(atom(), any()) :: :ok
|
||||||
|
def delete(cache_name, key) do
|
||||||
|
ConCache.dirty_delete(cache_name, key)
|
||||||
|
catch
|
||||||
|
:exit, _ ->
|
||||||
|
Logger.error("Error deleting a key in '#{cache_name}'")
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec keys(atom()) :: Enumerable.t()
|
||||||
|
def keys(cache_name) do
|
||||||
|
ets = ConCache.ets(cache_name)
|
||||||
|
|
||||||
|
Stream.resource(
|
||||||
|
fn -> :ets.first(ets) end,
|
||||||
|
fn
|
||||||
|
:"$end_of_table" -> {:halt, nil}
|
||||||
|
prev_key -> {[prev_key], :ets.next(ets, prev_key)}
|
||||||
|
end,
|
||||||
|
fn _ -> :ok end
|
||||||
|
)
|
||||||
|
catch
|
||||||
|
:exit, _ ->
|
||||||
|
Logger.error("Error retrieving key from '#{inspect(cache_name)}'")
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
74
lib/plausible/cache/stats.ex
vendored
Normal file
74
lib/plausible/cache/stats.ex
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
defmodule Plausible.Cache.Stats do
|
||||||
|
@moduledoc """
|
||||||
|
Keeps track of hit/miss ratio for various caches.
|
||||||
|
"""
|
||||||
|
|
||||||
|
use GenServer
|
||||||
|
|
||||||
|
def start_link(opts) do
|
||||||
|
GenServer.start_link(__MODULE__, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def init(opts) do
|
||||||
|
table = Keyword.get(opts, :table, __MODULE__)
|
||||||
|
|
||||||
|
^table =
|
||||||
|
:ets.new(table, [
|
||||||
|
:public,
|
||||||
|
:named_table,
|
||||||
|
:ordered_set,
|
||||||
|
read_concurrency: true,
|
||||||
|
write_concurrency: true
|
||||||
|
])
|
||||||
|
|
||||||
|
{:ok, table}
|
||||||
|
end
|
||||||
|
|
||||||
|
def gather(cache_name, table \\ __MODULE__) do
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
hit_rate: hit_rate(cache_name, table),
|
||||||
|
count: size(cache_name) || 0
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
defdelegate size(cache_name), to: Plausible.Cache.Adapter
|
||||||
|
|
||||||
|
def track(item, cache_name, table \\ __MODULE__)
|
||||||
|
|
||||||
|
def track({:from_fallback, item}, cache_name, table) do
|
||||||
|
bump(cache_name, :miss, 1, table)
|
||||||
|
item
|
||||||
|
end
|
||||||
|
|
||||||
|
def track(nil, cache_name, table) do
|
||||||
|
bump(cache_name, :miss, 1, table)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def track(item, cache_name, table) do
|
||||||
|
bump(cache_name, :hit, 1, table)
|
||||||
|
item
|
||||||
|
end
|
||||||
|
|
||||||
|
def bump(cache_name, type, increment, table \\ __MODULE__) do
|
||||||
|
:ets.update_counter(
|
||||||
|
table,
|
||||||
|
{cache_name, type},
|
||||||
|
increment,
|
||||||
|
{{cache_name, type}, 0}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def hit_rate(cache_name, table \\ __MODULE__) do
|
||||||
|
hit = :ets.lookup_element(table, {cache_name, :hit}, 2, 0)
|
||||||
|
miss = :ets.lookup_element(table, {cache_name, :miss}, 2, 0)
|
||||||
|
hit_miss = hit + miss
|
||||||
|
|
||||||
|
if hit_miss == 0 do
|
||||||
|
0.0
|
||||||
|
else
|
||||||
|
hit / hit_miss * 100
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -388,11 +388,9 @@ defmodule Plausible.Ingestion.Event do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp parse_user_agent(%Request{user_agent: user_agent}) when is_binary(user_agent) do
|
defp parse_user_agent(%Request{user_agent: user_agent}) when is_binary(user_agent) do
|
||||||
case Cachex.fetch(:user_agents, user_agent, &UAInspector.parse/1) do
|
Plausible.Cache.Adapter.get(:user_agents, user_agent, fn ->
|
||||||
{:ok, user_agent} -> user_agent
|
UAInspector.parse(user_agent)
|
||||||
{:commit, user_agent} -> user_agent
|
end)
|
||||||
_ -> nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp parse_user_agent(request), do: request
|
defp parse_user_agent(request), do: request
|
||||||
|
@ -19,27 +19,22 @@ defmodule Plausible.Session.CacheStore do
|
|||||||
defp find_session(_domain, nil), do: nil
|
defp find_session(_domain, nil), do: nil
|
||||||
|
|
||||||
defp find_session(event, user_id) do
|
defp find_session(event, user_id) do
|
||||||
from_cache = Cachex.get(:sessions, {event.site_id, user_id})
|
from_cache = Plausible.Cache.Adapter.get(:sessions, {event.site_id, user_id})
|
||||||
|
|
||||||
case from_cache do
|
case from_cache do
|
||||||
{:ok, nil} ->
|
nil ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
{:ok, session} ->
|
session ->
|
||||||
if Timex.diff(event.timestamp, session.timestamp, :minutes) <= 30 do
|
if Timex.diff(event.timestamp, session.timestamp, :minutes) <= 30 do
|
||||||
session
|
session
|
||||||
end
|
end
|
||||||
|
|
||||||
{:error, e} ->
|
|
||||||
Sentry.capture_message("Cachex error", extra: %{error: e})
|
|
||||||
nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp persist_session(session) do
|
defp persist_session(session) do
|
||||||
key = {session.site_id, session.user_id}
|
key = {session.site_id, session.user_id}
|
||||||
Cachex.put(:sessions, key, session, ttl: :timer.minutes(30))
|
Plausible.Cache.Adapter.put(:sessions, key, session)
|
||||||
session
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp update_session(session, event) do
|
defp update_session(session, event) do
|
||||||
|
@ -19,7 +19,7 @@ defmodule Plausible.Shield.CountryRuleCache do
|
|||||||
def name(), do: @cache_name
|
def name(), do: @cache_name
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def child_id(), do: :cachex_country_blocklist
|
def child_id(), do: :cache_country_blocklist
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def count_all() do
|
def count_all() do
|
||||||
|
@ -19,7 +19,7 @@ defmodule Plausible.Shield.IPRuleCache do
|
|||||||
def name(), do: @cache_name
|
def name(), do: @cache_name
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def child_id(), do: :cachex_ip_blocklist
|
def child_id(), do: :cache_ip_blocklist
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def count_all() do
|
def count_all() do
|
||||||
|
@ -37,7 +37,7 @@ defmodule Plausible.Site.Cache do
|
|||||||
def name(), do: @cache_name
|
def name(), do: @cache_name
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def child_id(), do: :cachex_sites
|
def child_id(), do: :cache_sites
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def count_all() do
|
def count_all() do
|
||||||
|
@ -88,29 +88,23 @@ defmodule Plausible.PromEx.Plugins.PlausibleMetrics do
|
|||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Add telemetry events for Cachex user agents and sessions
|
Fire telemetry events for various caches
|
||||||
"""
|
"""
|
||||||
def execute_cache_metrics do
|
def execute_cache_metrics do
|
||||||
{:ok, user_agents_stats} = Cachex.stats(:user_agents)
|
{:ok, user_agents_stats} = Plausible.Cache.Stats.gather(:user_agents)
|
||||||
{:ok, sessions_stats} = Cachex.stats(:sessions)
|
{:ok, sessions_stats} = Plausible.Cache.Stats.gather(:sessions)
|
||||||
|
|
||||||
user_agents_hit_rate = Map.get(user_agents_stats, :hit_rate, 0.0)
|
:telemetry.execute([:prom_ex, :plugin, :cache, :user_agents], %{
|
||||||
sessions_hit_rate = Map.get(sessions_stats, :hit_rate, 0.0)
|
count: user_agents_stats.count,
|
||||||
|
hit_rate: user_agents_stats.hit_rate
|
||||||
{:ok, user_agents_count} = Cachex.size(:user_agents)
|
|
||||||
{:ok, sessions_count} = Cachex.size(:sessions)
|
|
||||||
|
|
||||||
:telemetry.execute([:prom_ex, :plugin, :cachex, :user_agents], %{
|
|
||||||
count: user_agents_count,
|
|
||||||
hit_rate: user_agents_hit_rate
|
|
||||||
})
|
})
|
||||||
|
|
||||||
:telemetry.execute([:prom_ex, :plugin, :cachex, :sessions], %{
|
:telemetry.execute([:prom_ex, :plugin, :cache, :sessions], %{
|
||||||
count: sessions_count,
|
count: sessions_stats.count,
|
||||||
hit_rate: sessions_hit_rate
|
hit_rate: sessions_stats.hit_rate
|
||||||
})
|
})
|
||||||
|
|
||||||
:telemetry.execute([:prom_ex, :plugin, :cachex, :sites], %{
|
:telemetry.execute([:prom_ex, :plugin, :cache, :sites], %{
|
||||||
count: Site.Cache.size(),
|
count: Site.Cache.size(),
|
||||||
hit_rate: Site.Cache.hit_rate()
|
hit_rate: Site.Cache.hit_rate()
|
||||||
})
|
})
|
||||||
@ -144,32 +138,32 @@ defmodule Plausible.PromEx.Plugins.PlausibleMetrics do
|
|||||||
[
|
[
|
||||||
last_value(
|
last_value(
|
||||||
metric_prefix ++ [:cache, :sessions, :size],
|
metric_prefix ++ [:cache, :sessions, :size],
|
||||||
event_name: [:prom_ex, :plugin, :cachex, :sessions],
|
event_name: [:prom_ex, :plugin, :cache, :sessions],
|
||||||
measurement: :count
|
measurement: :count
|
||||||
),
|
),
|
||||||
last_value(
|
last_value(
|
||||||
metric_prefix ++ [:cache, :user_agents, :size],
|
metric_prefix ++ [:cache, :user_agents, :size],
|
||||||
event_name: [:prom_ex, :plugin, :cachex, :user_agents],
|
event_name: [:prom_ex, :plugin, :cache, :user_agents],
|
||||||
measurement: :count
|
measurement: :count
|
||||||
),
|
),
|
||||||
last_value(
|
last_value(
|
||||||
metric_prefix ++ [:cache, :user_agents, :hit_ratio],
|
metric_prefix ++ [:cache, :user_agents, :hit_ratio],
|
||||||
event_name: [:prom_ex, :plugin, :cachex, :user_agents],
|
event_name: [:prom_ex, :plugin, :cache, :user_agents],
|
||||||
measurement: :hit_rate
|
measurement: :hit_rate
|
||||||
),
|
),
|
||||||
last_value(
|
last_value(
|
||||||
metric_prefix ++ [:cache, :sessions, :hit_ratio],
|
metric_prefix ++ [:cache, :sessions, :hit_ratio],
|
||||||
event_name: [:prom_ex, :plugin, :cachex, :sessions],
|
event_name: [:prom_ex, :plugin, :cache, :sessions],
|
||||||
measurement: :hit_rate
|
measurement: :hit_rate
|
||||||
),
|
),
|
||||||
last_value(
|
last_value(
|
||||||
metric_prefix ++ [:cache, :sites, :size],
|
metric_prefix ++ [:cache, :sites, :size],
|
||||||
event_name: [:prom_ex, :plugin, :cachex, :sites],
|
event_name: [:prom_ex, :plugin, :cache, :sites],
|
||||||
measurement: :count
|
measurement: :count
|
||||||
),
|
),
|
||||||
last_value(
|
last_value(
|
||||||
metric_prefix ++ [:cache, :sites, :hit_ratio],
|
metric_prefix ++ [:cache, :sites, :hit_ratio],
|
||||||
event_name: [:prom_ex, :plugin, :cachex, :sites],
|
event_name: [:prom_ex, :plugin, :cache, :sites],
|
||||||
measurement: :hit_rate
|
measurement: :hit_rate
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
4
mix.exs
4
mix.exs
@ -67,7 +67,6 @@ defmodule Plausible.MixProject do
|
|||||||
{:bamboo_mua, "~> 0.1.4"},
|
{:bamboo_mua, "~> 0.1.4"},
|
||||||
{:bcrypt_elixir, "~> 3.0"},
|
{:bcrypt_elixir, "~> 3.0"},
|
||||||
{:bypass, "~> 2.1", only: [:dev, :test, :small_test]},
|
{:bypass, "~> 2.1", only: [:dev, :test, :small_test]},
|
||||||
{:cachex, "~> 3.4"},
|
|
||||||
{:ecto_ch, "~> 0.3"},
|
{:ecto_ch, "~> 0.3"},
|
||||||
{:cloak, "~> 1.1"},
|
{:cloak, "~> 1.1"},
|
||||||
{:cloak_ecto, "~> 1.2"},
|
{:cloak_ecto, "~> 1.2"},
|
||||||
@ -140,7 +139,8 @@ defmodule Plausible.MixProject do
|
|||||||
{:ex_aws, "~> 2.5"},
|
{:ex_aws, "~> 2.5"},
|
||||||
{:ex_aws_s3, "~> 2.5"},
|
{:ex_aws_s3, "~> 2.5"},
|
||||||
{:sweet_xml, "~> 0.7.4"},
|
{:sweet_xml, "~> 0.7.4"},
|
||||||
{:testcontainers, "~> 1.6", only: [:test, :small_test]}
|
{:testcontainers, "~> 1.6", only: [:test, :small_test]},
|
||||||
|
{:con_cache, "~> 1.0"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
2
mix.lock
2
mix.lock
@ -8,7 +8,6 @@
|
|||||||
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.1.0", "0b110a9a6c619b19a7f73fa3004aa11d6e719a67e672d1633dc36b6b2290a0f7", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2ad2acb5a8bc049e8d5aa267802631912bb80d5f4110a178ae7999e69dca1bf7"},
|
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.1.0", "0b110a9a6c619b19a7f73fa3004aa11d6e719a67e672d1633dc36b6b2290a0f7", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2ad2acb5a8bc049e8d5aa267802631912bb80d5f4110a178ae7999e69dca1bf7"},
|
||||||
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
||||||
"bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"},
|
"bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"},
|
||||||
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},
|
|
||||||
"castore": {:hex, :castore, "1.0.5", "9eeebb394cc9a0f3ae56b813459f990abb0a3dedee1be6b27fdb50301930502f", [:mix], [], "hexpm", "8d7c597c3e4a64c395980882d4bca3cebb8d74197c590dc272cfd3b6a6310578"},
|
"castore": {:hex, :castore, "1.0.5", "9eeebb394cc9a0f3ae56b813459f990abb0a3dedee1be6b27fdb50301930502f", [:mix], [], "hexpm", "8d7c597c3e4a64c395980882d4bca3cebb8d74197c590dc272cfd3b6a6310578"},
|
||||||
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
||||||
"ch": {:hex, :ch, "0.2.4", "d510fbb5542d009f7c5b00bb1ecab73307b6066d9fb9b220600257d462cba67f", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "8f065d15aaf912ae8da56c9ca5298fb2d1a09108d006de589bcf8c2b39a7e2bb"},
|
"ch": {:hex, :ch, "0.2.4", "d510fbb5542d009f7c5b00bb1ecab73307b6066d9fb9b220600257d462cba67f", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "8f065d15aaf912ae8da56c9ca5298fb2d1a09108d006de589bcf8c2b39a7e2bb"},
|
||||||
@ -19,6 +18,7 @@
|
|||||||
"combination": {:hex, :combination, "0.0.3", "746aedca63d833293ec6e835aa1f34974868829b1486b1e1cb0685f0b2ae1f41", [:mix], [], "hexpm", "72b099f463df42ef7dc6371d250c7070b57b6c5902853f69deb894f79eda18ca"},
|
"combination": {:hex, :combination, "0.0.3", "746aedca63d833293ec6e835aa1f34974868829b1486b1e1cb0685f0b2ae1f41", [:mix], [], "hexpm", "72b099f463df42ef7dc6371d250c7070b57b6c5902853f69deb894f79eda18ca"},
|
||||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||||
"comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"},
|
"comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"},
|
||||||
|
"con_cache": {:hex, :con_cache, "1.0.0", "6405e2bd5d5005334af72939432783562a8c35a196c2e63108fe10bb97b366e6", [:mix], [], "hexpm", "4d1f5cb1a67f3c1a468243dc98d10ac83af7f3e33b7e7c15999dc2c9bc0a551e"},
|
||||||
"cors_plug": {:hex, :cors_plug, "3.0.3", "7c3ac52b39624bc616db2e937c282f3f623f25f8d550068b6710e58d04a0e330", [:mix], [{:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3f2d759e8c272ed3835fab2ef11b46bddab8c1ab9528167bd463b6452edf830d"},
|
"cors_plug": {:hex, :cors_plug, "3.0.3", "7c3ac52b39624bc616db2e937c282f3f623f25f8d550068b6710e58d04a0e330", [:mix], [{:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3f2d759e8c272ed3835fab2ef11b46bddab8c1ab9528167bd463b6452edf830d"},
|
||||||
"cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"},
|
"cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"},
|
||||||
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
|
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
|
||||||
|
50
test/plausible/cache/stats_test.exs
vendored
Normal file
50
test/plausible/cache/stats_test.exs
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
defmodule Plausible.Cache.StatsTest do
|
||||||
|
use Plausible.DataCase, async: true
|
||||||
|
|
||||||
|
alias Plausible.Cache.Stats
|
||||||
|
|
||||||
|
test "when tracking is not initialized, stats are 0" do
|
||||||
|
assert {:ok, %{hit_rate: +0.0, count: 0}} = Stats.gather(:foo)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when cache is started, stats are 0", %{test: test} do
|
||||||
|
{:ok, _} = Stats.start_link(name: test, table: test)
|
||||||
|
assert {:ok, %{hit_rate: +0.0, count: 0}} = Stats.gather(test, test)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "tracking changes hit ratio", %{test: test} do
|
||||||
|
{:ok, _} = Stats.start_link(name: test, table: test)
|
||||||
|
|
||||||
|
Stats.track(nil, test, test)
|
||||||
|
assert {:ok, %{hit_rate: +0.0, count: 0}} = Stats.gather(test, test)
|
||||||
|
|
||||||
|
Stats.track(:not_nil, test, test)
|
||||||
|
assert {:ok, %{hit_rate: 50.0, count: 0}} = Stats.gather(test, test)
|
||||||
|
|
||||||
|
Stats.track(:not_nil, test, test)
|
||||||
|
Stats.track(:not_nil, test, test)
|
||||||
|
assert {:ok, %{hit_rate: 75.0, count: 0}} = Stats.gather(test, test)
|
||||||
|
|
||||||
|
Stats.track(nil, test, test)
|
||||||
|
Stats.track(nil, test, test)
|
||||||
|
assert {:ok, %{hit_rate: 50.0, count: 0}} = Stats.gather(test, test)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "bump by custom number", %{test: test} do
|
||||||
|
{:ok, _} = Stats.start_link(name: test, table: test)
|
||||||
|
Stats.bump(test, :miss, 10, test)
|
||||||
|
assert {:ok, %{hit_rate: +0.0, count: 0}} = Stats.gather(test, test)
|
||||||
|
Stats.bump(test, :hit, 90, test)
|
||||||
|
assert {:ok, %{hit_rate: 90.0, count: 0}} = Stats.gather(test, test)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "count comes from cache adapter", %{test: test} do
|
||||||
|
{:ok, _} = Stats.start_link(name: test, table: test)
|
||||||
|
%{start: {m, f, a}} = Plausible.Cache.Adapter.child_spec(test, test)
|
||||||
|
{:ok, _} = apply(m, f, a)
|
||||||
|
|
||||||
|
Plausible.Cache.Adapter.put(test, :key, :value)
|
||||||
|
assert Stats.size(test) == 1
|
||||||
|
assert {:ok, %{hit_rate: +0.0, count: 1}} = Stats.gather(test, test)
|
||||||
|
end
|
||||||
|
end
|
@ -50,7 +50,7 @@ defmodule Plausible.CacheTest do
|
|||||||
assert ExampleCache.get("key", force?: true, cache_name: NonExistingCache) == nil
|
assert ExampleCache.get("key", force?: true, cache_name: NonExistingCache) == nil
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert log =~ "Error retrieving key from 'NonExistingCache': :no_cache"
|
assert log =~ "Error retrieving key from 'NonExistingCache'"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "cache is not ready when it doesn't exist", %{test: test} do
|
test "cache is not ready when it doesn't exist", %{test: test} do
|
||||||
@ -58,6 +58,32 @@ defmodule Plausible.CacheTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "stats tracking" do
|
||||||
|
test "get affects hit rate", %{test: test} do
|
||||||
|
{:ok, _} = start_test_cache(test)
|
||||||
|
:ok = ExampleCache.merge_items([{"item1", :item1}], cache_name: test)
|
||||||
|
assert ExampleCache.get("item1", cache_name: test, force?: true)
|
||||||
|
assert {:ok, %{hit_rate: 100.0}} = Plausible.Cache.Stats.gather(test)
|
||||||
|
refute ExampleCache.get("item2", cache_name: test, force?: true)
|
||||||
|
assert {:ok, %{hit_rate: 50.0}} = Plausible.Cache.Stats.gather(test)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "get_or_store affects hit rate", %{test: test} do
|
||||||
|
{:ok, _} = start_test_cache(test)
|
||||||
|
|
||||||
|
:ok = ExampleCache.merge_items([{"item1", :item1}], cache_name: test)
|
||||||
|
assert ExampleCache.get("item1", cache_name: test, force?: true)
|
||||||
|
|
||||||
|
assert "value" ==
|
||||||
|
ExampleCache.get_or_store("item2", fn -> "value" end,
|
||||||
|
cache_name: test,
|
||||||
|
force?: true
|
||||||
|
)
|
||||||
|
|
||||||
|
assert {:ok, %{hit_rate: 50.0}} = Plausible.Cache.Stats.gather(test)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "merging cache items" do
|
describe "merging cache items" do
|
||||||
test "merging adds new items", %{test: test} do
|
test "merging adds new items", %{test: test} do
|
||||||
{:ok, _} = start_test_cache(test)
|
{:ok, _} = start_test_cache(test)
|
||||||
|
@ -323,7 +323,6 @@ defmodule Plausible.Site.CacheTest do
|
|||||||
@items1 for i <- 1..200_000, do: {i, nil, :batch1}
|
@items1 for i <- 1..200_000, do: {i, nil, :batch1}
|
||||||
@items2 for _ <- 1..200_000, do: {Enum.random(1..400_000), nil, :batch2}
|
@items2 for _ <- 1..200_000, do: {Enum.random(1..400_000), nil, :batch2}
|
||||||
@max_seconds 2
|
@max_seconds 2
|
||||||
@tag :slow
|
|
||||||
test "merging large sets is expected to be under #{@max_seconds} seconds", %{test: test} do
|
test "merging large sets is expected to be under #{@max_seconds} seconds", %{test: test} do
|
||||||
{:ok, _} = start_test_cache(test)
|
{:ok, _} = start_test_cache(test)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user