legion-cache
Caching wrapper for the LegionIO framework. Provides a consistent interface for Memcached (via dalli) and Redis (via redis gem) with connection pooling. Driver selection is config-driven.
Version: 1.3.17
Installation
gem install legion-cache
Or add to your Gemfile:
gem 'legion-cache'
Usage
require 'legion/cache'
Legion::Cache.setup
Legion::Cache.connected? # => true
# Memcached driver (default) — TTL is a positional argument, default 180s
Legion::Cache.set('foobar', 'testing', 10)
Legion::Cache.get('foobar') # => 'testing'
Legion::Cache.fetch('foobar') # => 'testing' (get with block support)
Legion::Cache.delete('foobar') # => true
Legion::Cache.flush # flush all keys
# Redis driver — TTL is the third positional argument
Legion::Cache.set('foobar', 'testing', 10)
Legion::Cache.get('foobar') # => 'testing'
Legion::Cache.delete('foobar') # => true
Legion::Cache.flush # flushdb
Legion::Cache.shutdown
Lite Mode (No Infrastructure)
When LEGION_MODE=lite is set, Legion::Cache activates the pure in-memory Memory adapter, bypassing Redis and Memcached entirely:
ENV['LEGION_MODE'] = 'lite'
Legion::Cache.setup
Legion::Cache.using_memory? # => true
Legion::Cache.set('key', 'value', 60)
Legion::Cache.get('key') # => 'value'
The Memory adapter is thread-safe (Mutex), supports TTL expiry, and exposes the same get/set/fetch/delete/flush interface. Shutdown cleanly tears it down.
Two-Tier Cache
Legion::Cache supports a two-tier architecture: a shared remote cluster and a local per-machine cache. If the shared cluster is unreachable at setup, all operations transparently fall back to local.
# Shared cache connects to remote cluster; Local connects to localhost
Legion::Cache.setup # starts Local first, then tries shared
Legion::Cache.using_local? # => true if shared was unreachable
Legion::Cache.local # => Legion::Cache::Local
# Use Local directly if needed
Legion::Cache::Local.setup
Legion::Cache::Local.set('key', 'value', 60)
Legion::Cache::Local.get('key') # => 'value'
Legion::Cache::Local.shutdown
Local uses a separate namespace (legion_local) and independent connection pool (pool_size: 5, timeout: 3) so it never collides with the shared tier.
Configuration
{
"driver": "dalli",
"servers": ["127.0.0.1:11211"],
"connected": false,
"enabled": true,
"namespace": "legion",
"compress": false,
"failover": true,
"threadsafe": true,
"cache_nils": false,
"pool_size": 10,
"timeout": 5,
"expires_in": 0
}
The driver is auto-detected at load time: prefers dalli (Memcached) if available, falls back to redis. Override with "driver": "redis" and update servers to point at your Redis instance.
Driver Names
Supported driver names: memcached (or dalli), redis. All names are normalized internally — "memcached" and "dalli" are equivalent.
Server Resolution
Both server (singular string) and servers (array) are accepted and merged. Default ports are injected per driver when omitted: 11211 for memcached, 6379 for redis. Duplicates are removed.
{
"cache": {
"driver": "memcached",
"server": "10.0.0.5",
"servers": ["10.0.0.6", "10.0.0.7:22122"]
}
}
Memcached notes
value_max_bytesdefaults to 8MB. Dalli enforces a 1MB client-side limit by default, which silently rejects large values. This default overrides that. Your Memcached server should also be started with-I 8mto match.- Redis default pool size is 20; Memcached default pool size is 10.
Local Cache Settings
{
"driver": "dalli",
"servers": ["127.0.0.1:11211"],
"namespace": "legion_local",
"pool_size": 5,
"timeout": 3
}
Override via Legion::Settings[:cache_local].
Method Caching
Runner modules can use cache_method to transparently cache method results with TTL:
module Runners::Presence
extend Legion::Cache::Cacheable
cache_method :get_presence, ttl: 300, exclude_from_key: [:token]
def get_presence(user_id: 'me', **)
conn = graph_connection(**)
response = conn.get("#{user_path(user_id)}/presence")
{ availability: response.body['availability'], activity: response.body['activity'] }
end
end
Every caller of get_presence gets cached results for 5 minutes. Use bypass_local_method_cache: true to force-refresh:
runner.get_presence(user_id: 'me') # cached
runner.get_presence(user_id: 'me', bypass_local_method_cache: true) # fresh
Options: ttl: (seconds), scope: (:local or :global), exclude_from_key: (args to ignore in cache key). Falls back to in-memory store when no cache backend is available.
Pool API
Legion::Cache.connected? # => true/false
Legion::Cache.size # total pool connections
Legion::Cache.available # idle pool connections
Legion::Cache.restart # close and reconnect pool
Legion::Cache.shutdown # close pool and mark disconnected
Requirements
- Ruby >= 3.4
- Memcached or Redis server
License
Apache-2.0