Skip to main content

Caching

Snapshot providers persist definitions, not per-user results or variant assignments. Reuse one client and evaluate each request with its own context.

Snapshot Providers​

Snapshot providers persist feature definitions for:

  • A last-known definition set when the API is unavailable
  • Resilience to network failures
  • Definition persistence across process restarts

Built-in Providers​

ProviderUse CasePersistence
MemoryTesting, developmentNo
FileSingle-server deploymentsYes (local)
Rails CacheRails applicationsDepends on store
RedisMulti-server deploymentsYes (distributed)

Memory Provider​

In-memory storage (no persistence). This self-contained example exercises the provider protocol without network access:

require 'toggly'
provider = Toggly::SnapshotProviders::Memory.new
# The values are FeatureDefinition objects, not evaluation booleans.
definitions = {
'ExpressCheckout' => Toggly::FeatureDefinition.new(
feature_key: 'ExpressCheckout', enabled: false
)
}
provider.save(definitions, { etag: 'example-revision' })
snapshot = provider.load
puts provider.exists?
provider.clear

Pass snapshot_provider: provider when constructing your process client to let the SDK save/load definitions automatically.

File Provider​

After require 'toggly', store snapshots on the filesystem using a distinct path for each app/environment. This is an alternative startup configuration; close the client at process shutdown.

provider = Toggly::SnapshotProviders::File.new(
path: './tmp/shop-Production-toggly.json'
)

client = Toggly::Client.new(
app_key: ENV['TOGGLY_APP_KEY'],
environment: 'Production',
defaults: { 'ExpressCheckout' => false },
snapshot_provider: provider
)

Rails Cache Provider​

Merge these settings into your single Rails initializer. The provider uses the configured Rails.cache store:

config/initializers/toggly.rb
Toggly::Rails.configure do |config|
config.app_key = Rails.application.credentials.dig(:toggly, :app_key)
config.environment = Rails.env.production? ? 'Production' : 'Staging'
config.defaults = { 'ExpressCheckout' => false }
config.use_rails_cache = true
config.cache_key_prefix = "shop:#{config.environment}:toggly"
end

Works with:

  • memory_store
  • file_store
  • mem_cache_store
  • redis_cache_store
  • Any ActiveSupport::Cache::Store

Redis Provider​

For distributed caching with Redis, use toggly-cache:

Gemfile
gem 'toggly-cache'

toggly-cache accepts the Ruby redis client gem 4.0 or later. Redis client 4.8.1 is retained and Redis client 6.0.0 is verified against a Redis 8 server. Set REDIS_URL or construct the client with the connection settings required by your Redis deployment.

Basic Usage​

Create this provider once at startup, supply your Redis connection configuration, and close the Toggly client at process shutdown:

require 'toggly'
require 'toggly-cache'
require 'redis'

redis = Redis.new(url: ENV['REDIS_URL'])

provider = Toggly::Cache::RedisSnapshotProvider.new(
redis: redis,
key_prefix: 'shop:Production:toggly',
ttl: 3600 # 1 hour
)

client = Toggly::Client.new(
app_key: ENV['TOGGLY_APP_KEY'],
environment: 'Production',
defaults: { 'ExpressCheckout' => false },
snapshot_provider: provider
)

With Connection Pool​

As an alternative to the Redis connection above, add connection_pool to your Gemfile and pass a pool:

require 'connection_pool'

pool = ConnectionPool.new(size: 5, timeout: 5) do
Redis.new(url: ENV['REDIS_URL'])
end

provider = Toggly::Cache::RedisSnapshotProvider.new(
redis: pool,
key_prefix: 'shop:Production:toggly'
)

Redis Options​

OptionTypeDefaultDescription
redisRedis/PoolrequiredRedis client or connection pool
key_prefixString"toggly"Prefix for Redis keys
ttlIntegernilTTL in seconds (nil = no expiration)

Redis Provider Methods​

# Check if snapshot exists
provider.exists? # => true/false

# Get remaining TTL
provider.remaining_ttl # Seconds, or nil when absent/no expiry

# Extend TTL
provider.touch # Uses the configured ttl; false if ttl was omitted

# Clear snapshot
provider.clear

Caching Strategy​

How It Works​

  1. On Startup:

    • Load from snapshot provider (if available)
    • Fetch latest from API
    • Save to snapshot provider
  2. On Refresh:

    • Fetch from API
    • Update in-memory definitions
    • Save to snapshot provider
  3. On API Failure:

    • Retain the last loaded definitions where available
    • Use configured defaults for unknown keys, otherwise false

Startup loads a snapshot and then attempts the initial fetch. A snapshot does not make startup nonblocking. Refresh replaces the definition set; snapshots do not promise atomic consistency across several checks or simultaneous changes in every process.

Configuration​

Use the previously created provider in your one client construction:

client = Toggly::Client.new(
app_key: ENV['TOGGLY_APP_KEY'],
environment: 'Production',
defaults: { 'ExpressCheckout' => false },
snapshot_provider: provider,
refresh_interval: 60, # Refresh every 60 seconds
disable_background_refresh: false # Enable background polling
)

Warm-up​

After your application has configured Toggly.client, force a fetch when you need a fresh definition set:

# In a rake task or initializer
Toggly.client.refresh(force: true)

wait_for_ready waits for readiness; it does not start a fetch.

A restored snapshot can supply feature evaluations even when ready is false after the initial fetch fails. wait_for_ready can therefore time out while those definitions remain available. Report readiness separately from evaluated values; do not replace native readiness with a check for cached keys. Startup still attempts its initial fetch after loading the snapshot. See the runnable Ruby sample's snapshot and lifecycle explanation.

In Rails:

config/initializers/toggly.rb
# After configuration
Rails.application.config.after_initialize do
Toggly.client&.wait_for_ready(timeout: 5)
end

Cache Invalidation​

Manual Refresh​

Toggly.client.refresh(force: true)

Via Rake Task​

rake toggly:refresh

Webhook Trigger​

If an application-owned integration triggers refresh, authenticate and authorize that integration before calling Toggly.client.refresh(force: true). The SDK does not provide a webhook authentication controller.

Best Practices​

1. Use TTL with Redis​

provider = Toggly::Cache::RedisSnapshotProvider.new(
redis: redis,
key_prefix: 'shop:Production:toggly',
ttl: 86400 # 24 hours
)

2. Handle Missing Snapshots​

Set defaults in the existing startup configuration block:

config.defaults = {
'critical-feature' => true
}

3. Log Cache Events​

The Rails adapter uses Rails.logger. For a core client, pass a logger: to Toggly::Client.new; see Logging.

4. Monitor Cache Health​

For the Redis provider above (remaining_ttl is Redis-specific):

# Check if snapshot exists
if provider.exists?
puts "Snapshot available, TTL: #{provider.remaining_ttl}s"
else
puts "No snapshot available"
end

Reliability​

Prefer clearing persisted snapshots after key rotation or suspected corruption. Signed endpoint configuration: Signed definitions. Shared contract: Server-side reliability.