Exth.Provider.ClientCache (Exth v0.4.2)
View SourceProvides caching functionality for Ethereum JSON-RPC clients.
This module implements a high-performance, persistent caching mechanism for RPC clients
using Erlang's :persistent_term
storage. It ensures efficient client reuse while
maintaining a small memory footprint.
Features
- Persistent client caching
- Thread-safe operations
- O(1) lookup performance
- Memory-efficient storage
- Process-independent cache
- Atomic operations
- Automatic client lifecycle
Cache Architecture
The cache is built on :persistent_term
and provides:
- Fast, constant-time lookups
- Single copy per cached term
- Process crash resilience
- Node-local storage
- Atomic operations
- Low memory overhead
Cache Structure
Keys:
- Format:
{__MODULE__, transport_type, rpc_url}
- Unique per client configuration
- Efficient lookup pattern
Values:
- Type:
Exth.Rpc.Client
structs - Complete client configuration
- Ready-to-use instances
Usage Examples
# Fetch existing client
{:ok, client} = ClientCache.get_client(:http, "https://eth-mainnet.example.com")
# Create and cache new client
client = ClientCache.create_client(
:http,
"https://eth-mainnet.example.com",
%Client{
transport: transport,
counter: counter
}
)
# Pattern for get-or-create
case ClientCache.get_client(transport_type, rpc_url) do
{:ok, client} ->
client
{:error, :not_found} ->
client = create_new_client()
ClientCache.create_client(transport_type, rpc_url, client)
end
Performance Characteristics
Read Operations:
- O(1) lookup time
- No process communication
- Zero-copy term sharing
- Minimal memory overhead
Write Operations:
- Atomic updates
- Copy-on-write semantics
- Process-independent durability
Cache Properties:
- Node-local scope
- Process-independent
- Crash-resilient
- Memory-efficient
Best Practices
- Reuse clients when possible
- Handle cache misses gracefully
- Monitor cache size
- Clean up unused clients
- Use appropriate timeouts
- Implement circuit breakers
Limitations
- Node-local only (not distributed)
- No automatic cleanup
- Memory limited by node
- No expiration mechanism
Error Handling
{:ok, client}
- Client found in cache{:error, :not_found}
- Cache miss- Raises
ArgumentError
for invalid inputs
Implementation Details
The cache uses Erlang's :persistent_term
module which:
- Stores terms in a special ETS table
- Provides fast, copy-free reads
- Maintains single source of truth
- Survives process crashes
- Uses minimal memory
See Exth.Rpc.Client
for client details and
Exth.Transport
for transport configuration.
Summary
Functions
Creates and caches a new RPC client for the given transport type and URL.
Retrieves a cached RPC client for the given transport type and URL.
Functions
@spec create_client(Exth.Transport.type(), String.t(), Exth.Rpc.Client.t()) :: Exth.Rpc.Client.t()
Creates and caches a new RPC client for the given transport type and URL.
Stores the client in persistent term storage and returns the cached client. If a client already exists for the given combination, it will be overwritten.
Parameters
transport_type
- The type of transport (e.g.,:http
,:websocket
)rpc_url
- The URL of the Ethereum JSON-RPC endpointclient
- TheClient
struct to cache
Returns
- The cached client instance
Examples
iex> client = %Client{}
iex> ClientCache.create_client(:http, "https://eth-mainnet.example.com", client)
%Client{}
@spec get_client(Exth.Transport.type(), String.t()) :: {:ok, Exth.Rpc.Client.t()} | {:error, :not_found}
Retrieves a cached RPC client for the given transport type and URL.
Attempts to fetch an existing client from the persistent term storage.
Returns {:error, :not_found}
if no client exists for the given combination.
Parameters
transport_type
- The type of transport (e.g.,:http
,:websocket
)rpc_url
- The URL of the Ethereum JSON-RPC endpoint
Returns
{:ok, client}
- When a cached client is found{:error, :not_found}
- When no cached client exists
Examples
iex> ClientCache.get_client(:http, "https://eth-mainnet.example.com")
{:ok, %Client{}}
iex> ClientCache.get_client(:http, "https://non-existing.example.com")
{:error, :not_found}