Caching Makes Apps Fast
But invalidation makes developers cry.
Cache-Aside (Lazy Loading)
Most common pattern:
function getUser($id) {
$cached = Cache::get("user:{$id}");
if ($cached) return $cached;
$user = User::find($id);
Cache::put("user:{$id}", $user, 3600);
return $user;
}
Write-Through
Update cache when data changes:
function updateUser($id, $data) {
$user = User::find($id);
$user->update($data);
Cache::put("user:{$id}", $user, 3600);
return $user;
}
Cache Invalidation
The hard part:
// Time-based (TTL)
Cache::put('key', $value, 3600); // Expires in 1 hour
// Event-based
class User extends Model {
protected static function booted() {
static::saved(fn($user) => Cache::forget("user:{$user->id}"));
}
}
// Tag-based
Cache::tags(['users'])->put("user:{$id}", $user);
Cache::tags(['users'])->flush(); // Clear all user caches
What to Cache
// Database queries (especially aggregates)
Cache::remember('stats:users', 3600, fn() => User::count());
// External API responses
Cache::remember("weather:{$city}", 1800, fn() => WeatherApi::get($city));
// Computed/rendered content
Cache::remember("post:{$id}:html", 86400, fn() => markdown($post->content));
Cache Stampede
When cache expires, many requests hit the database:
// Solution: Lock while regenerating
Cache::lock("user:{$id}:lock")->get(function () use ($id) {
$user = User::find($id);
Cache::put("user:{$id}", $user, 3600);
});
TTL Guidelines
- User sessions: 1-24 hours
- API responses: 5-60 minutes
- Computed stats: 1-24 hours
- Static content: 1+ days
