# Multi-Level Cache Multi-level caching enables hierarchical cache storage, similar to how CPUs use L1/L2/L3 caches or CDNs use edge/regional/origin tiers. This feature allows sccache to check multiple storage backends in sequence, dramatically improving cache hit rates and reducing latency. ## Table of Contents - [Overview](#overview) - [Architecture](#architecture) - [Use Cases](#use-cases) - [Configuration](#configuration) - [Best Practices](#best-practices) ## Overview Multi-level caching allows you to configure multiple cache storage backends that work together: - **Fast, small caches** (e.g., local disk) are checked first - **Slower, larger caches** (e.g., S3) are checked if earlier levels miss - **Cache hits at any level** return immediately to the compiler - **Automatic backfill** copies data from slower to faster levels for future requests - **Write-through** ensures all levels stay synchronized on writes This creates a cache hierarchy where frequently accessed artifacts stay in fast storage while less common ones are still available from slower storage. ## Architecture ### Cache Hierarchy ``` ┌─────────────────────────────────────────────────┐ │ Compiler Request │ └─────────────────────┬───────────────────────────┘ │ ┌────────────▼────────────┐ │ Multi-Level Storage │ └────────────┬────────────┘ │ ┌───────────────┼───────────────┐ │ │ │ ┌─────▼─────┐ ┌────▼────┐ ┌────▼────┐ │ Level 0 │ │ Level 1 │ │ Level 2 │ │ (Disk) │ │ (Redis) │ │ (S3) │ │ │ │ │ │ │ │ Fast │ │ Medium │ │ Slow │ │ Small │ │ Medium │ │ Large │ │ ~5ms │ │ ~10ms │ │ ~200ms │ └───────────┘ └─────────┘ └─────────┘ ``` ### Read Path (Cache Hit at Level 2) ``` 1. Check L0 (disk) → Miss (5ms) 2. Check L1 (redis) → Miss (10ms) 3. Check L2 (s3) → Hit! (200ms) 4. Return to compiler wrapper / sccache (Total: 215ms) 5. Background: Backfill L2→L1 (async, non-blocking) 6. Background: Backfill L2→L0 (async, non-blocking) 7. Next request: Check L0 → Hit! (10ms) ``` ### Write Path All write operations go to **all configured levels** in parallel: ``` Compiler writes artifact ├─> L0 (disk) ✓ ├─> L1 (redis) ✓ └─> L2 (s3) ✓ ``` If any level fails, the error is logged but the write succeeds if at least one level accepts it. ## Use Cases ### 1. CI/CD with Shared Team Cache **Problem**: Each CI runner has isolated disk cache, no sharing across machines. **Solution**: Add Redis or Memcached as L1 ```bash SCCACHE_MULTILEVEL_CHAIN="disk,redis" SCCACHE_DIR="/tmp/sccache" SCCACHE_REDIS_ENDPOINT="redis://cache.internal:6379" ``` **Result**: Fast local hits when available, team-shared cache otherwise. ### 2. Enterprise with CDN-like Architecture **Problem**: Global team with high S3 latency, want local speed. **Solution**: Multi-tier hierarchy ```bash SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3" ``` - L0: Local disk (instant) - L1: Regional Redis (5-10ms) - L2: Global S3 bucket (50-200ms) **Result**: 90%+ hits at L0/L1, L2 as long-term backup. ### 3. Developer Workstation with Cloud Backup **Problem**: Local disk fills up, don't want to lose cache history. **Solution**: Disk + cloud storage ```bash SCCACHE_MULTILEVEL_CHAIN="disk,s3" SCCACHE_DIR="$HOME/.cache/sccache" SCCACHE_BUCKET="my-personal-sccache" SCCACHE_CACHE_SIZE="5G" # Keep disk small ``` **Result**: Unlimited cloud storage, fast local hits. ## Configuration ### Via Environment Variables The primary configuration is `SCCACHE_MULTILEVEL_CHAIN`: ```bash export SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3" ``` **Format**: Comma-separated list of cache backend names **Order**: Left-to-right is fast-to-slow (L0, L1, L2, ...) **Valid names**: `disk`, `redis`, `memcached`, `s3`, `gcs`, `azure`, `gha`, `webdav`, `oss`, `cos` ### Write Error Policy Configuration Control how sccache handles write failures across cache levels using `SCCACHE_MULTILEVEL_WRITE_ERROR_POLICY`: **Available policies**: - **`ignore`** - Never fail on write errors, log warnings only (most permissive) - **`l0`** - Fail only if L0 (first level) write fails (default - balances reliability and performance) - **`all`** - Fail if any read-write level write fails (most strict) **Note**: Read-only levels are always skipped during writes and never cause failures. #### Write Error Policy Examples **Example 1: Default Behavior (l0 policy)** ```bash export SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3" export SCCACHE_MULTILEVEL_WRITE_ERROR_POLICY="l0" # or omit, it's the default ``` Compilation succeeds if disk write succeeds. Redis/S3 failures are logged but don't block compilation. Ensures local cache is always populated. **Best for most use cases.** **Example 2: Best Effort (ignore policy)** ```bash export SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3" export SCCACHE_MULTILEVEL_WRITE_ERROR_POLICY="ignore" ``` Compilation always succeeds, even if all writes fail. Write failures are logged as warnings. **Best for unstable cache backends** where you don't want cache issues blocking builds. **Example 3: Strict Consistency (all policy)** ```bash export SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3" export SCCACHE_MULTILEVEL_WRITE_ERROR_POLICY="all" ``` Compilation succeeds only if all read-write levels succeed. Any write failure fails the compilation. **Best for critical environments** where cache consistency is mandatory. #### Read-Only Levels Any level configured as read-only (e.g., `SCCACHE_LOCAL_RW_MODE=READ_ONLY`) is automatically skipped during writes, regardless of write policy: ```bash export SCCACHE_MULTILEVEL_CHAIN="disk,redis" export SCCACHE_MULTILEVEL_WRITE_ERROR_POLICY="all" export SCCACHE_LOCAL_RW_MODE="READ_ONLY" # Disk is read-only # Compilation succeeds if Redis write succeeds (disk is skipped) ``` ### Complete Example ```bash # Multi-level configuration export SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3" export SCCACHE_MULTILEVEL_WRITE_ERROR_POLICY="l0" # Default: fail only if disk fails # Level 0: Disk cache export SCCACHE_DIR="/var/cache/sccache" export SCCACHE_CACHE_SIZE="10G" # Level 1: Redis cache export SCCACHE_REDIS_ENDPOINT="redis://localhost:6379" export SCCACHE_REDIS_EXPIRATION="86400" # 24 hours # Level 2: S3 cache export SCCACHE_BUCKET="my-sccache-bucket" export SCCACHE_REGION="us-east-1" export SCCACHE_S3_USE_SSL="true" ``` ### Via Configuration File ```toml # ~/.config/sccache/config [cache.multilevel] chain = ["disk", "redis", "s3"] write_error_policy = "l0" # Optional: ignore, l0 (default), or all [cache.disk] dir = "/var/cache/sccache" size = 10737418240 # 10GB [cache.redis] endpoint = "redis://localhost:6379" expiration = 86400 [cache.s3] bucket = "my-sccache-bucket" endpoint = "s3-us-east-1.amazonaws.com" use_ssl = true ``` ### Single Level (No Multi-Level) If `SCCACHE_MULTILEVEL_CHAIN` is not set, sccache uses the first configured cache backend (legacy behavior): ```bash # Just uses disk (backwards compatible) export SCCACHE_DIR="/tmp/cache" ``` ## Best Practices ### 1. Order Levels by Latency (Fastest First) **Good**: `disk,redis,s3` (10ms → 50ms → 200ms) **Bad**: `s3,disk,redis` (slow L0 blocks every request) ### 2. Match Cache Sizes to Access Patterns - **L0 (disk)**: Small, hot data (5-10GB) - **L1 (redis)**: Team shared, medium (50-100GB) - **L2 (s3)**: Unlimited, cold storage ## See Also - [Configuration Options](Configuration.md) - Full config reference - [Local Cache](Local.md) - Disk cache details - [Redis Cache](Redis.md) - Redis configuration - [S3 Cache](S3.md) - S3 configuration - [Caching](Caching.md) - How cache keys are computed