Configuration

On this page

All configuration for the CacheManager pipeline (and, indirectly, MultiLevelCache) flows through one dataclass tree rooted at CacheConfig, defined in cache/config_cache. Pass a single CacheConfig instance to every component that needs it — the codebase's own docstring for CacheConfig states this directly: "Single root config object – pass one instance everywhere."

CacheConfig at a glance

Python · 3 lines
from zerda.cache import CacheConfig

config = CacheConfig()          # every field below takes its documented default

Storage & embedding backend selection

FieldTypeDefaultEffect
storage_backendStorageBackendStorageBackend.SQLITESelects MemoryStorage, SQLiteStorage, or RedisStorage via build_storage()
redisRedisConfigsee belowUsed only when storage_backend == StorageBackend.REDIS
sqliteSQLiteConfigsee belowUsed only when storage_backend == StorageBackend.SQLITE (also used as RedisStorage's fallback file)
embeddingEmbeddingConfigsee belowControls which embedding provider CachedEmbedder builds

Exact-cache (L1) sizing — top-level fields

[!WARNING] Duplicate field names in the source CacheConfig is a single @dataclass whose class body assigns l1_max_items, l2_max_items, and default_ttl_s/default_ttl more than once (once early in the class near a _N_BUCKETS comment, and again later under "Item limits per level"). In a Python dataclass, the last assignment in the source order wins — earlier values are silently discarded when the class is defined. The effective defaults, as actually produced by CacheConfig(), are the second block's values, shown in the table below. This is very likely unintentional duplication in the source rather than a deliberate override mechanism, and is listed again in Known Issues.

FieldEffective defaultUsed by
l1_max_items100CacheManager._l1 (_L1ExactCache)
l2_max_items1000(no CacheManager L2 concept — see note below)
l3_max_items10000MultiLevelCache (only if not overridden in its constructor)
l1_max_size_mb10.0MultiLevelCache L1 byte budget
l2_max_size_mb100.0MultiLevelCache L2 byte budget
l3_max_size_mb1000.0MultiLevelCache L3 (disk) byte budget
default_ttlNoneMultiLevelCache.set() default TTL
default_ttl_s3600.0CacheManager.put() default TTL
cache_dir"./cache_storage"MultiLevelCache L3 disk directory
enable_statsTrueDeclared but not read by any method in the analyzed source
auto_cleanup_interval3600MultiLevelCache's periodic cleanup_expired() timer interval (seconds)
l1_speed_ns / l2_speed_ns / l3_speed_ns100 / 1000 / 10000Declared for reference/simulation; not read by any method in the analyzed source
l2_to_l1_hits10MultiLevelCache.get() — hit-count threshold that triggers L2→L1 promotion
size_bytes1024Default estimated size for a CacheEntry when pickle.dumps fails (see cache_entry.CacheEntry._calculate_size)

[!NOTE] CacheManager has no L2 in the MultiLevelCache sense l2_max_items is validated (l2_max_items >= l1_max_items) but, in the analyzed source, is only actually used to size MultiLevelCache's second tier. CacheManager itself has just one in-process exact-match tier (_L1ExactCache, sized by l1_max_items) plus the persistence backend and the semantic vector index — it does not instantiate a second in-process tier.

Building from the environment

Python · 3 lines
from zerda.cache import CacheConfig

config = CacheConfig.from_env()

from_env() only overrides l1_max_items, l2_max_items, l3_max_items, cache_dir, and default_ttl from CACHE_L1_MAX_ITEMS, CACHE_L2_MAX_ITEMS, CACHE_L3_MAX_ITEMS, CACHE_DIR, and CACHE_DEFAULT_TTL respectively — every other field (Redis, embeddings, security, tenants, etc.) still takes its dataclass default (which, for RedisConfig / SQLiteConfig / SecurityConfig, is itself independently environment-aware; see Installation).

Validation

CacheConfig.__post_init__ calls _validate(), which raises ValueError if:

  • l1_max_items < 1
  • l2_max_items < l1_max_items
  • l3_max_items < l2_max_items
  • l1_max_size_mb, l2_max_size_mb, or l3_max_size_mb is <= 0
  • default_ttl is not None and is <= 0

No validation is performed on the Redis, embedding, semantic, cost, RL, tenant, security, eviction, or performance sub-configs in the analyzed source.

Example: production-leaning configuration

Python · 16 lines
from zerda.cache import (
    CacheConfig, StorageBackend, RedisConfig,
    EmbeddingConfig, EmbeddingProvider, SecurityConfig, TenantConfig,
)

config = CacheConfig(
    storage_backend=StorageBackend.REDIS,
    redis=RedisConfig(host="redis.internal", port=6379, ssl=True),
    embedding=EmbeddingConfig(
        provider=EmbeddingProvider.OPENAI,
        model_name="text-embedding-3-small",
    ),
    security=SecurityConfig(enable_pii_scrub=True, hmac_secret="change-me"),
    tenant=TenantConfig(default_memory_quota_mb=2048, enable_shared_cache=False),
    default_ttl_s=6 * 3600,
)

[!DANGER] OpenAIEmbeddingProvider requires a package not present in this repo Selecting EmbeddingProvider.OPENAI (or HUGGINGFACE/OLLAMA) causes build_embedding_provider() to from zerda.embeddings import OpenAIEmbedder (or the HuggingFace/Ollama equivalent). That zerda.embeddings module is not part of this codebase — it's an external dependency referenced by name only. If it isn't installed, construction raises ImportError with the message "Install zerda_rag[embeddings]: pip install zerda_rag[embeddings]". See Known Issues for details on all three non-mock providers.

Related pages