Info API
View SourceSince Nebulex v3, the adapter's Info API is introduced. This is a more generic API to get information about the cache, including the stats. Adapters are responsible for implementing the Info API and are also free to add the information specification keys they want. Therefore, it is highly recommended to review the adapter's documentation you're using.
See
Nebulex.Cache.info/2for more information.
Nebulex also provides a simple implementation
Nebulex.Adapters.Common.Info, which is used by the
Nebulex.Adapters.Local adapter. This implementation uses a Telemetry
handler to aggregate the stats and keep them updated, therefore, it requires
:telemetry to be available.
Usage
Let's define our cache:
defmodule MyApp.Cache do
use Nebulex.Cache,
otp_app: :my_app,
adapter: Nebulex.Adapters.Local
endAnd the configuration:
config :my_app, MyApp.Cache,
gc_interval: :timer.hours(12),
max_size: 1_000_000,
allocated_memory: 1_000_000,
gc_memory_check_interval: :timer.seconds(10)Once you have set up the MyApp.Cache within the application's supervision
tree, you can get the cache info like so:
iex> MyApp.Cache.info!()
%{
server: %{
nbx_version: "3.0.0",
cache_module: "MyCache",
cache_adapter: "Nebulex.Adapters.Local",
cache_name: "MyCache",
cache_pid: #PID<0.111.0>
},
memory: %{
total: 1_000_000,
used: 0
},
stats: %{
deletions: 0,
evictions: 0,
expirations: 0,
hits: 0,
misses: 0,
updates: 0,
writes: 0
}
}You could also request for a specific item or items:
iex> MyApp.Cache.info!(:stats)
%{
deletions: 0,
evictions: 0,
expirations: 0,
hits: 0,
misses: 0,
updates: 0,
writes: 0
}
iex> MyApp.Cache.info!([:stats, :memory])
%{
memory: %{
total: 1_000_000,
used: 0
},
stats: %{
deletions: 0,
evictions: 0,
expirations: 0,
hits: 0,
misses: 0,
updates: 0,
writes: 0
}
}Understanding Cache Stats
The stats map includes the following metrics:
hits- Number of successfulgetoperations that found a value in the cachemisses- Number ofgetoperations that did not find a value (returnednil)writes- Number ofputoperations that added new entries to the cacheupdates- Number ofputoperations that updated existing cache entriesdeletions- Number of entries removed viadeleteoperationsevictions- Number of entries automatically removed due to exceedingmax_sizeexpirations- Number of entries automatically removed due to TTL expiration
These metrics are useful for understanding cache behavior and health. For example:
iex> stats = MyApp.Cache.info!(:stats).stats
iex> hit_rate = stats.hits / (stats.hits + stats.misses)
iex> IO.inspect(hit_rate, label: "Cache hit rate")Telemetry Metrics
Now, let's see how we can provide metrics out of the info data.
First of all, make sure you have added :telemetry, :telemetry_metrics, and
:telemetry_poller packages as dependencies to your mix.exs file.
Create your Telemetry supervisor at lib/my_app/telemetry.ex:
# lib/my_app/telemetry.ex
defmodule MyApp.Telemetry do
use Supervisor
import Telemetry.Metrics
def start_link(arg) do
Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
end
def init(_arg) do
children = [
# Configure `:telemetry_poller` for reporting the cache stats
{:telemetry_poller, measurements: periodic_measurements(), period: 10_000},
# For example, we use the console reporter, but you can change it.
# See `:telemetry_metrics` for more information.
{Telemetry.Metrics.ConsoleReporter, metrics: metrics()}
]
Supervisor.init(children, strategy: :one_for_one)
end
defp metrics do
[
# Stats
last_value("my_app.cache.info.stats.hits", tags: [:cache]),
last_value("my_app.cache.info.stats.misses", tags: [:cache]),
last_value("my_app.cache.info.stats.writes", tags: [:cache]),
last_value("my_app.cache.info.stats.evictions", tags: [:cache]),
# Memory
last_value("my_app.cache.info.memory.used", tags: [:cache]),
last_value("my_app.cache.info.memory.total", tags: [:cache])
]
end
defp periodic_measurements do
[
{__MODULE__, :cache_stats, []},
{__MODULE__, :cache_memory, []}
]
end
def cache_stats do
with {:ok, info} <- MyApp.Cache.info([:server, :stats]) do
:telemetry.execute(
[:my_app, :cache, :info, :stats],
info.stats,
%{cache: info.server[:cache_name]}
)
end
:ok
end
def cache_memory do
with {:ok, info} <- MyApp.Cache.info([:server, :memory]) do
:telemetry.execute(
[:my_app, :cache, :info, :memory],
info.memory,
%{cache: info.server[:cache_name]}
)
end
:ok
end
endThen add it to your main application's supervision tree
(usually in lib/my_app/application.ex):
children = [
MyApp.Cache,
MyApp.Telemetry,
...
]Now start an IEx session and you should see something like the following output:
[Telemetry.Metrics.ConsoleReporter] Got new event!
Event name: my_app.cache.info.stats
All measurements: %{evictions: 2, hits: 1, misses: 2, writes: 2}
All metadata: %{cache: MyApp.Cache}
Metric measurement: :hits (last_value)
With value: 1
Tag values: %{cache: MyApp.Cache}
Metric measurement: :misses (last_value)
With value: 2
Tag values: %{cache: MyApp.Cache}
Metric measurement: :writes (last_value)
With value: 2
Tag values: %{cache: MyApp.Cache}
Metric measurement: :evictions (last_value)
With value: 2
Tag values: %{cache: MyApp.Cache}
[Telemetry.Metrics.ConsoleReporter] Got new event!
Event name: my_app.cache.info.memory
All measurements: %{total: 2000000, used: 0}
All metadata: %{cache: MyApp.Cache}
Metric measurement: :total (last_value)
With value: 2000000
Tag values: %{cache: MyApp.Cache}
Metric measurement: :used (last_value)
With value: 0
Tag values: %{cache: MyApp.Cache}Custom metrics
In the same way, you can add another periodic measurement for reporting the cache size:
defmodule MyApp.Cache do
use Nebulex.Cache,
otp_app: :my_app,
adapter: Nebulex.Adapters.Local
def dispatch_cache_size do
:telemetry.execute(
[:my_app, :cache, :size],
%{value: count_all()},
%{cache: __MODULE__, node: node()}
)
end
endNow let's add a new periodic measurement to invoke dispatch_cache_size()
through :telemetry_poller:
defp periodic_measurements do
[
{__MODULE__, :cache_stats, []},
{__MODULE__, :cache_memory, []},
{MyApp.Cache, :dispatch_cache_size, []}
]
endNotice the node name was added to the metadata so we can use it in the metric tags.
Metrics:
defp metrics do
[
# Stats
last_value("my_app.cache.info.stats.hits", tags: [:cache]),
last_value("my_app.cache.info.stats.misses", tags: [:cache]),
last_value("my_app.cache.info.stats.writes", tags: [:cache]),
last_value("my_app.cache.info.stats.evictions", tags: [:cache]),
# Memory
last_value("my_app.cache.info.memory.used", tags: [:cache]),
last_value("my_app.cache.info.memory.total", tags: [:cache]),
# Nebulex custom Metrics
last_value("my_app.cache.size.value", tags: [:cache, :node])
]
endIf you start an IEx session like previously, you should see the new metric too:
[Telemetry.Metrics.ConsoleReporter] Got new event!
Event name: my_app.cache.size
All measurements: %{value: 0}
All metadata: %{cache: MyApp.Cache, node: :nonode@nohost}
Metric measurement: :value (last_value)
With value: 0
Tag values: %{cache: MyApp.Cache, node: :nonode@nohost}Interpreting the Metrics
Once you have metrics flowing, you can use them to understand cache health:
Cache Hit Rate
Calculate hit rate as a percentage of successful lookups:
hit_rate = hits / (hits + misses) * 100- Hit rates above 80% generally indicate a well-sized cache
- Low hit rates (<30%) may indicate the cache is too small or TTL too short
- A hit rate of 0% suggests the cache isn't being used effectively
Memory Usage
Monitor memory trends:
utilization = used / total * 100- Consistently high utilization (>90%) may trigger frequent evictions
- Growing memory usage over time can indicate memory leaks
- Spikes in memory can correlate with batch operations
Eviction Rate
Track evictions over time:
- Increasing evictions usually mean cache capacity is insufficient
- Sudden eviction spikes may indicate unusual access patterns
- High evictions combined with low hit rate suggests cache configuration issues
Activity Indicators
writes + updatesshows cache update frequencydeletions + evictions + expirationsshows cache turnover- Imbalanced ratios can reveal inefficient access patterns
Poller Interval and Configuration
The :telemetry_poller with period: 10_000 reports cache stats every 10 seconds.
Consider your needs:
# Every 5 seconds - for closely monitoring cache behavior (more overhead)
{:telemetry_poller, measurements: periodic_measurements(), period: 5_000},
# Every 10 seconds - good default for most applications
{:telemetry_poller, measurements: periodic_measurements(), period: 10_000},
# Every 30 seconds - for low-overhead monitoring in production
{:telemetry_poller, measurements: periodic_measurements(), period: 30_000},Trade-offs:
- Shorter periods: More frequent updates, higher overhead, better precision for detecting rapid changes
- Longer periods: Lower overhead, less precise but sufficient for long-term trending
- Adaptive: You can use different periods in different environments (dev, staging, production)
Monitoring Multiple Cache Instances
If your application uses multiple cache instances or dynamic caches, you can track them separately by including cache metadata in the telemetry event:
def cache_stats do
with {:ok, info} <- MyApp.Cache.info([:server, :stats]) do
:telemetry.execute(
[:my_app, :cache, :info, :stats],
info.stats,
%{
cache: info.server[:cache_name],
cache_module: info.server[:cache_module]
}
)
end
:ok
endThen in your metrics, group by cache instance:
last_value("my_app.cache.info.stats.hits",
tags: [:cache, :cache_module],
tag_values: &%{
cache: &1.cache,
cache_module: &1.cache_module
}
)Adapter-Specific Information
The Info API is adapter-specific. While Nebulex.Adapters.Local provides the stats
and memory metrics shown in this guide, other adapters may provide different information keys.
Always consult your adapter's documentation for:
- Available info keys (
:stats,:memory,:server, etc.) - What each stat means in the adapter's context
- Any adapter-specific configuration options
- Performance characteristics of the Info API itself
For example, some adapters may not support all stat types or may have different memory measurement approaches for distributed caches.