May 19, 2026·Karlo Hrvačić

Caching Strategy for a URL Shortener: TTL, Warmup, Priming, and Graceful Degradation

A deep dive into our Redis caching architecture — how we keep 99% of redirects cache-hot, validate stale data, and survive Redis outages.

engineering
caching
redis
performance

Every redirect at hrva.cc goes through Redis first. The database is the fallback, not the primary path. Here's how we designed our caching layer to be fast, correct, and resilient.

Why Cache at All

The redirect endpoint (GET /{short}) is the hottest path in the system — every click on every short URL hits it. Without caching, every redirect requires two database queries: one to check if the URL exists and is active, and another to read the destination URL. With caching, a single Redis lookup replaces both queries. The database is only touched when the cache is cold or the cached data is stale.

Cache Layout

We use a single Redis cache named urls. Each entry maps a short URL code to a serialized UrlResponse object containing the destination URL, active status, expiration date, visit count, and visit limit. The cache is configured with a 7-day TTL — entries expire after a week regardless of access.

Validation on Every Cache Hit

A long TTL means entries can become stale. Every cache hit runs isUrlRedirectValid(), which checks three things from the cached data:

  • Is the URL still active? (wasn't deactivated by owner or scheduler)
  • Has the expiration date passed?
  • Has the visit limit been reached?

If any check fails, the entry is evicted from cache and the request falls through to the database. This gives us the performance of a 7-day TTL with the correctness of real-time validation.

Cache Warmup

When the application starts, the CacheWarmup component loads the 20 most visited URLs, the 20 most recently created, and the 20 most recently accessed into Redis. This runs asynchronously after startup, so the most popular links are cache-hot from the beginning.

Cache Priming

New URLs are cached immediately at creation time. When a user shortens a URL, the response is written to Redis before the HTTP response is sent. The first visitor gets a cache hit.

Eviction Strategy

When a URL is deactivated, activated, updated, or deleted, the corresponding cache entry is evicted immediately. Deactivation and activation evict by key, delete evicts all entries (it's a rare operation). The scheduled URL deactivation task (runs every minute) does not evict cache — expired URLs fail validation on cache hit, so the database is only queried when necessary.

Graceful Degradation

Redis is not a hard dependency. A custom CacheErrorHandler catches every Redis failure — get, put, evict, clear — logs a warning, and returns null. The application falls through to the database without throwing an error. During a Redis outage, redirects are slightly slower (two database queries instead of one cache lookup), but the site stays up.

The admin dashboard tracks redirect timing separately for cache hits and cache misses via Micrometer timers, so we can monitor cache effectiveness in real-time.