StaleFill deterministically tests cache-aside race conditions by controlling Redis/Valkey command ordering.
It can hold an old cache fill, allow a newer authoritative write and invalidation to finish, then release the old fill and check whether stale data becomes visible again.
Application → StaleFill RESP proxy → Redis / Valkey
↑
HTTP probe runner
An ordinary Redis client needs only its Redis address changed to the proxy address. StaleFill checks specific modeled schedules. It does not prove complete cache consistency.
A reader misses the cache and reads authoritative version V1. Before its old fill reaches Redis, a writer stores V2 and invalidates the key. The delayed fill can then put V1 back into the cache. StaleFill forces this ordering and checks the final cache-backed HTTP response.
Reader: cache miss → read V1 → cache fill held
Writer: write V2 → invalidate cache → HTTP write completes
independent authoritative endpoint confirms V2
Fill: old publication released → publication completes
Read: HTTP response may complete before or after publication
Verify: cache-backed HTTP read returns V1 (FAIL) or V2 (PASS)
Detached background fills can outlive the initiating HTTP response. Association uses one confirmed miss and one compatible publication in an isolated fixture, within the existing scenario timeout. Conflicting evidence remains UNRESOLVED.
Supported fills include GET/MGET with SET/SETEX/PSETEX, hash reads with HSET, and single-key hash transactions held at EXEC. Negative-cache scenarios check whether an old “missing” result survives creation of the record. See architecture and supported behavior for exact command patterns and protocol limits.
Extract the archive for your operating system and architecture. Archives contain the CLI, examples and documentation; Go is needed only for source builds. On Windows, use stalefill.exe.
./stalefill init
# Edit stalefill.json for your application's endpoints and cache key.
./stalefill doctor --config stalefill.json
./stalefill test --config stalefill.json --report stalefill-repro.jsonConfigure your application's Redis client to use the proxy address. Both commands execute the configured application writes, so use a disposable integration environment. Follow the configuration guide when connecting your application. Verify downloaded archives against their SHA256SUMS file.
Requires Go 1.26 or newer, plus a disposable Redis or Valkey instance. No third-party Go dependencies.
go build -o bin/stalefill ./cmd/stalefill
go build -o bin/stalefill-demo ./cmd/demo
./bin/stalefill initStart a disposable Redis (or use your existing local test instance):
docker run --rm --name stalefill-demo-redis \
-p 127.0.0.1:6379:6379 redis:8.10.2-alpine \
redis-server --save '' --appendonly noIn another terminal, start the broken application:
./bin/stalefill-demo --mode broken --redis 127.0.0.1:6380In a third terminal:
./bin/stalefill doctor --config stalefill.json
./bin/stalefill test --config stalefill.json --report broken.json
# Exits 1: FAIL SF001: stale cache resurrectionStop the application and restart it with --mode fixed. Repeat the same test command with --report fixed.json: it exits 0, PASS. prepare resets the fixture before every schedule, so repeated runs need no manual Redis cleanup.
The application must accept HTTP requests without requiring Redis during startup, or retry its initial Redis connection. The proxy listener exists only during doctor and test; no background daemon is installed.
- Configuration: connect an application, select a key and define HTTP observations.
- Commands and results: CLI options, exit codes, findings and diagnostic traces.
- Architecture and supported behavior: scheduling, transaction handling, protocol support and limits.
- Client compatibility: tested Redis clients and reproducible validation.
- gocache upstream regression: deterministic stale publication tests, an experimental fix and real-server evidence.
- Historical gocache v0.2 case study: original external validation and scheduling limitations.
- gocache v0.3 validation: detached publication scheduling against the same unmodified dependency.
- Tool comparison: how controlled command ordering differs from other testing approaches.
- Contributing: source setup, checks and release packaging.
- Changelog: versioned changes.
Licensed under Apache-2.0.