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
| Provider | Use Case | Persistence |
|---|---|---|
| Memory | Testing, development | No |
| File | Single-server deployments | Yes (local) |
| Rails Cache | Rails applications | Depends on store |
| Redis | Multi-server deployments | Yes (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:
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_storefile_storemem_cache_storeredis_cache_store- Any ActiveSupport::Cache::Store
Redis Provider
For distributed caching with Redis, use toggly-cache:
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
| Option | Type | Default | Description |
|---|---|---|---|
redis | Redis/Pool | required | Redis client or connection pool |
key_prefix | String | "toggly" | Prefix for Redis keys |
ttl | Integer | nil | TTL 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
-
On Startup:
- Load from snapshot provider (if available)
- Fetch latest from API
- Save to snapshot provider
-
On Refresh:
- Fetch from API
- Update in-memory definitions
- Save to snapshot provider
-
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:
# 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.