Caching
The core client evaluates downloaded definitions in memory. A snapshot provider preserves definitions for initialization and recovery; it does not make each flag check a cache-server request or coordinate refresh across workers.
Core snapshot providers
TogglyConfig(snapshot_provider=...) accepts the core SnapshotProvider interface. The built-in MemorySnapshotProvider is the default. Use FileSnapshotProvider to retain definitions, JWKS and variants across restarts.
import os
from toggly import TogglyClient, TogglyConfig
from toggly.providers import FileSnapshotProvider
provider = FileSnapshotProvider(directory="./toggly-cache/checkout-production")
client = TogglyClient(TogglyConfig(
app_key=os.environ.get("TOGGLY_APP_KEY"),
environment="Production",
snapshot_provider=provider,
))
client.init()
try:
print(client.is_enabled("new-dashboard", default=False))
finally:
client.close()
Choose a separate directory per application/environment and grant the worker permission to write it. A cache miss uses configured defaults until definitions are available. For a server, keep the client alive between requests and close it at worker shutdown; the short example above owns its whole lifecycle.
Deterministic offline definitions
For tests, seed a MemorySnapshotProvider with real definitions and omit the app key. This exercises the SDK's evaluator without making a definition API request:
from toggly import TogglyClient, TogglyConfig, FeatureDefinition, FeatureFilter
from toggly.providers import MemorySnapshotProvider, DefinitionsSnapshot
provider = MemorySnapshotProvider()
provider.save_definitions(DefinitionsSnapshot(definitions=[
FeatureDefinition("new-dashboard", [FeatureFilter("AlwaysOn")]),
]))
client = TogglyClient(TogglyConfig(
snapshot_provider=provider,
disable_background_refresh=True,
enable_live_updates=False,
enable_usage_tracking=False,
register_contexts_on_startup=False,
))
client.init()
try:
assert client.is_enabled("new-dashboard")
assert not client.is_enabled("missing-flag")
finally:
client.close()
Use EvaluationContext in checks to exercise user, claim, request and entity targeting. A default boolean alone cannot test those rules.
Redis and Memcached utilities
The optional toggly-cache package exports RedisSnapshotProvider and MemcachedSnapshotProvider. These utilities expose save(app_key, environment, definitions) and load(app_key, environment), which store a list of FeatureDefinition objects. They do not implement the core SnapshotProvider interface and cannot be passed directly to TogglyConfig.snapshot_provider.
Use their explicit storage methods for application-managed definition lists. They do not preserve the full signed-definition envelope, JWKS, or remote variant context. Use a core provider for those client features; never treat a bare cached list as a verified signed snapshot.
pip install 'toggly-cache[redis]'
pip install 'toggly-cache[memcached]'
Redis
Run Redis locally on port 6379 before executing this standalone storage example. The storage namespace below is demonstration data, not a Toggly credential.
from toggly import FeatureDefinition, FeatureFilter
from toggly_cache import RedisSnapshotProvider
provider = RedisSnapshotProvider(
host="localhost", port=6379, db=0,
prefix="example:", ttl=3600,
)
try:
provider.save("demo-storage", "Development", [
FeatureDefinition("new-dashboard", [FeatureFilter("AlwaysOn")]),
])
definitions = provider.load("demo-storage", "Development")
assert definitions is not None and definitions[0].feature_key == "new-dashboard"
assert provider.exists("demo-storage", "Development")
provider.delete("demo-storage", "Development")
finally:
provider.close()
ttl=None leaves Redis data without expiry. prefix separates cache namespaces. Additional Redis connection options, such as password and ssl, configure the underlying Redis client. For a URL, construct that client explicitly:
import os
from redis import Redis
from toggly_cache import RedisSnapshotProvider
redis_client = Redis.from_url(os.environ["REDIS_URL"])
provider = RedisSnapshotProvider(client=redis_client, prefix="checkout:")
# Use provider.save/load as above; close the connection when its owner shuts down.
provider.close()
provider.close() calls the underlying client's close() even when supplied externally; coordinate ownership if sharing a connection client.
Memcached
Run Memcached on port 11211 before executing this standalone storage example:
from toggly import FeatureDefinition, FeatureFilter
from toggly_cache import MemcachedSnapshotProvider
provider = MemcachedSnapshotProvider(
servers=[("localhost", 11211)],
prefix="example:", ttl=3600,
)
try:
provider.save("demo-storage", "Development", [
FeatureDefinition("new-dashboard", [FeatureFilter("AlwaysOn")]),
])
definitions = provider.load("demo-storage", "Development")
assert definitions is not None and definitions[0].feature_key == "new-dashboard"
assert provider.exists("demo-storage", "Development")
provider.delete("demo-storage", "Development")
finally:
provider.close()
Pass one server for a single client or multiple servers for a hash client. ttl=0 means no expiry. You can supply an existing pymemcache client using client=. As with Redis, closing the provider closes that client's connection.
Failure behavior
load returns None on a miss or a read/parse error. exists and delete return False on errors. save raises storage errors: handle them according to your application's durability requirements. Neither utility has an async API; run its blocking operations outside the event loop when using async application code.
Framework integration
Create a core client with a MemorySnapshotProvider or FileSnapshotProvider as shown above, initialize it once, and keep it open for the worker's lifetime. Register it with the adapter at startup:
# Django: in your application's startup, with TOGGLY unset so the
# SDK AppConfig does not initialize a second client.
from toggly import set_default_client
set_default_client(client)
# Flask: use the same supplied client for extension and decorators.
from flask import Flask
from toggly import set_default_client
from toggly_flask import Toggly
app = Flask(__name__)
set_default_client(client)
toggly = Toggly(app, client=client)
# FastAPI: register inside lifespan, then close client in its finally block.
from toggly_fastapi import configure_toggly
configure_toggly(client=client)
See the Django, Flask, and FastAPI guides for complete request setup. The initialization examples above close their clients at the end; move registration and request serving before that shutdown when adapting them to a server.
Reliability: signed snapshots
Use the built-in core snapshot providers for signed definitions. They preserve the complete signature metadata and exact signed JSON needed for verification after storage. A bare definition list from the optional Redis/Memcached utilities cannot establish signature validity. Keep JWKS and definitions in the same application/environment storage namespace, and retain the full remote variant context fingerprint when caching variants.
Custom providers
Implement toggly.providers.SnapshotProvider when your application needs another core storage backend. Required methods are load_definitions, save_definitions, load_jwks, and save_jwks. Implement load_variants and save_variants for remote variant persistence, and define clear and clear_jwks for cache invalidation.
Preserve the complete snapshot models, including signed payload bytes represented by signed_defs_json, signature metadata and remote variant context_key. Namespace storage by application/environment and protect concurrent writes. The core provider implementations are the reference for these interfaces.