PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
205: Cache Invalidation Strategies
Caching is the easy part. Putting a value into Redis or Memcached takes about two lines of code. The actual engineering challenge—the part that keeps us up at night—is cache invalidation. As the saying goes, there are only two hard things in Computer Science: cache invalidation and naming things.
I want to show you how this plays out in a real scenario. Let's imagine we're building a "Featured Products" section for an e-commerce site. This list is expensive to generate because it involves several joins and a complex sorting algorithm based on current sales trends. We can't hit the database for this on every single page load.
The "Set it and Forget it" approach
My first instinct, and usually the first move for most developers, is to use a TTL (Time-to-Live). I'll just tell the cache to hold onto the data for an hour. It's simple and requires zero logic in my update scripts.
$cacheKey = 'featured_products_list';
$products = $cache->get($cacheKey);
if (!$products) {
$products = $db->getFeaturedProducts();
// Cache for 3600 seconds (1 hour)
$cache->set($cacheKey, $products, 3600);
}
return $products;
For a while, this works great. The site is snappy, and the database is breathing easy.
The moment it all falls apart
Here is where I messed up. A few weeks into production, the marketing manager tells me that they've just updated the price of a "Door Buster" deal to be 50% off, but the home page is still showing the old price. I check the DB—the price is correct. I check the site—it's wrong.
Of course. The cache is still alive for another 40 minutes. I've created a "stale data" bug. In a high-traffic store, showing an incorrect price for 40 minutes isn't just a glitch; it's a customer service nightmare. I realized that relying solely on TTL is a gamble: you're either caching for too short a time (hurting performance) or too long (hurting data integrity).
Switching to Event-Driven Invalidation
To fix this, I shifted to a "Push" model. Instead of waiting for the cache to expire, I'll tell the cache to die the second a product is updated. I added a hook into the ProductService update method.
class ProductService {
public function updateProduct($productId, $data) {
$this->db->updateProduct($productId, $data);
// Explicitly kill the cache key
$this->cache->delete('featured_products_list');
}
}
This is much better. The moment the price changes, the next visitor triggers a fresh DB hit and repopulates the cache with the correct price. But wait—what if we have other cached lists? What if the product is also in a "Summer Sale" cache and a "Category: Electronics" cache? I can't possibly keep a manual list of every single cache key that might contain this product. That's a recipe for a maintenance disaster.
Solving the scaling problem with Cache Tagging
The professional way to handle this is through tagging. Instead of thinking about individual keys, we group related cache entries under a tag. While not every PHP cache driver supports tags natively, the logic is the same: we associate the 'featured_products_list' with a tag called products.
When I save the cache, I tag it. When I update any product, I invalidate everything associated with that tag.
// Saving with tags
$cache->set('featured_products_list', $products, 3600, ['products']);
$cache->set('electronics_category_list', $electronics, 3600, ['products']);
// In the ProductService update method:
public function updateProduct($productId, $data) {
$this->db->updateProduct($productId, $data);
// This wipes every cache entry tagged as 'products'
$this->cache->invalidateTags(['products']);
}
Now, it doesn't matter if I have one cached list or one thousand. If a product changes, everything that depends on product data is flushed. It's a slightly heavier hammer, but it guarantees that your users never see a price from an hour ago.
📋 Practical Task
Implement a Multi-View Cache Invalidator for User Profiles
You are managing a user profile system. A user's username and avatar_url appear in three different cached locations: the Profile Page, the Global Navigation Header, and the Comment Section of their posts.
Your Task:
- Create a
UserCacheManagerclass that handles the saving of these three different cache keys. - Implement a tagging system (you can simulate this using an array of keys mapped to a tag in a mock cache object) so that all three keys are associated with a tag unique to that user (e.g.,
user_123). - Write a method
updateUserProfile($userId, $newData)that updates the database and then invalidates all cache entries associated with that specific user's tag, ensuring the header and comments are updated simultaneously with the profile page.
There are no comments for now.