Make WordPress Core

Opened 4 weeks ago

Last modified 4 weeks ago

#63284 reviewing defect (bug)

_prime_post_caches is getting called unchunked

Reported by: ckeeney's profile ckeeney Owned by: joemcgill's profile joemcgill
Milestone: Awaiting Review Priority: normal
Severity: major Version: 6.7.2
Component: General Keywords:
Focuses: Cc:

Description

I setup a local environment where I can reliably reproduce this issue. The local environment has just two plugins active: Query Monitor and Redis Object Cache. I also stripped away all of my functions.php except for registering my custom post types. This environment has 16,000+ "Article" post types.

Steps to reproduce:

  1. Navigate to the Article index on wp-admin (/wp-admin/edit.php?post_type=article)
  2. Open Query Monitor -> Database Queries -> Slow Queries
  3. Observe query for SELECT wp_posts.* FROM wp_posts WHERE ID IN (~16,000 individually listed IDs here)
  4. If you do not see the giant query on Step 3, use the Object Cache-> Flush Cache tool provided by the Redis Object Cache toolbar at the top of the page.
  5. Refresh the page and observe the query described in step 3.

https://i.imgur.com/qZUpt9S.png

Change History (13)

#1 @ckeeney
4 weeks ago

FWIW, this environment is Roots Bedrock running on Docker.

I originally thought this issue was related to Redis Object Cache, but that was only because I first noticed this issue in the Redis SLOWLOG with giant MGET queries.

I opened a support ticket on the Redis Object Cache plugin forum, but now I am no longer sure Redis Object Cache is related other than that it provides the easy access to flush the cache.

The related ticket on Redis Object Cache is at https://wordpress.org/support/topic/help-tracing-slow-redis-queries

#2 @joemcgill
4 weeks ago

  • Keywords reporter-feedback added
  • Owner set to joemcgill
  • Status changed from new to reviewing

Thanks for the report, @ckeeney. Just to confirm, you were experiencing this issue on WP 6.7.2, correct? Have you tried reproducing on the latest version WP 6.8 which was released earlier today?

Can you tell us more about your current setup? It looks from your screenshot like this is related to a custom 'articles' post type so there may be code in a site plugin or the theme that is altering the way these post types are being queried for the list table.

#3 @ckeeney
4 weeks ago

I am able to reproduce this on 6.7.2 and 6.8.0.

There are no other plugins running besides Query Monitor and Redis Object Cache.

We use WordPress as a headless CMS, so I only really care about wp-admin, wp-json, and the RSS feeds, so our theme code is pretty thin. I am running a custom theme that is a child theme of Twenty Seventeen, but I have stripped all the code from functions.php except for the code that registers a few custom post types.

There is not much more I can slim down in this reproduction case.

#4 @ckeeney
4 weeks ago

I am able to reproduce this on a clean WordPress docker setup. I created a new repository to demonstrate this issue, but you probably will have to play with permissions on the volumes folder to make the docker containers run properly, and it is probably easier to just start from my docker-compose.yml and no data.

Here are the steps I used to reproduce this issue with a base WordPress install:

  1. Install 3 plugins: Query Monitor, Advanced Custom Fields (just to create a CPT), and Redis Object Cache (maybe not required, but I have not reproduced this bug without it yet).
  2. Create a CPT and make sure to set both hierarchical and supports page attributes to true.
  3. In the container, install wp-cli and generate a bunch of posts for the CPT you created:
wp --allow-root post generate --count=1000 --post_type=my-cpt
  1. Go view those posts in the admin panel and open the query monitor panel for database queries. If the cache was already warm, you will have to click the Object Cache -> Flush Cache button at the top of the page and reload.
  2. Observe the big database query.

https://i.imgur.com/QIQASIW.png

The issue presents itself when viewing the WP Admin index for a custom post type that was registered with both hierarchical=true AND supports.page-attributes.

If I remove either one of these flags when registering the CPT, the issue goes away.

Here is my repo.
https://github.com/ckeeney/blank-wordpress

The username and password I created in the included MySQL data is ckeeney / 987987

#5 follow-up: @tillkruess
4 weeks ago

@joemcgill: I guess _prime_post_caches() is being called with >16,000 IDs. Can we chunk that maybe?

