Make WordPress Core

Opened 8 months ago

Closed 3 weeks ago

Last modified 2 weeks ago

#63045 closed enhancement (fixed)

Add caching to `count_many_users_posts()`

Reported by: flixos90's profile flixos90 Owned by: peterwilsoncc's profile peterwilsoncc
Milestone: 6.9 Priority: normal
Severity: normal Version:
Component: Users Keywords: has-patch has-unit-tests needs-testing
Focuses: performance Cc:

Description

Follow up to #39242, specifically as raised in https://core.trac.wordpress.org/ticket/39242#comment:39: As we already added caching to count_user_posts(), it makes sense to add similar caching to count_many_users_posts().

cc @spacedmonkey @peterwilsoncc

Attachments (1)

63045-cache-count_many_users_posts.diff (1.2 KB) - added by sachinrajcp123 3 months ago.

Download all attachments as: .zip

Change History (29)

This ticket was mentioned in PR #8444 on WordPress/wordpress-develop by @sukhendu2002.


8 months ago
#1

  • Keywords has-patch added; needs-patch removed

This ticket was mentioned in Slack in #core by jeffpaul. View the logs.


8 months ago

This ticket was mentioned in Slack in #core by jeffpaul. View the logs.


8 months ago

This ticket was mentioned in Slack in #core-performance by adamsilverstein. View the logs.


5 months ago

#5 @adamsilverstein
5 months ago

  • Keywords has-unit-tests added; needs-unit-tests removed

This ticket was mentioned in PR #9106 on WordPress/wordpress-develop by @rollybueno.


4 months ago
#6

Add proper caching support to count_many_users_posts() function

Enhances the performance of count_many_users_posts() function with working cache key generation.

Trac ticket: https://core.trac.wordpress.org/ticket/63045

#7 @rollybueno
4 months ago

I'm not a fan of md5() the query statement so I pushed a new patch version in https://github.com/WordPress/wordpress-develop/pull/9106/ that:

  • More transparent cache keys – avoids md5() and instead uses readable, parameter-based keys (user IDs, post type, $public_only), which makes it easier to debug and trace.
  • Proper separation of cached data – different input combinations get unique keys, reducing the risk of stale or incorrect cache results.
  • Better cache grouping – avoids using a static group, so it's safer and more aligned with how the query behaves.
  • Easier to maintain or extend – the logic is clearer and more flexible for future changes.

Overall, it's a more accurate, debuggable, and scalable approach to caching in count_many_users_posts().

My questions:

  1. ❓ Should we have the OC an expiration? If yes, how long? I have set to 1 hour but it's open for discussions.
  2. ❓ We probably need cache clearing function, but on what actions it should hook into?

This ticket was mentioned in Slack in #core-performance by rollybueno. View the logs.


4 months ago

#9 @rollybueno
4 months ago

  • Keywords 2nd-opinion dev-feedback added

During the bi-weekly coffee-hours, this needs more discussion regarding the caching approach, either using readable cache key or using md5.

Props @flixos90

#10 @spacedmonkey
4 months ago

I strongly believe we should work on #59592 first, as that will effect this ticket's approach.

#11 @sachinrajcp123
3 months ago

Add object caching to count_many_users_posts() to avoid repeated database queries, similar to count_user_posts().

#12 @kalpeshh
2 months ago

  • Keywords needs-refresh added

I checked the PR https://github.com/WordPress/wordpress-develop/pull/9106/files and 63045-cache-count_many_users_posts.diff

The issue with both approaches is different cache_key would be generated if order of array $users is different

$userlist    = implode( ',', array_map( 'absint', $users ) );
$cache_key   = "count_many_users_posts_{$post_type}_{$public_only}_" . str_replace( ',', '_', $userlist ) . '_' . get_current_user_id();

For above code,

$users = [ 1, 2, 3 ] → $userlist = "1,2,3" → _1_2_3_ in the cache key.

$users = [ 3, 2, 1 ] → $userlist = "3,2,1" → _3_2_1_ in the cache key.

To fix this, we should normalize the $users array before building the key:

  • Ensure all IDs are integers.
  • Remove duplicates.
  • Sort the array to guarantee consistent order.

Here is the suggested fix for this,

$users      = array_map( 'absint', (array) $users );
$users      = array_filter( $users );   // remove 0/invalids
$users      = array_unique( $users );   // remove duplicates
sort( $users );                         // enforce consistent order

$userlist   = implode( '_', $users );
$cache_key  = "count_many_users_posts_{$post_type}_{$public_only}_{$userlist}_" . get_current_user_id();

#13 @SirLouen
2 months ago

  • Keywords changes-requested added; needs-refresh removed

Hey @kalpeshh, for your information, needs-refresh means, that the patch is not applying correctly. What I think you were trying to add is changes-requested. I'm adding this for you.

