Skip to content

LegionIO/legion-cache

 
 

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_bytes defaults 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 8m to 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

About

A Wrapper class for the LegionIO framework to interface with both Memcached and Redis for caching purposes

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Ruby 100.0%