Master WordPress performance by implementing database query memoization. Learn to eliminate redundant REST API calls and scale your plugins efficiently.
Last week, I spent about two days debugging a REST API endpoint that was triggering over 400 SQL queries for a single request. The culprit wasn't bad indexing, but a classic N+1 problem buried deep within a nested data transformation layer. While we often rely on persistent object caches like Redis, sometimes you just need to keep things local to the request lifecycle to avoid unnecessary network round-trips.
If you’re building high-traffic plugins, you already know that WordPress performance is fragile. When you're dealing with complex REST API hydration, every millisecond counts.
We initially tried using wp_cache_set with a short expiration, but that introduced race conditions and overhead that weren't worth the trouble. By switching to a request-scoped memoization pattern, we kept the data in memory only for the duration of the current PHP process. It’s faster, simpler, and keeps your Redis instance clean from transient keys that only exist for a few milliseconds.
This approach is particularly effective when you have multiple service classes that might independently request the same resource metadata. Instead of hitting the database or the persistent cache, they check a static variable in your class.
I’ve settled on a simple static array pattern within my service classes. It’s lightweight and plays nicely with WordPress object caching optimization: A guide for senior engineers.
Here is a skeletal example of how I handle this in production:
PHPclass ProductHydrator { private static $memo = []; public function get_product_meta(int $product_id) { if (isset(self::$memo[$product_id])) { return self::$memo[$product_id]; } #6A9955">// Simulate a heavy DB query $meta = get_post_meta($product_id, '_extended_data', true); self::$memo[$product_id] = $meta; return $meta; } }
This ensures that no matter how many times your API logic asks for the same product meta, the database is touched exactly once.
When you're dealing with massive payloads, simple memoization isn't enough. You have to consider PHP memory management as well. If you cache every single object you touch in a loop, you’ll hit your memory_limit before the response is even serialized.
I’ve found that combining this memoization pattern with WordPress performance: Database-level request coalescing for REST API creates a robust safety net. If the memoization layer doesn't have the data, the coalescing layer ensures we aren't firing identical concurrent queries.
The biggest danger here is "memory creep." If you’re processing a collection of 500 items, don't store the full objects in your static cache. Store only the specific fields you need for the response.
If you find your memory usage climbing, you might need to look into WordPress performance: Streaming Large REST API Exports to offload the hydration process. Don't try to solve every performance issue with a giant cache; sometimes, the architecture of your data fetching logic is the real bottleneck.
Don't use this for data that changes within the same request. If you have a hook that modifies post metadata after your hydrator has already cached it, your REST API response will be stale. I’ve been burned by this more times than I care to admit. Always ensure that if your data is mutable, you either clear the static cache or implement a "write-through" logic that updates the memoized value immediately upon save.
Does this replace Redis/Memcached? No. This is for request-scoped optimization. It complements persistent caching by reducing the number of times you even have to ask the persistent cache for data.
How do I handle cache invalidation? Since this is request-scoped, it dies when the process ends. You don't need to worry about invalidation unless your code modifies the data within the same request.
Does this impact PHP memory limits? Yes. If you cache large objects, you will see memory usage rise. Keep your memoized data lean and only store what is strictly necessary for the current request context.
I’m still experimenting with using WeakMap in PHP 8+ for these caches, which allows for better memory cleanup when objects are destroyed. It’s a cleaner way to handle database query memoization without the risk of stale data hanging around in long-running processes like WP-CLI or background workers. If you're on a modern stack, it's worth the extra effort to implement.
Master WordPress REST API cache invalidation using a dependency graph. Learn to track resource relationships for granular purging and improved headless performance.
Read moreMaster WordPress performance by stopping cache stampedes. Learn how to implement request coalescing in your REST API to handle high concurrency with ease.