Skip to:
Content

BuddyPress.org

Changeset 11865


Ignore:
Timestamp:
02/27/2018 01:29:33 PM (4 years ago)
Author:
dcavins
Message:

Change check in BP_Friends_Friendship::check_is_friend().

In BP2.5, we introduced a new friendship caching mechanism that created a new cache item containing all of a user's friendships. This approach worked well on sites with fewer friendships per user, but caused cache access issues on sites with lots of friendship connections.

This update replaces the cache-intensive change to BP_Friends_Friendship::check_is_friend() with a return to a single lookup to determine friendship status between two users. To minimize SQL lookups, individual friendships are now cached, and a new cache-warming function has been added to bulk update these new cache items: BP_Friends_Friendship::update_bp_friends_cache().

Props januzi_pl, boonebgorges, r-a-y, djpaul, dcavins.

Fixes #7436.

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/bp-friends/bp-friends-cache.php

    r11123 r11865  
    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' );
  • trunk/src/bp-friends/bp-friends-filters.php

    r11124 r11865  
    3535    $maybe_friend_ids = wp_parse_id_list( $user_ids_sql );
    3636
     37    // Bulk prepare the friendship cache.
     38    BP_Friends_Friendship::update_bp_friends_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 );
  • trunk/src/bp-friends/classes/class-bp-friends-friendship.php

    r11447 r11865  
    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' );
    615 
    616         if ( $result ) {
    617             $friendship = current( $result );
    618             if ( ! $friendship->is_confirmed ) {
    619                 $status = $initiator_userid == $friendship->initiator_user_id ? 'pending' : 'awaiting_response';
     606        BP_Friends_Friendship::update_bp_friends_cache( $initiator_userid, $possible_friend_userid );
     607
     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 update_bp_friends_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';
    622             }
    623         } else {
    624             $status = 'not_friends';
    625         }
    626 
    627         return $status;
     652                $status_initiator = 'pending';
     653                $status_friend    = 'awaiting_response';
     654            }
     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;
     659        }
     660
     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
  • trunk/tests/phpunit/testcases/friends/functions.php

    r11737 r11865  
    283283     * @group friendship_caching
    284284     */
    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 
    309     /**
    310      * @group friendship_caching
    311      */
    312285    public function test_friends_check_friendship_should_hit_friendship_object_cache() {
    313286        global $wpdb;
     
    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
Note: See TracChangeset for help on using the changeset viewer.