Revert "Replace caching engine (#3878)" (#3883)

This reverts commit 437a3350ff.
This commit is contained in:
hq1 2024-03-12 08:30:16 +01:00 committed by GitHub
parent ca7edd47cb
commit c5881cdc6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 100 additions and 323 deletions

View File

@ -10,7 +10,6 @@ defmodule Plausible.Application do
on_full_build(do: Plausible.License.ensure_valid_license())
children = [
Plausible.Cache.Stats,
Plausible.Repo,
Plausible.ClickhouseRepo,
Plausible.IngestRepo,
@ -24,15 +23,13 @@ defmodule Plausible.Application do
Supervisor.child_spec(Plausible.Event.WriteBuffer, id: Plausible.Event.WriteBuffer),
Supervisor.child_spec(Plausible.Session.WriteBuffer, id: Plausible.Session.WriteBuffer),
ReferrerBlocklist,
Plausible.Cache.Adapter.child_spec(:user_agents, :cache_user_agents,
ttl_check_interval: :timer.seconds(5),
global_ttl: :timer.minutes(60)
Supervisor.child_spec({Cachex, name: :user_agents, limit: 10_000, stats: true},
id: :cachex_user_agents
),
Plausible.Cache.Adapter.child_spec(:sessions, :cache_sessions,
ttl_check_interval: :timer.seconds(1),
global_ttl: :timer.minutes(30)
Supervisor.child_spec({Cachex, name: :sessions, limit: nil, stats: true},
id: :cachex_sessions
),
{Plausible.Site.Cache, ttl_check_interval: false},
{Plausible.Site.Cache, []},
{Plausible.Cache.Warmer,
[
child_name: Plausible.Site.Cache.All,
@ -47,7 +44,7 @@ defmodule Plausible.Application do
interval: :timer.seconds(30),
warmer_fn: :refresh_updated_recently
]},
{Plausible.Shield.IPRuleCache, ttl_check_interval: false},
{Plausible.Shield.IPRuleCache, []},
{Plausible.Cache.Warmer,
[
child_name: Plausible.Shield.IPRuleCache.All,
@ -62,7 +59,7 @@ defmodule Plausible.Application do
interval: :timer.seconds(35),
warmer_fn: :refresh_updated_recently
]},
{Plausible.Shield.CountryRuleCache, ttl_check_interval: false},
{Plausible.Shield.CountryRuleCache, []},
{Plausible.Cache.Warmer,
[
child_name: Plausible.Shield.CountryRuleCache.All,
@ -171,6 +168,16 @@ defmodule Plausible.Application do
)
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
OpentelemetryPhoenix.setup()
OpentelemetryEcto.setup([:plausible, :repo])

View File

@ -8,10 +8,10 @@ defmodule Plausible.Cache do
# - Optionally override `unwrap_cache_keys/1`
# - Populate the cache with `Plausible.Cache.Warmer`
Serves as a wrapper around `Plausible.Cache.Adapter`, where the underlying
Serves as a thin wrapper around Cachex, but the underlying
implementation can be transparently swapped.
Even though normally the relevant Adapter processes are started, cache access is disabled
Even though the Cachex process is started, cache access is disabled
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.
@ -61,33 +61,29 @@ defmodule Plausible.Cache do
@behaviour Plausible.Cache
@modes [:all, :updated_recently]
alias Plausible.Cache.Adapter
@spec get(any(), Keyword.t()) :: any() | nil
def get(key, opts \\ []) when is_list(opts) do
def get(key, 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)
case Cachex.get(cache_name, key) do
{:ok, nil} ->
nil
{:ok, item} ->
item
{:error, e} ->
Logger.error("Error retrieving key from '#{inspect(cache_name)}': #{inspect(e)}")
nil
end
else
get_from_source(key)
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
defoverridable unwrap_cache_keys: 1
@ -121,10 +117,10 @@ defmodule Plausible.Cache do
def merge_items(new_items, opts) do
new_items = unwrap_cache_keys(new_items)
cache_name = Keyword.get(opts, :cache_name, name())
:ok = Adapter.put_many(cache_name, new_items)
true = Cachex.put_many!(cache_name, new_items)
if Keyword.get(opts, :delete_stale_items?, true) do
old_keys = Adapter.keys(cache_name)
{:ok, old_keys} = Cachex.keys(cache_name)
new = MapSet.new(Enum.into(new_items, [], fn {k, _} -> k end))
old = MapSet.new(old_keys)
@ -132,7 +128,7 @@ defmodule Plausible.Cache do
old
|> MapSet.difference(new)
|> Enum.each(fn k ->
Adapter.delete(cache_name, k)
Cachex.del(cache_name, k)
end)
end
@ -143,7 +139,11 @@ defmodule Plausible.Cache do
def child_spec(opts) do
cache_name = Keyword.get(opts, :cache_name, name())
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
@doc """
@ -154,7 +154,7 @@ defmodule Plausible.Cache do
@spec ready?(atom()) :: boolean
def ready?(cache_name \\ name()) do
case size(cache_name) do
n when is_integer(n) and n > 0 ->
n when n > 0 ->
true
0 ->
@ -165,8 +165,21 @@ defmodule Plausible.Cache do
end
end
defdelegate size(cache_name \\ name()), to: Plausible.Cache.Adapter
defdelegate hit_rate(cache_name \\ name()), to: Plausible.Cache.Stats
@spec size() :: non_neg_integer()
def size(cache_name \\ name()) do
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())
def telemetry_event_refresh(cache_name \\ name(), mode) when mode in @modes do

View File

@ -1,107 +0,0 @@
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

View File

@ -1,74 +0,0 @@
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

View File

@ -388,9 +388,11 @@ defmodule Plausible.Ingestion.Event do
end
defp parse_user_agent(%Request{user_agent: user_agent}) when is_binary(user_agent) do
Plausible.Cache.Adapter.get(:user_agents, user_agent, fn ->
UAInspector.parse(user_agent)
end)
case Cachex.fetch(:user_agents, user_agent, &UAInspector.parse/1) do
{:ok, user_agent} -> user_agent
{:commit, user_agent} -> user_agent
_ -> nil
end
end
defp parse_user_agent(request), do: request

View File

@ -19,22 +19,27 @@ defmodule Plausible.Session.CacheStore do
defp find_session(_domain, nil), do: nil
defp find_session(event, user_id) do
from_cache = Plausible.Cache.Adapter.get(:sessions, {event.site_id, user_id})
from_cache = Cachex.get(:sessions, {event.site_id, user_id})
case from_cache do
nil ->
{:ok, nil} ->
nil
session ->
{:ok, session} ->
if Timex.diff(event.timestamp, session.timestamp, :minutes) <= 30 do
session
end
{:error, e} ->
Sentry.capture_message("Cachex error", extra: %{error: e})
nil
end
end
defp persist_session(session) do
key = {session.site_id, session.user_id}
Plausible.Cache.Adapter.put(:sessions, key, session)
Cachex.put(:sessions, key, session, ttl: :timer.minutes(30))
session
end
defp update_session(session, event) do

View File

@ -19,7 +19,7 @@ defmodule Plausible.Shield.CountryRuleCache do
def name(), do: @cache_name
@impl true
def child_id(), do: :cache_country_blocklist
def child_id(), do: :cachex_country_blocklist
@impl true
def count_all() do

View File

@ -19,7 +19,7 @@ defmodule Plausible.Shield.IPRuleCache do
def name(), do: @cache_name
@impl true
def child_id(), do: :cache_ip_blocklist
def child_id(), do: :cachex_ip_blocklist
@impl true
def count_all() do

View File

@ -37,7 +37,7 @@ defmodule Plausible.Site.Cache do
def name(), do: @cache_name
@impl true
def child_id(), do: :cache_sites
def child_id(), do: :cachex_sites
@impl true
def count_all() do

View File

@ -88,23 +88,29 @@ defmodule Plausible.PromEx.Plugins.PlausibleMetrics do
end
@doc """
Fire telemetry events for various caches
Add telemetry events for Cachex user agents and sessions
"""
def execute_cache_metrics do
{:ok, user_agents_stats} = Plausible.Cache.Stats.gather(:user_agents)
{:ok, sessions_stats} = Plausible.Cache.Stats.gather(:sessions)
{:ok, user_agents_stats} = Cachex.stats(:user_agents)
{:ok, sessions_stats} = Cachex.stats(:sessions)
:telemetry.execute([:prom_ex, :plugin, :cache, :user_agents], %{
count: user_agents_stats.count,
hit_rate: user_agents_stats.hit_rate
user_agents_hit_rate = Map.get(user_agents_stats, :hit_rate, 0.0)
sessions_hit_rate = Map.get(sessions_stats, :hit_rate, 0.0)
{: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, :cache, :sessions], %{
count: sessions_stats.count,
hit_rate: sessions_stats.hit_rate
:telemetry.execute([:prom_ex, :plugin, :cachex, :sessions], %{
count: sessions_count,
hit_rate: sessions_hit_rate
})
:telemetry.execute([:prom_ex, :plugin, :cache, :sites], %{
:telemetry.execute([:prom_ex, :plugin, :cachex, :sites], %{
count: Site.Cache.size(),
hit_rate: Site.Cache.hit_rate()
})
@ -138,32 +144,32 @@ defmodule Plausible.PromEx.Plugins.PlausibleMetrics do
[
last_value(
metric_prefix ++ [:cache, :sessions, :size],
event_name: [:prom_ex, :plugin, :cache, :sessions],
event_name: [:prom_ex, :plugin, :cachex, :sessions],
measurement: :count
),
last_value(
metric_prefix ++ [:cache, :user_agents, :size],
event_name: [:prom_ex, :plugin, :cache, :user_agents],
event_name: [:prom_ex, :plugin, :cachex, :user_agents],
measurement: :count
),
last_value(
metric_prefix ++ [:cache, :user_agents, :hit_ratio],
event_name: [:prom_ex, :plugin, :cache, :user_agents],
event_name: [:prom_ex, :plugin, :cachex, :user_agents],
measurement: :hit_rate
),
last_value(
metric_prefix ++ [:cache, :sessions, :hit_ratio],
event_name: [:prom_ex, :plugin, :cache, :sessions],
event_name: [:prom_ex, :plugin, :cachex, :sessions],
measurement: :hit_rate
),
last_value(
metric_prefix ++ [:cache, :sites, :size],
event_name: [:prom_ex, :plugin, :cache, :sites],
event_name: [:prom_ex, :plugin, :cachex, :sites],
measurement: :count
),
last_value(
metric_prefix ++ [:cache, :sites, :hit_ratio],
event_name: [:prom_ex, :plugin, :cache, :sites],
event_name: [:prom_ex, :plugin, :cachex, :sites],
measurement: :hit_rate
)
]

View File

@ -67,6 +67,7 @@ defmodule Plausible.MixProject do
{:bamboo_mua, "~> 0.1.4"},
{:bcrypt_elixir, "~> 3.0"},
{:bypass, "~> 2.1", only: [:dev, :test, :small_test]},
{:cachex, "~> 3.4"},
{:ecto_ch, "~> 0.3"},
{:cloak, "~> 1.1"},
{:cloak_ecto, "~> 1.2"},
@ -139,8 +140,7 @@ defmodule Plausible.MixProject do
{:ex_aws, "~> 2.5"},
{:ex_aws_s3, "~> 2.5"},
{:sweet_xml, "~> 0.7.4"},
{:testcontainers, "~> 1.6", only: [:test, :small_test]},
{:con_cache, "~> 1.0"}
{:testcontainers, "~> 1.6", only: [:test, :small_test]}
]
end

View File

@ -8,6 +8,7 @@
"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"},
"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"},
"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"},
@ -18,7 +19,6 @@
"combination": {:hex, :combination, "0.0.3", "746aedca63d833293ec6e835aa1f34974868829b1486b1e1cb0685f0b2ae1f41", [:mix], [], "hexpm", "72b099f463df42ef7dc6371d250c7070b57b6c5902853f69deb894f79eda18ca"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"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"},
"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"},

View File

@ -1,50 +0,0 @@
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

View File

@ -50,7 +50,7 @@ defmodule Plausible.CacheTest do
assert ExampleCache.get("key", force?: true, cache_name: NonExistingCache) == nil
end)
assert log =~ "Error retrieving key from 'NonExistingCache'"
assert log =~ "Error retrieving key from 'NonExistingCache': :no_cache"
end
test "cache is not ready when it doesn't exist", %{test: test} do
@ -58,32 +58,6 @@ defmodule Plausible.CacheTest do
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
test "merging adds new items", %{test: test} do
{:ok, _} = start_test_cache(test)

View File

@ -323,6 +323,7 @@ defmodule Plausible.Site.CacheTest do
@items1 for i <- 1..200_000, do: {i, nil, :batch1}
@items2 for _ <- 1..200_000, do: {Enum.random(1..400_000), nil, :batch2}
@max_seconds 2
@tag :slow
test "merging large sets is expected to be under #{@max_seconds} seconds", %{test: test} do
{:ok, _} = start_test_cache(test)