NanoGlobalCache
🔒 Lightweight global cache for Elixir with expiration support and intelligent failure handling.
Perfect for caching OAuth tokens, API keys, and other time-sensitive data that shouldn't be repeatedly refreshed.
Why NanoGlobalCache?
- ✅ Smart caching: Caches successes, retries failures on next fetch
- 🌍 Distributed: Shared across entire Erlang cluster
-
🔐 Concurrency-safe: Safe concurrent access via
:global.trans/2 - ⏱️ Expiration: Time-based invalidation
- 📝 Clean DSL: Spark-based compile-time configuration with auto-generated functions
- ⚡ Minimal overhead: No background processes or setup
Installation
def deps do
[{:nano_global_cache, "~> 0.2.1"}]
endQuick Example
defmodule MyApp.TokenCache do
use NanoGlobalCache
# Regular OAuth token - calculate expiration yourself
cache :github do
fetch fn ->
case GitHub.refresh_token() do
{:ok, token} ->
expires_at = System.system_time(:millisecond) + :timer.hours(1)
{:ok, token, expires_at}
:error ->
:error
end
end
end
# JWT token - use expiration time from the token itself
cache :auth0 do
fetch fn ->
case Auth0.get_access_token() do
{:ok, jwt} ->
# JWT exp claim is in seconds, convert to milliseconds
%{"exp" => exp_seconds} = JOSE.JWT.peek_payload(jwt)
expires_at = exp_seconds * 1000
{:ok, jwt, expires_at}
:error ->
:error
end
end
end
# Generated functions: fetch/1, fetch!/1, clear/1, clear_all/0
endUsage
# Pattern match on result with expiration time
{:ok, token, expires_at} = MyApp.TokenCache.fetch(:github)
# Or use bang version (no expiration time)
token = MyApp.TokenCache.fetch!(:github)
# Clear cache
MyApp.TokenCache.clear(:github)
MyApp.TokenCache.clear_all()How It Works
- Successful results: Cached with expiration time, returned until expiration
- Failed results (
:error): Never cached, always retried on next call - Distributed concurrency: All operations use global Erlang transactions (
global.trans/2) for safe access across nodes
When to Use
This library is optimized for lightweight data like:
- OAuth tokens, API keys, JWT tokens
- Small configuration values
- Session identifiers
- Cached credentials
NOT recommended for:
- High-traffic scenarios (frequent reads/writes)
- Dynamic cache keys (unbounded number of entries)
- Large cache values that would cause heavy network traffic between nodes
NanoGlobalCache uses :global and Agent for simplicity and minimal overhead. Each cache lives on a single node without replication - other nodes access it remotely. It's designed for scenarios where performance impact is negligible and simplicity is valued over throughput.
For more demanding use cases, consider Cachex or Nebulex.
API Reference
Define Caches
cache :cache_name do
fetch fn ->
# Your fetch logic here
# Must return {:ok, value, expires_at} or :error
# expires_at is Unix timestamp in milliseconds
{:ok, value, System.system_time(:millisecond) + ttl_milliseconds}
end
endGenerated Functions
fetch(name)→{:ok, value, expires_at}or:errorfetch!(name)→value(without expiration time) or raisesRuntimeErrorclear(name)→:okclear_all()→:ok
Implementation
- Spark DSL for compile-time configuration
- Erlang global agents for distributed storage
- Automatic function generation via transformers