From 0ca5a2bea54ef6fee38e6be21808071bc694a827 Mon Sep 17 00:00:00 2001 From: Ziinc Date: Wed, 6 Nov 2024 23:46:43 +0800 Subject: [PATCH] feat: upgrade cachex to v4 (#2253) * feat: upgrade cachex to v4 * chore: bump to v1.9.5 * chore: fix failing tests --- VERSION | 2 +- lib/logflare/auth/cache.ex | 13 +++- lib/logflare/backends/cache.ex | 13 +++- lib/logflare/backends/source_metrics_cache.ex | 71 +++++++++++++++++++ lib/logflare/billing/cache.ex | 13 +++- lib/logflare/context_cache.ex | 9 ++- lib/logflare/logs/log_events_cache.ex | 15 ++-- lib/logflare/logs/rejected_log_events.ex | 40 ++++++++--- lib/logflare/partners/cache.ex | 13 +++- lib/logflare/pubsub_rates/cache.ex | 14 +++- lib/logflare/source_schemas/cache.ex | 10 ++- lib/logflare/sources/cache.ex | 13 +++- lib/logflare/team_users/cache.ex | 13 +++- lib/logflare/users/cache.ex | 13 +++- lib/logflare/utils.ex | 18 +++++ mix.exs | 2 +- mix.lock | 5 +- 17 files changed, 246 insertions(+), 31 deletions(-) create mode 100644 lib/logflare/backends/source_metrics_cache.ex diff --git a/VERSION b/VERSION index 70b02ffc1..6ecac6812 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.9.4 \ No newline at end of file +1.9.5 \ No newline at end of file diff --git a/lib/logflare/auth/cache.ex b/lib/logflare/auth/cache.ex index 2290f2224..e2b2868cb 100644 --- a/lib/logflare/auth/cache.ex +++ b/lib/logflare/auth/cache.ex @@ -16,7 +16,18 @@ defmodule Logflare.Auth.Cache do id: __MODULE__, start: {Cachex, :start_link, - [__MODULE__, [stats: stats, expiration: Utils.cache_expiration_sec(30, 15)]]} + [ + __MODULE__, + [ + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(100_000) + ] + |> Enum.filter(& &1), + expiration: Utils.cache_expiration_sec(30, 15) + ] + ]} } end diff --git a/lib/logflare/backends/cache.ex b/lib/logflare/backends/cache.ex index 1eca90cb0..be2f485e1 100644 --- a/lib/logflare/backends/cache.ex +++ b/lib/logflare/backends/cache.ex @@ -11,7 +11,18 @@ defmodule Logflare.Backends.Cache do id: __MODULE__, start: {Cachex, :start_link, - [__MODULE__, [stats: stats, expiration: Utils.cache_expiration_min(), limit: 100_000]]} + [ + __MODULE__, + [ + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(100_000) + ] + |> Enum.filter(& &1), + expiration: Utils.cache_expiration_min() + ] + ]} } end diff --git a/lib/logflare/backends/source_metrics_cache.ex b/lib/logflare/backends/source_metrics_cache.ex new file mode 100644 index 000000000..b75c550e6 --- /dev/null +++ b/lib/logflare/backends/source_metrics_cache.ex @@ -0,0 +1,71 @@ +defmodule Logflare.Backends.SourceMetricsCache do + @moduledoc false + alias Logflare.Logs.LogEvents + alias Logflare.ContextCache + alias Logflare.LogEvent, as: LE + alias Logflare.Utils + + @cache __MODULE__ + + def child_spec(_) do + stats = Application.get_env(:logflare, :cache_stats, false) + + %{ + id: __MODULE__, + name: __MODULE__, + start: { + Cachex, + :start_link, + [ + @cache, + [ + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(5_000) + ] + |> Enum.filter(& &1), + expiration: Utils.cache_expiration_min(60) + ] + ] + } + } + end + + @fetch_event_by_id_and_timestamp_key {:fetch_event_by_id_and_timestamp, 2} + @spec put_event_with_id_and_timestamp(atom, keyword, LE.t()) :: term + def put_event_with_id_and_timestamp(source_token, kw, %LE{} = log_event) do + cache_key = {@fetch_event_by_id_and_timestamp_key, [source_token, kw]} + Cachex.put(@cache, cache_key, {:ok, log_event}) + end + + @spec fetch_event_by_id_and_timestamp(atom, keyword) :: {:ok, map()} | {:error, map()} + def fetch_event_by_id_and_timestamp(source_token, kw) when is_atom(source_token) do + ContextCache.apply_fun(LogEvents, @fetch_event_by_id_and_timestamp_key, [source_token, kw]) + end + + @fetch_event_by_id {:fetch_event_by_id, 2} + @spec fetch_event_by_id(atom, binary()) :: {:ok, LE.t() | nil} | {:error, map()} + def fetch_event_by_id(source_token, id) when is_atom(source_token) and is_binary(id) do + ContextCache.apply_fun(LogEvents, @fetch_event_by_id, [source_token, id]) + end + + @fetch_event_by_path {:fetch_event_by_path, 3} + @spec fetch_event_by_path(atom, binary(), term()) :: {:ok, LE.t() | nil} | {:error, map()} + def fetch_event_by_path(source_token, path, value) + when is_atom(source_token) and is_binary(path) do + ContextCache.apply_fun(LogEvents, @fetch_event_by_path, [source_token, path, value]) + end + + @spec put(atom(), term(), LE.t()) :: {:error, boolean} | {:ok, boolean} + def put(source_token, key, log_event) do + Cachex.put(__MODULE__, {source_token, key}, log_event) + end + + @spec get!(atom(), term()) :: LE.t() | nil + def get!(source_token, log_id) do + Cachex.get!(__MODULE__, {source_token, log_id}) + end + + def name, do: __MODULE__ +end diff --git a/lib/logflare/billing/cache.ex b/lib/logflare/billing/cache.ex index 10e4eef0d..87e148059 100644 --- a/lib/logflare/billing/cache.ex +++ b/lib/logflare/billing/cache.ex @@ -13,7 +13,18 @@ defmodule Logflare.Billing.Cache do id: __MODULE__, start: {Cachex, :start_link, - [__MODULE__, [stats: stats, expiration: Utils.cache_expiration_min(), limit: 100_000]]} + [ + __MODULE__, + [ + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(100_000) + ] + |> Enum.filter(& &1), + expiration: Utils.cache_expiration_min() + ] + ]} } end diff --git a/lib/logflare/context_cache.ex b/lib/logflare/context_cache.ex index a49ece95d..173da798f 100644 --- a/lib/logflare/context_cache.ex +++ b/lib/logflare/context_cache.ex @@ -44,7 +44,12 @@ defmodule Logflare.ContextCache do [ @cache, [ - stats: stats, + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(100_000) + ] + |> Enum.filter(& &1), expiration: Utils.cache_expiration_min() ] ]} @@ -93,7 +98,7 @@ defmodule Logflare.ContextCache do def bust_keys(values) when is_list(values) do for {context, primary_key} <- values do filter = {:==, {:element, 1, :key}, {{context, primary_key}}} - query = Cachex.Query.create(filter, {:key, :value}) + query = Cachex.Query.build(where: filter, output: {:key, :value}) context_cache = cache_name(context) Logflare.ContextCache diff --git a/lib/logflare/logs/log_events_cache.ex b/lib/logflare/logs/log_events_cache.ex index 6fdaa5484..f67ea5e83 100644 --- a/lib/logflare/logs/log_events_cache.ex +++ b/lib/logflare/logs/log_events_cache.ex @@ -1,10 +1,9 @@ defmodule Logflare.Logs.LogEvents.Cache do @moduledoc false - import Cachex.Spec alias Logflare.Logs.LogEvents alias Logflare.ContextCache alias Logflare.LogEvent, as: LE - @ttl :timer.hours(1) + alias Logflare.Utils @cache __MODULE__ @@ -19,7 +18,15 @@ defmodule Logflare.Logs.LogEvents.Cache do :start_link, [ @cache, - [expiration: expiration(default: @ttl), limit: limit(size: 5_000), stats: stats] + [ + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(5_000) + ] + |> Enum.filter(& &1), + expiration: Utils.cache_expiration_min(60) + ] ] } } @@ -29,7 +36,7 @@ defmodule Logflare.Logs.LogEvents.Cache do @spec put_event_with_id_and_timestamp(atom, keyword, LE.t()) :: term def put_event_with_id_and_timestamp(source_token, kw, %LE{} = log_event) do cache_key = {@fetch_event_by_id_and_timestamp_key, [source_token, kw]} - Cachex.put(@cache, cache_key, {:ok, log_event}, ttl: @ttl) + Cachex.put(@cache, cache_key, {:ok, log_event}) end @spec fetch_event_by_id_and_timestamp(atom, keyword) :: {:ok, map()} | {:error, map()} diff --git a/lib/logflare/logs/rejected_log_events.ex b/lib/logflare/logs/rejected_log_events.ex index 16ddc92d2..cc18592de 100644 --- a/lib/logflare/logs/rejected_log_events.ex +++ b/lib/logflare/logs/rejected_log_events.ex @@ -21,19 +21,42 @@ defmodule Logflare.Logs.RejectedLogEvents do }' ``` """ + require Ex2ms alias Logflare.Source alias Logflare.LogEvent, as: LE + alias Logflare.Utils @cache __MODULE__ def child_spec(_) do stats = Application.get_env(:logflare, :cache_stats, false) - %{id: @cache, start: {Cachex, :start_link, [@cache, [limit: 10_000, stats: stats]]}} + + %{ + id: @cache, + start: + {Cachex, :start_link, + [ + @cache, + [ + expiration: Utils.cache_expiration_min(60), + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(10_000) + ] + |> Enum.filter(& &1) + ] + ]} + } end @spec get_by_source(Source.t()) :: list(LE.t()) def get_by_source(%Source{token: token}) do - get!(token).log_events + get!(token) + |> case do + %{log_events: events} -> events + other -> other + end end def count(%Source{} = s) do @@ -63,15 +86,12 @@ defmodule Logflare.Logs.RejectedLogEvents do :ok end - def query(source_id) when is_atom(source_id) do + def query(token) when is_atom(token) do + filter = {:==, {:element, 1, :key}, {:const, token}} + query = Cachex.Query.build(where: filter, output: :value) + @cache - |> Cachex.stream!() - |> Stream.filter(fn x -> - match?({:entry, {^source_id, _le_id}, _ts, _, _le}, x) - end) - |> Stream.map(fn {:entry, {^source_id, _le_id}, _ts, _, le} -> - le - end) + |> Cachex.stream!(query) |> Enum.reverse() |> Enum.take(100) end diff --git a/lib/logflare/partners/cache.ex b/lib/logflare/partners/cache.ex index 54688e20a..f1a6b1253 100644 --- a/lib/logflare/partners/cache.ex +++ b/lib/logflare/partners/cache.ex @@ -13,7 +13,18 @@ defmodule Logflare.Partners.Cache do id: __MODULE__, start: {Cachex, :start_link, - [__MODULE__, [stats: stats, expiration: Utils.cache_expiration_min(), limit: 100_000]]} + [ + __MODULE__, + [ + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(100_000) + ] + |> Enum.filter(& &1), + expiration: Utils.cache_expiration_min() + ] + ]} } end diff --git a/lib/logflare/pubsub_rates/cache.ex b/lib/logflare/pubsub_rates/cache.ex index d0817ee51..888093b39 100644 --- a/lib/logflare/pubsub_rates/cache.ex +++ b/lib/logflare/pubsub_rates/cache.ex @@ -15,7 +15,19 @@ defmodule Logflare.PubSubRates.Cache do %{ id: __MODULE__, start: - {Cachex, :start_link, [@cache, [stats: stats, expiration: Utils.cache_expiration_min(5)]]} + {Cachex, :start_link, + [ + @cache, + [ + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(100_000) + ] + |> Enum.filter(& &1), + expiration: Utils.cache_expiration_min(5) + ] + ]} } end diff --git a/lib/logflare/source_schemas/cache.ex b/lib/logflare/source_schemas/cache.ex index 6f57c3056..4419cb7c0 100644 --- a/lib/logflare/source_schemas/cache.ex +++ b/lib/logflare/source_schemas/cache.ex @@ -13,10 +13,14 @@ defmodule Logflare.SourceSchemas.Cache do [ __MODULE__, [ - stats: stats, + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(100_000) + ] + |> Enum.filter(& &1), # shorter expiration for schemas - expiration: Utils.cache_expiration_min(10, 2), - limit: 100_000 + expiration: Utils.cache_expiration_min(10, 2) ] ]} } diff --git a/lib/logflare/sources/cache.ex b/lib/logflare/sources/cache.ex index 10f3d9699..081d23ceb 100644 --- a/lib/logflare/sources/cache.ex +++ b/lib/logflare/sources/cache.ex @@ -11,7 +11,18 @@ defmodule Logflare.Sources.Cache do id: __MODULE__, start: {Cachex, :start_link, - [__MODULE__, [stats: stats, expiration: Utils.cache_expiration_min(), limit: 100_000]]} + [ + __MODULE__, + [ + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(100_000) + ] + |> Enum.filter(& &1), + expiration: Utils.cache_expiration_min() + ] + ]} } end diff --git a/lib/logflare/team_users/cache.ex b/lib/logflare/team_users/cache.ex index e8bde5a37..9baee5521 100644 --- a/lib/logflare/team_users/cache.ex +++ b/lib/logflare/team_users/cache.ex @@ -13,7 +13,18 @@ defmodule Logflare.TeamUsers.Cache do id: __MODULE__, start: {Cachex, :start_link, - [__MODULE__, [stats: stats, expiration: Utils.cache_expiration_min(), limit: 100_000]]} + [ + __MODULE__, + [ + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(100_000) + ] + |> Enum.filter(& &1), + expiration: Utils.cache_expiration_min() + ] + ]} } end diff --git a/lib/logflare/users/cache.ex b/lib/logflare/users/cache.ex index c7e34f9eb..30faac13e 100644 --- a/lib/logflare/users/cache.ex +++ b/lib/logflare/users/cache.ex @@ -13,7 +13,18 @@ defmodule Logflare.Users.Cache do id: __MODULE__, start: {Cachex, :start_link, - [__MODULE__, [stats: stats, expiration: Utils.cache_expiration_min(), limit: 100_000]]} + [ + __MODULE__, + [ + hooks: + [ + if(stats, do: Utils.cache_stats()), + Utils.cache_limit(100_000) + ] + |> Enum.filter(& &1), + expiration: Utils.cache_expiration_min() + ] + ]} } end diff --git a/lib/logflare/utils.ex b/lib/logflare/utils.ex index 85ed0a485..15811927b 100644 --- a/lib/logflare/utils.ex +++ b/lib/logflare/utils.ex @@ -4,6 +4,24 @@ defmodule Logflare.Utils do """ import Cachex.Spec + def cache_stats() do + hook(module: Cachex.Stats) + end + + def cache_limit(n) when is_integer(n) do + hook( + module: Cachex.Limit.Scheduled, + args: { + # setting cache max size + n, + # options for `Cachex.prune/3` + [], + # options for `Cachex.Limit.Scheduled` + [] + } + ) + end + @doc """ Builds a long Cachex expiration spec Defaults to 20 min with 5 min cleanup intervals diff --git a/mix.exs b/mix.exs index 99347d57a..b8595199d 100644 --- a/mix.exs +++ b/mix.exs @@ -103,7 +103,7 @@ defmodule Logflare.Mixfile do {:timex, "~> 3.1"}, {:typed_struct, "~> 0.1", runtime: false}, {:lqueue, "~> 1.1"}, - {:cachex, "~> 3.1"}, + {:cachex, "~> 4.0"}, {:ex_machina, "~> 2.3", only: [:dev, :test]}, {:iteraptor, "~> 1.10"}, {:decorator, "~> 1.3"}, diff --git a/mix.lock b/mix.lock index af7cdfbd6..5451de08b 100644 --- a/mix.lock +++ b/mix.lock @@ -7,7 +7,7 @@ "bertex": {:hex, :bertex, "1.3.0", "0ad0df9159b5110d9d2b6654f72fbf42a54884ef43b6b651e6224c0af30ba3cb", [:mix], [], "hexpm", "0a5d5e478bb5764b7b7bae37cae1ca491200e58b089df121a2fe1c223d8ee57a"}, "broadway": {:git, "https://github.com/Logflare/broadway.git", "092f4ab5891d37be4fb84f4ad91a329c05419db8", [ref: "092f4ab"]}, "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, - "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"}, + "cachex": {:hex, :cachex, "4.0.2", "120f9c27b0a453c7cb3319d9dc6c61c050a480e5299fc1f8bded1e2e334992ab", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:ex_hash_ring, "~> 6.0", [hex: :ex_hash_ring, 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", "4f4890122bddd979f6c217d5e300d0c0d3eb858a976cbe1f65a94e6322bc5825"}, "cainophile": {:git, "https://github.com/Logflare/cainophile.git", "267999b3142f491f2b57f9f892bc9c0ddcadb3a5", [ref: "267999b"]}, "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, @@ -40,6 +40,7 @@ "eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"}, "ets": {:hex, :ets, "0.8.1", "8ff9bcda5682b98493f8878fc9dbd990e48d566cba8cce59f7c2a78130da29ea", [:mix], [], "hexpm", "6be41b50adb5bc5c43626f25ea2d0af1f4a242fb3fad8d53f0c67c20b78915cc"}, "ex2ms": {:hex, :ex2ms, "1.7.0", "45b9f523d0b777667ded60070d82d871a37e294f0b6c5b8eca86771f00f82ee1", [:mix], [], "hexpm", "2589eee51f81f1b1caa6d08c990b1ad409215fe6f64c73f73c67d36ed10be827"}, + "ex_hash_ring": {:hex, :ex_hash_ring, "6.0.4", "bef9d2d796afbbe25ab5b5a7ed746e06b99c76604f558113c273466d52fa6d6b", [:mix], [], "hexpm", "89adabf31f7d3dfaa36802ce598ce918e9b5b33bae8909ac1a4d052e1e567d18"}, "ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"}, "ex_oauth2_provider": {:git, "https://github.com/aristamd/ex_oauth2_provider.git", "8df4b62acfe858e918be38202948b6b4db142dc6", []}, "ex_twilio": {:hex, :ex_twilio, "0.8.2", "86c135827efc6e252a47d3fe95c37368959e840cd012900d11a3ded5a6ca554c", [:mix], [{:httpoison, ">= 0.9.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:inflex, "~> 2.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:joken, "~> 2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:poison, ">= 3.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "b14759966001eaa94d9d432822e9c245724197e4b53d0e40ef44d3f0052ef7bf"}, @@ -134,7 +135,7 @@ "scrivener_ecto": {:hex, :scrivener_ecto, "2.7.0", "cf64b8cb8a96cd131cdbcecf64e7fd395e21aaa1cb0236c42a7c2e34b0dca580", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:scrivener, "~> 2.4", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm", "e809f171687806b0031129034352f5ae44849720c48dd839200adeaf0ac3e260"}, "scrivener_html": {:git, "https://github.com/Logflare/scrivener_html.git", "e81b4845c2542f59f768aba07950459240ddddcc", []}, "scrivener_list": {:hex, :scrivener_list, "2.0.1", "2b3b5c6aaf21d13b76071e755af498b641f37a069e34e68585ba4c624095d719", [:mix], [{:scrivener_ecto, "~> 1.0 or ~> 2.0", [hex: :scrivener_ecto, repo: "hexpm", optional: false]}], "hexpm", "dc82a317268e24b29891b2de659cd82d15654f49ceee86846de2c96b3c4f4e5d"}, - "sleeplocks": {:hex, :sleeplocks, "1.1.2", "d45aa1c5513da48c888715e3381211c859af34bee9b8290490e10c90bb6ff0ca", [:rebar3], [], "hexpm", "9fe5d048c5b781d6305c1a3a0f40bb3dfc06f49bf40571f3d2d0c57eaa7f59a5"}, + "sleeplocks": {:hex, :sleeplocks, "1.1.3", "96a86460cc33b435c7310dbd27ec82ca2c1f24ae38e34f8edde97f756503441a", [:rebar3], [], "hexpm", "d3b3958552e6eb16f463921e70ae7c767519ef8f5be46d7696cc1ed649421321"}, "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},