feat(api): Redis read cache for product details and search (stage 0) #29
Reference in New Issue
Block a user
Delete Branch "devin/1782283345-redis-read-cache"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Stage-0 caching from
docs/scalability.md: the read-only API now caches hot product-detail and search-result reads in Redis so repeated requests skip PostgreSQL. It mirrors the existingratelimitpackage — every cache operation fails open, so a missing/unreachable Redis never takes the API down and never serves stale data after a Redis loss.Invalidation is global and O(1) via an epoch counter. Keys are namespaced
og:v<epoch>:...; ingestion bumpsog:cache:epochafter a write run, which logically drops the entire cache at once while old keys age out by TTL. The epoch is read from Redis at most once per 10s per process, so caching adds no extra per-request round trip beyond the value lookup.API (Go)
api/internal/cache:Cache.GetJSON/SetJSON, namespaced byepochNow(). Disabled (always-miss / no-op) when Redis is unconfigured; all ops capped at 150ms and fail open.store.StoregainsWithCache(c); reads become read-through:ProductByGTIN(24h).SearchProductscaches{items,total}keyed by a sha1 of the full filter set + paging window (1h TTL).cmd/server: wiresstore.New(pool).WithCache(cache.New(cfg.RedisURL))(same URL as the limiter).Ingestion (Python)
opengoods/cache.py→bump_cache_epoch(): best-effortINCR og:cache:epoch, never raises (an invalidation failure must not fail a run).update_off,seed_off,import_bypos(loaded), anddedup(merged).schedule.pyinherits it through those jobs.redis>=5.0,<6dependency.Testing
go build/vet,gofmt,go test ./...clean. Newcache_test.go(disabled-fails-open + round-trip + epoch-invalidation) verified green against a real Redis.ruff check,ruff format --check,pytest→ 45 passed / 7 skipped; newtest_cache.pycovers disabled, unreachable-fail-open, and epoch increment.