Skip to content

Commit 8261038

Browse files
mmastracclaude
andcommitted
feat: multi-level caching with automatic backfill
Cherry-picked from upstream PR mozilla#2581. Adds hierarchical cache storage (e.g., disk -> redis -> s3) with configurable write tolerance policies and automatic backfill from slower tiers to faster ones. Upstream: mozilla#2581 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a1236d0 commit 8261038

18 files changed

Lines changed: 3649 additions & 37 deletions

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
sccache - Shared Compilation Cache
1111
==================================
1212

13-
sccache is a [ccache](https://ccache.dev/)-like compiler caching tool. It is used as a compiler wrapper and avoids compilation when possible, storing cached results either on [local disk](docs/Local.md) or in one of [several cloud storage backends](#storage-options).
13+
sccache is a [ccache](https://ccache.dev/)-like compiler caching tool. It is used as a compiler wrapper and avoids compilation when possible, storing cached results either on [local disk](docs/Local.md) or in one of [several cloud storage backends](#storage-options). Multi-level caching with automatic backfill is supported for hierarchical cache architectures (see [Multi-Level Cache](docs/MultiLevel.md)).
1414

1515
sccache includes support for caching the compilation of Assembler, C/C++ code, [Rust](docs/Rust.md), as well as NVIDIA's CUDA using [nvcc](https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html), and [clang](https://llvm.org/docs/CompileCudaWithLLVM.html), [AMD's ROCm HIP](https://rocm.docs.amd.com/projects/HIP/en/latest/index.html).
1616

@@ -33,6 +33,7 @@ Table of Contents (ToC)
3333
* [Interaction with GNU `make` jobserver](#interaction-with-gnu-make-jobserver)
3434
* [Known Caveats](#known-caveats)
3535
* [Storage Options](#storage-options)
36+
* [Multi-Level (Hierarchical Caching)](docs/MultiLevel.md)
3637
* [Local](docs/Local.md)
3738
* [S3](docs/S3.md)
3839
* [R2](docs/S3.md#R2)

docs/Configuration.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ cache_dir = "/home/user/.cache/sccache-dist-client"
3939
type = "token"
4040
token = "secrettoken"
4141

42+
# Multi-level cache configuration
43+
# Define cache levels in order (fast to slow).
44+
# Each level must be separately configured below.
45+
# See docs/MultiLevel.md for details.
46+
[cache.multilevel]
47+
chain = ["disk", "redis", "s3"]
48+
write_policy = "l0" # Optional: ignore, l0 (default), or all
4249

4350
#[cache.azure]
4451
# Azure Storage connection string (see <https://docs.azure.cn/en-us/storage/common/storage-configure-connection-string>)
@@ -176,6 +183,41 @@ Note that some env variables may need sccache server restart to take effect.
176183

177184
### cache configs
178185

186+
#### multi-level cache
187+
188+
Multi-level caching enables hierarchical cache storage with automatic backfill. See the [Multi-Level Cache documentation](MultiLevel.md) for detailed information.
189+
190+
* `SCCACHE_MULTILEVEL_CHAIN` comma-separated list of cache backend names to use in hierarchy (e.g., `disk,redis,s3`)
191+
- Order matters: left-to-right is fast-to-slow (L0, L1, L2, ...)
192+
- Valid names: `disk`, `redis`, `memcached`, `s3`, `gcs`, `azure`, `gha`, `webdav`, `oss`, `cos`
193+
- Each level must be separately configured with its own environment variables
194+
- If not set, sccache uses single-level mode (legacy behavior)
195+
* `SCCACHE_MULTILEVEL_WRITE_POLICY` controls error handling on cache writes (default: `l0`)
196+
- `ignore` - never fail on write errors, log warnings only (most permissive)
197+
- `l0` - fail only if L0 (first level) write fails (default, balances reliability and performance)
198+
- `all` - fail if any read-write level fails (most strict)
199+
- Read-only levels are always skipped and never cause failures
200+
201+
**Basic example**:
202+
```bash
203+
export SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3"
204+
export SCCACHE_DIR="/tmp/cache" # for disk level
205+
export SCCACHE_REDIS_ENDPOINT="redis://..." # for redis level
206+
export SCCACHE_BUCKET="my-bucket" # for s3 level
207+
```
208+
209+
**Write policy examples**:
210+
```bash
211+
# Default: Fail only if disk write fails
212+
export SCCACHE_MULTILEVEL_WRITE_POLICY="l0"
213+
214+
# Best effort: Never fail on cache writes
215+
export SCCACHE_MULTILEVEL_WRITE_POLICY="ignore"
216+
217+
# Strict: Fail if any level write fails
218+
export SCCACHE_MULTILEVEL_WRITE_POLICY="all"
219+
```
220+
179221
#### disk (local)
180222

181223
* `SCCACHE_DIR` local on disk artifact cache directory

docs/MultiLevel.md

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
# Multi-Level Cache
2+
3+
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.
4+
5+
## Table of Contents
6+
7+
- [Overview](#overview)
8+
- [Architecture](#architecture)
9+
- [Use Cases](#use-cases)
10+
- [Configuration](#configuration)
11+
- [Best Practices](#best-practices)
12+
13+
## Overview
14+
15+
Multi-level caching allows you to configure multiple cache storage backends that work together:
16+
17+
- **Fast, small caches** (e.g., local disk) are checked first
18+
- **Slower, larger caches** (e.g., S3) are checked if earlier levels miss
19+
- **Cache hits at any level** return immediately to the compiler
20+
- **Automatic backfill** copies data from slower to faster levels for future requests
21+
- **Write-through** ensures all levels stay synchronized on writes
22+
23+
This creates a cache hierarchy where frequently accessed artifacts stay in fast storage while less common ones are still available from slower storage.
24+
25+
## Architecture
26+
27+
### Cache Hierarchy
28+
29+
```
30+
┌─────────────────────────────────────────────────┐
31+
│ Compiler Request │
32+
└─────────────────────┬───────────────────────────┘
33+
34+
┌────────────▼────────────┐
35+
│ Multi-Level Storage │
36+
└────────────┬────────────┘
37+
38+
┌───────────────┼───────────────┐
39+
│ │ │
40+
┌─────▼─────┐ ┌────▼────┐ ┌────▼────┐
41+
│ Level 0 │ │ Level 1 │ │ Level 2 │
42+
│ (Disk) │ │ (Redis) │ │ (S3) │
43+
│ │ │ │ │ │
44+
│ Fast │ │ Medium │ │ Slow │
45+
│ Small │ │ Medium │ │ Large │
46+
│ ~5ms │ │ ~10ms │ │ ~200ms │
47+
└───────────┘ └─────────┘ └─────────┘
48+
```
49+
50+
### Read Path (Cache Hit at Level 2)
51+
52+
```
53+
1. Check L0 (disk) → Miss (5ms)
54+
2. Check L1 (redis) → Miss (10ms)
55+
3. Check L2 (s3) → Hit! (200ms)
56+
4. Return to compiler wrapper / sccache (Total: 215ms)
57+
5. Background: Backfill L2→L1 (async, non-blocking)
58+
6. Background: Backfill L2→L0 (async, non-blocking)
59+
7. Next request: Check L0 → Hit! (10ms)
60+
```
61+
62+
### Write Path
63+
64+
All write operations go to **all configured levels** in parallel:
65+
66+
```
67+
Compiler writes artifact
68+
├─> L0 (disk) ✓
69+
├─> L1 (redis) ✓
70+
└─> L2 (s3) ✓
71+
```
72+
73+
If any level fails, the error is logged but the write succeeds if at least one level accepts it.
74+
75+
## Use Cases
76+
77+
### 1. CI/CD with Shared Team Cache
78+
79+
**Problem**: Each CI runner has isolated disk cache, no sharing across machines.
80+
81+
**Solution**: Add Redis or Memcached as L1
82+
```bash
83+
SCCACHE_MULTILEVEL_CHAIN="disk,redis"
84+
SCCACHE_DIR="/tmp/sccache"
85+
SCCACHE_REDIS_ENDPOINT="redis://cache.internal:6379"
86+
```
87+
88+
**Result**: Fast local hits when available, team-shared cache otherwise.
89+
90+
### 2. Enterprise with CDN-like Architecture
91+
92+
**Problem**: Global team with high S3 latency, want local speed.
93+
94+
**Solution**: Multi-tier hierarchy
95+
```bash
96+
SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3"
97+
```
98+
99+
- L0: Local disk (instant)
100+
- L1: Regional Redis (5-10ms)
101+
- L2: Global S3 bucket (50-200ms)
102+
103+
**Result**: 90%+ hits at L0/L1, L2 as long-term backup.
104+
105+
### 3. Developer Workstation with Cloud Backup
106+
107+
**Problem**: Local disk fills up, don't want to lose cache history.
108+
109+
**Solution**: Disk + cloud storage
110+
```bash
111+
SCCACHE_MULTILEVEL_CHAIN="disk,s3"
112+
SCCACHE_DIR="$HOME/.cache/sccache"
113+
SCCACHE_BUCKET="my-personal-sccache"
114+
SCCACHE_CACHE_SIZE="5G" # Keep disk small
115+
```
116+
117+
**Result**: Unlimited cloud storage, fast local hits.
118+
119+
## Configuration
120+
121+
### Via Environment Variables
122+
123+
The primary configuration is `SCCACHE_MULTILEVEL_CHAIN`:
124+
125+
```bash
126+
export SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3"
127+
```
128+
129+
**Format**: Comma-separated list of cache backend names
130+
**Order**: Left-to-right is fast-to-slow (L0, L1, L2, ...)
131+
**Valid names**: `disk`, `redis`, `memcached`, `s3`, `gcs`, `azure`, `gha`, `webdav`, `oss`, `cos`
132+
133+
### Write Policy Configuration
134+
135+
Control how sccache handles write failures across cache levels using `SCCACHE_MULTILEVEL_WRITE_POLICY`:
136+
137+
**Available policies**:
138+
- **`ignore`** - Never fail on write errors, log warnings only (most permissive)
139+
- **`l0`** - Fail only if L0 (first level) write fails (default - balances reliability and performance)
140+
- **`all`** - Fail if any read-write level write fails (most strict)
141+
142+
**Note**: Read-only levels are always skipped during writes and never cause failures.
143+
144+
#### Write Policy Examples
145+
146+
**Example 1: Default Behavior (l0 policy)**
147+
```bash
148+
export SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3"
149+
export SCCACHE_MULTILEVEL_WRITE_POLICY="l0" # or omit, it's the default
150+
```
151+
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.**
152+
153+
**Example 2: Best Effort (ignore policy)**
154+
```bash
155+
export SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3"
156+
export SCCACHE_MULTILEVEL_WRITE_POLICY="ignore"
157+
```
158+
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.
159+
160+
**Example 3: Strict Consistency (all policy)**
161+
```bash
162+
export SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3"
163+
export SCCACHE_MULTILEVEL_WRITE_POLICY="all"
164+
```
165+
Compilation succeeds only if all read-write levels succeed. Any write failure fails the compilation. **Best for critical environments** where cache consistency is mandatory.
166+
167+
#### Read-Only Levels
168+
169+
Any level configured as read-only (e.g., `SCCACHE_LOCAL_RW_MODE=READ_ONLY`) is automatically skipped during writes, regardless of write policy:
170+
171+
```bash
172+
export SCCACHE_MULTILEVEL_CHAIN="disk,redis"
173+
export SCCACHE_MULTILEVEL_WRITE_POLICY="all"
174+
export SCCACHE_LOCAL_RW_MODE="READ_ONLY" # Disk is read-only
175+
# Compilation succeeds if Redis write succeeds (disk is skipped)
176+
```
177+
178+
### Complete Example
179+
180+
```bash
181+
# Multi-level configuration
182+
export SCCACHE_MULTILEVEL_CHAIN="disk,redis,s3"
183+
export SCCACHE_MULTILEVEL_WRITE_POLICY="l0" # Default: fail only if disk fails
184+
185+
# Level 0: Disk cache
186+
export SCCACHE_DIR="/var/cache/sccache"
187+
export SCCACHE_CACHE_SIZE="10G"
188+
189+
# Level 1: Redis cache
190+
export SCCACHE_REDIS_ENDPOINT="redis://localhost:6379"
191+
export SCCACHE_REDIS_EXPIRATION="86400" # 24 hours
192+
193+
# Level 2: S3 cache
194+
export SCCACHE_BUCKET="my-sccache-bucket"
195+
export SCCACHE_REGION="us-east-1"
196+
export SCCACHE_S3_USE_SSL="true"
197+
```
198+
199+
### Via Configuration File
200+
201+
```toml
202+
# ~/.config/sccache/config
203+
[cache.multilevel]
204+
chain = ["disk", "redis", "s3"]
205+
write_policy = "l0" # Optional: ignore, l0 (default), or all
206+
207+
[cache.disk]
208+
dir = "/var/cache/sccache"
209+
size = 10737418240 # 10GB
210+
211+
[cache.redis]
212+
endpoint = "redis://localhost:6379"
213+
expiration = 86400
214+
215+
[cache.s3]
216+
bucket = "my-sccache-bucket"
217+
endpoint = "s3-us-east-1.amazonaws.com"
218+
use_ssl = true
219+
```
220+
221+
### Single Level (No Multi-Level)
222+
223+
If `SCCACHE_MULTILEVEL_CHAIN` is not set, sccache uses the first configured cache backend (legacy behavior):
224+
225+
```bash
226+
# Just uses disk (backwards compatible)
227+
export SCCACHE_DIR="/tmp/cache"
228+
```
229+
230+
## Best Practices
231+
232+
### 1. Order Levels by Latency (Fastest First)
233+
234+
**Good**: `disk,redis,s3` (10ms → 50ms → 200ms)
235+
**Bad**: `s3,disk,redis` (slow L0 blocks every request)
236+
237+
### 2. Match Cache Sizes to Access Patterns
238+
239+
- **L0 (disk)**: Small, hot data (5-10GB)
240+
- **L1 (redis)**: Team shared, medium (50-100GB)
241+
- **L2 (s3)**: Unlimited, cold storage
242+
243+
## See Also
244+
245+
- [Configuration Options](Configuration.md) - Full config reference
246+
- [Local Cache](Local.md) - Disk cache details
247+
- [Redis Cache](Redis.md) - Redis configuration
248+
- [S3 Cache](S3.md) - S3 configuration
249+
- [Caching](Caching.md) - How cache keys are computed

0 commit comments

Comments
 (0)