#6 in reply to: ↑ 5 @joemcgill
4 weeks ago

Replying to tillkruess:

@joemcgill: I guess _prime_post_caches() is being called with >16,000 IDs. Can we chunk that maybe?

Adding some batching logic is possible, but I first want to see if we can determine why that many IDs are being passed to _prime_post_caches() in the first place, as that in itself is surprising.

#7 @ckeeney
4 weeks ago

  • Keywords reporter-feedback removed

Let me know if I can provide any more information.

#8 follow-up: @joemcgill
4 weeks ago

@ckeeney I've followed the steps you've outlined with a few noted exceptions and am not able to reproduce this currently with a regular Core set up.

  1. I've started up a fresh site, using @wordpress/env running WP 6.8.
  2. I've installed Query Monitor and Redis Object Cache (Note: Redis is not enabled in the environment)
  3. I've activated the Twenty Seventeen theme on the site.
  4. I've registered a custom post type that is hierarchical and supports 'page-attributes'. (Note: I've registered this post type in a custom mu-plugin rathe than via ACF.)
  5. I've generated 1000 posts for that post type via CLI
  6. When visiting the list table for the post type, _prime_post_caches() is only being called with 20 IDs (the number being displayed in the list table).

Two main differences I see:

  1. I'm not using ACF to register the post type. @ckeeney Could you try registering a custom post type using code directly in the theme or an mu-plugin without ACF?
  1. Redis Object Cache is active but the drop in is not installed, since no Redis connection could be established. @tillkruess any chance the plugin is filtering something about the queries on this page? I can try reproducing with an active Redis connection, but wanted to eliminate the obvious things first.

Any other ideas welcome

#9 @ckeeney
4 weeks ago

I am only able to reproduce this with an active Redis connection. Sorry, I did leave off the step to setup an active Redis connection in my instructions.

I have reproduced this issue without ACF, but not in the public repo.

#10 in reply to: ↑ 8 @tillkruess
4 weeks ago

Replying to joemcgill:

  1. Redis Object Cache is active but the drop in is not installed, since no Redis connection could be established. @tillkruess any chance the plugin is filtering something about the queries on this page? I can try reproducing with an active Redis connection, but wanted to eliminate the obvious things first.

No, Redis Object Cache won't touch the query or filter anything relevant, only provide the wp_cache_*() functions to be called.

#11 @ckeeney
4 weeks ago

I did some more testing:

  1. I loaded up some test data with no persistent object cache plugin configured, cleared the (MySQL?) transients using the wp-cli, and refreshed the CPT index page. I was not able to reproduce this error with this configuration.
  2. I installed and activated the SQLite Object Cache and repeated my test and was able to reproduce this error. However, using the wp cli to clear the transients was insufficient for the SQLite cache. I had to navigate to the plugin's settings and clear the from that page by checking the "Flush now".
  3. I am able to reproduce this using the default Page post type, which is expected because that post type has hierarchical and page-attributes by default.

It seems the requirements for this are just:

  • any post type that has both hierarchical and page-attributes
  • using a persistent object cache

#12 @peterwilsoncc
4 weeks ago

I'm able to reproduce this on a standard WP install on the pages list:

  1. Install Yoast WPCLI faker
  2. Run wp faker core content --posts=0 --pages=1000
  3. Run wp cache flush
  4. Visit wp-admin/edit.php?post_type=page

On my local, the database call returned 981 rows.

Relevant environment info:

  • Memcached
  • Plugins: Query Monitor, QM Path mappings, custom plugin top show cache config in admin bar
  • WP trunk @ r60166
  • PHP 8.3
  • Dropins: db (Query Monitor's), object-cache, advanced-cache

#13 @peterwilsoncc
4 weeks ago

It looks like the difference is from a clause in WP_Query that determines whether the query should be split, ie whether WP should query the post IDs only before calling _prime_post_caches()

Without an object cache, WP_Query doesn't split the query so queries all the post objects (in my example above, 1000 posts). WP_Query then primes the cache with this data directly via update_post_caches() (code ref).

With an object cache, WP_Query does split the query and subsequently calls _prime_post_caches().

So in both cases, a large number of posts are queried and primed but it happens using a slightly different code path.

Note: See TracTickets for help on using tickets.