#14 @kalpeshh
2 months ago

Hi @SirLouen yes my bad. Thanks for updating to changes-requested

This ticket was mentioned in Slack in #core-performance by westonruter. View the logs.


2 months ago

#16 @rollybueno
7 weeks ago

Thanks for the feedback @kalpeshh! Updated https://github.com/WordPress/wordpress-develop/pull/9106.

I just noticed, however, that @spacedmonkey has also requested feedback on https://github.com/WordPress/wordpress-develop/pull/8444 , which I only saw after I have updated my branch.

By the way that PR has unit test(still need some polishing) so I' think we should use that instead of mine.

Keeping the existing keywords for https://github.com/WordPress/wordpress-develop/pull/8444

#17 @rollybueno
7 weeks ago

  • Keywords needs-testing added; 2nd-opinion dev-feedback changes-requested removed

Update: @peterwilsoncc and @westonruter have reviewed PR https://github.com/WordPress/wordpress-develop/pull/9106, and I’ve applied their feedback. The changes include:

  1. Switching to md5 for the cache key to handle Memcached’s 250-character limit
  2. Replacing with wp_cache_get_salted()
  3. Adding a dedicated unit test function specifically for this ticket

Marking the ticket as needs testing.

This ticket was mentioned in Slack in #core by wildworks. View the logs.


4 weeks ago

#19 @wildworks
4 weeks ago

This ticket was featured on today's 6.9 Bug Scrub. We need to test the updated PR.

@peterwilsoncc commented on PR #9106:


3 weeks ago
#20

I think the tests could do with a little work to ensure the cache is hit, invalidated correctly and to ensure against public/private collisions.

@rollybueno I'll take the liberty of pushing the changes to your branch as the beta is approaching

@peterwilsoncc commented on PR #9106:


3 weeks ago
#21

@rollybueno I've pushed the tests changes:

The main purpose was to split the tests out in to individual items rather than one big mega test. This is to make it clearer for devs if something starts failing in the future.

For the caching tests, I also added tests for the number of database queries on the second call of the function. This ensures that the cache is hit rather than the database. I did similar for cache invalidation tests, but in those cases a call to the database is expected.

I've also added some tests that equivalent arguments it the cache, eg querying [ posts, pages ] hits the same cache as [ pages, posts ]. For this I discovered a bug that I've pushed a code fix for.

Ignore the failing build processes tests, that's unrelated to this pull request.

@peterwilsoncc commented on PR #9106:


3 weeks ago
#22

Thanks @shail-mehta, I've fixed the ticket references in each of the tests.

#23 @westonruter
3 weeks ago

  • Owner set to peterwilsoncc
  • Status changed from new to reviewing

#24 @peterwilsoncc
3 weeks ago

  • Resolution set to fixed
  • Status changed from reviewing to closed

In 60941:

Users: Add caching to count_many_users_posts().

Introduces object caching to the count_many_users_posts() function.

Argument equivalency is checked prior to generating the cache key to ensure that the same cache is hit regardless of array order for users and post types. For example count_many_users_posts( [ 1, 2 ] ) will hit the same cache as count_many_users_posts( [ 2, 1 ] ).

Props adamsilverstein, flixos90, kalpeshh, rollybueno, sachinrajcp123, shailu25, sirlouen, spacedmonkey, westonruter, wildworks.
Fixes #63045.

#25 @spacedmonkey
3 weeks ago

  • Resolution fixed deleted
  • Status changed from closed to reopened

@peterwilsoncc I would tweak the logic here.

   $where       = get_posts_by_author_sql( $post_type, true, null, $public_only );
   $query       = "SELECT post_author, COUNT(*) FROM $wpdb->posts $where AND post_author IN ($userlist) GROUP BY post_author";
   ...
   if ( false === $count ) {
        $result = $wpdb->get_results( $query, ARRAY_N );

This ticket was mentioned in PR #10336 on WordPress/wordpress-develop by @peterwilsoncc.


3 weeks ago
#26

Removes duplicate logic generating the SQL query from the function.

Follow up to r60941, https://github.com/WordPress/wordpress-develop/commit/bb4d8706d0fea1a836eeed8d097c29368433d017

Trac ticket: https://core.trac.wordpress.org/ticket/63045

#27 @peterwilsoncc
3 weeks ago

  • Resolution set to fixed
  • Status changed from reopened to closed

In 60981:

Users: Remove duplicate query generation from count_many_users_posts().

Removes duplicate code generating the WHERE clause and SQL query from the function.

Follow up to [60941].

Props spacedmonkey.
Fixes #63045.

This ticket was mentioned in Slack in #core by westonruter. View the logs.


2 weeks ago

Note: See TracTickets for help on using tickets.