Skip to:
Content

BuddyPress.org

Ticket #7436: 7436.2.diff

File 7436.2.diff, 8.5 KB (added by dcavins, 4 years ago)

Add individual friendship caching and a "find uncached friendships" cache warmer function.

  • src/bp-core/bp-core-cache.php

    diff --git a/src/bp-core/bp-core-cache.php b/src/bp-core/bp-core-cache.php
    index bd73b37af..5cf0d9c07 100644
    a b function bp_core_set_incremented_cache( $key, $group, $ids ) { 
    306306        return wp_cache_set( $cache_key, $ids, $group );
    307307}
    308308
     309/**
     310 * Delete a value that has been cached using an incremented key.
     311 *
     312 * A utility function for use by query methods like BP_Activity_Activity::get().
     313 *
     314 * @since 3.0.0
     315 * @see bp_core_set_incremented_cache()
     316 *
     317 * @param string $key   Unique key for the query. Usually a SQL string.
     318 * @param string $group Cache group. Eg 'bp_activity'.
     319 * @return bool True on successful removal, false on failure.
     320 */
     321function bp_core_delete_incremented_cache( $key, $group ) {
     322        $cache_key = bp_core_get_incremented_cache_key( $key, $group );
     323        return wp_cache_delete( $cache_key, $group );
     324}
     325
    309326/**
    310327 * Gets the key to be used when caching a value using an incremented cache key.
    311328 *
  • src/bp-friends/bp-friends-cache.php

    diff --git a/src/bp-friends/bp-friends-cache.php b/src/bp-friends/bp-friends-cache.php
    index 819f81796..67081d11e 100644
    a b function bp_friends_clear_request_cache_on_remove( $friendship_id, BP_Friends_Fr 
    119119add_action( 'friends_friendship_withdrawn', 'bp_friends_clear_request_cache_on_remove', 10, 2 );
    120120add_action( 'friends_friendship_rejected',  'bp_friends_clear_request_cache_on_remove', 10, 2 );
    121121
     122/**
     123 * Delete individual friendships from the cache when they are changed.
     124 *
     125 * @since 3.0.0
     126 *
     127 * @param BP_Friends_Friendship $friendship Friendship object.
     128 */
     129function bp_friends_delete_cached_friendships_on_friendship_save( $friendship ) {
     130        bp_core_delete_incremented_cache( $friendship->friend_user_id . ':' . $friendship->initiator_user_id, 'bp_friends' );
     131        bp_core_delete_incremented_cache( $friendship->initiator_user_id . ':' . $friendship->friend_user_id, 'bp_friends' );
     132}
     133add_action( 'friends_friendship_after_save', 'bp_friends_delete_cached_friendships_on_friendship_save' );
     134
    122135// List actions to clear super cached pages on, if super cache is installed.
    123136add_action( 'friends_friendship_rejected',  'bp_core_clear_cache' );
    124137add_action( 'friends_friendship_accepted',  'bp_core_clear_cache' );
  • src/bp-friends/bp-friends-filters.php

    diff --git a/src/bp-friends/bp-friends-filters.php b/src/bp-friends/bp-friends-filters.php
    index 9513b4760..cd8ffb04e 100644
    a b function bp_friends_filter_user_query_populate_extras( BP_User_Query $user_query 
    3434
    3535        $maybe_friend_ids = wp_parse_id_list( $user_ids_sql );
    3636
     37        // Bulk prepare the friendship cache.
     38        BP_Friends_Friendship::warm_friendship_cache( $user_id, $maybe_friend_ids );
     39
    3740        foreach ( $maybe_friend_ids as $friend_id ) {
    3841                $status = BP_Friends_Friendship::check_is_friend( $user_id, $friend_id );
    3942                $user_query->results[ $friend_id ]->friendship_status = $status;
  • src/bp-friends/classes/class-bp-friends-friendship.php

    diff --git a/src/bp-friends/classes/class-bp-friends-friendship.php b/src/bp-friends/classes/class-bp-friends-friendship.php
    index f683214e5..195b678a8 100644
    a b class BP_Friends_Friendship { 
    603603                        return 'not_friends';
    604604                }
    605605
    606                 /*
    607                  * Find friendships where the possible_friend_userid is the
    608                  * initiator or friend.
    609                  */
    610                 $args = array(
    611                         'initiator_user_id' => $possible_friend_userid,
    612                         'friend_user_id'    => $possible_friend_userid
    613                 );
    614                 $result = self::get_friendships( $initiator_userid, $args, 'OR' );
     606                BP_Friends_Friendship::warm_friendship_cache( $initiator_userid, $possible_friend_userid );
    615607
    616                 if ( $result ) {
    617                         $friendship = current( $result );
    618                         if ( ! $friendship->is_confirmed ) {
    619                                 $status = $initiator_userid == $friendship->initiator_user_id ? 'pending' : 'awaiting_response';
     608                return bp_core_get_incremented_cache( $initiator_userid . ':' . $possible_friend_userid, 'bp_friends' );
     609        }
     610
     611
     612        /**
     613         * Find uncached friendships between a user and one or more other users and cache them.
     614         *
     615         * @since 3.0.0
     616         *
     617         * @param int $user_id                          The ID of the primary user for whom we want
     618         *                                              to check friendships statuses.
     619         * @param int|array|string $possible_friend_ids The IDs of the one or more users
     620         *                                              to check friendship status with primary user.
     621         * @return null
     622         */
     623        public static function warm_friendship_cache( $user_id, $possible_friend_ids ) {
     624                global $wpdb;
     625                $bp = buddypress();
     626                $possible_friend_ids = wp_parse_id_list( $possible_friend_ids );
     627
     628                $fetch = array();
     629                foreach ( $possible_friend_ids as $friend_id ) {
     630                        // Check for cached items in both friendship directions.
     631                        if ( false === bp_core_get_incremented_cache( $user_id . ':' . $friend_id, 'bp_friends' )
     632                                || false === bp_core_get_incremented_cache( $friend_id . ':' . $user_id, 'bp_friends' ) ) {
     633                                $fetch[] = $friend_id;
     634                        }
     635                }
     636                if ( empty( $fetch ) ) {
     637                        return;
     638                }
     639
     640                $friend_ids_sql = implode( ',', array_unique( $fetch ) );
     641                $sql = $wpdb->prepare( "SELECT initiator_user_id, friend_user_id, is_confirmed FROM {$bp->friends->table_name} WHERE (initiator_user_id = %d AND friend_user_id IN ({$friend_ids_sql}) ) OR (initiator_user_id IN ({$friend_ids_sql}) AND friend_user_id = %d )", $user_id, $user_id );
     642                $friendships = $wpdb->get_results( $sql );
     643
     644                // Use $handled to keep track of all of the $possible_friend_ids we've matched.
     645                $handled = array();
     646                foreach ( $friendships as $friendship ) {
     647                        $initiator_user_id = (int) $friendship->initiator_user_id;
     648                        $friend_user_id    = (int) $friendship->friend_user_id;
     649                        if ( 1 === (int) $friendship->is_confirmed ) {
     650                                $status_initiator = $status_friend = 'is_friend';
    620651                        } else {
    621                                 $status = 'is_friend';
     652                                $status_initiator = 'pending';
     653                                $status_friend    = 'awaiting_response';
    622654                        }
    623                 } else {
    624                         $status = 'not_friends';
     655                        bp_core_set_incremented_cache( $initiator_user_id . ':' . $friend_user_id, 'bp_friends', $status_initiator );
     656                        bp_core_set_incremented_cache( $friend_user_id . ':' . $initiator_user_id, 'bp_friends', $status_friend );
     657
     658                        $handled[] = ( $initiator_user_id === $user_id ) ? $friend_user_id : $initiator_user_id;
    625659                }
    626660
    627                 return $status;
     661                // Set all those with no matching entry to "not friends" status.
     662                $not_friends = array_diff( $fetch, $handled );
     663
     664                foreach ( $not_friends as $not_friend_id ) {
     665                        bp_core_set_incremented_cache( $user_id . ':' . $not_friend_id, 'bp_friends', 'not_friends' );
     666                        bp_core_set_incremented_cache( $not_friend_id . ':' . $user_id, 'bp_friends', 'not_friends' );
     667                }
    628668        }
    629669
    630670        /**
  • tests/phpunit/testcases/friends/functions.php

    diff --git a/tests/phpunit/testcases/friends/functions.php b/tests/phpunit/testcases/friends/functions.php
    index 2a9209090..a6f9eaa44 100644
    a b class BP_Tests_Friends_Functions extends BP_UnitTestCase { 
    279279                return $value;
    280280        }
    281281
    282         /**
    283          * @group friendship_caching
    284          */
    285         public function test_friends_check_friendship_should_hit_user_cache() {
    286                 global $wpdb;
    287                 $now = time();
    288                 $u1 = self::factory()->user->create( array(
    289                         'last_activity' => date( 'Y-m-d H:i:s', $now ),
    290                 ) );
    291                 $u2 = self::factory()->user->create( array(
    292                         'last_activity' => date( 'Y-m-d H:i:s', $now - 100 ),
    293                 ) );
    294                 $u3 = self::factory()->user->create( array(
    295                         'last_activity' => date( 'Y-m-d H:i:s', $now - 200 ),
    296                 ) );
    297 
    298                 friends_add_friend( $u1, $u2, true );
    299                 friends_add_friend( $u1, $u3, false );
    300 
    301                 friends_check_friendship_status( $u1, $u2 );
    302                 $first_query_count = $wpdb->num_queries;
    303 
    304                 friends_check_friendship_status( $u1, $u3 );
    305 
    306                 $this->assertEquals( $first_query_count, $wpdb->num_queries );
    307         }
    308 
    309282        /**
    310283         * @group friendship_caching
    311284         */
    class BP_Tests_Friends_Functions extends BP_UnitTestCase { 
    325298                $first_query_count = $wpdb->num_queries;
    326299
    327300                /*
    328                  * We expect this to generate one query to find $u2's friendships,
    329                  * but the friendship object itself should come from cache.
     301                 * This should access the previous friendship check's cached items.
    330302                 */
    331303                friends_check_friendship_status( $u2, $u1 );
    332304
    333                 $this->assertEquals( $first_query_count + 1, $wpdb->num_queries );
     305                $this->assertEquals( $first_query_count, $wpdb->num_queries );
    334306        }
    335307
    336308        public function test_friends_get_recently_active() {