Make WordPress Core

Changeset 37625


Ignore:
Timestamp:
06/02/2016 06:27:43 PM (8 years ago)
Author:
boonebgorges
Message:

Comments: Improve caching for hierarchical queries.

Hierarchical comment queries work by first fetching the IDs of top-level
comments, and then filling the descendant tree one level at a time based on the
top-level results. When top-level comment IDs are found in the cache,
WP_Comment_Query does not generate the SQL used to fetch these comments. In
this case, the fill_descendants() query does not have enough information
to fill children. As a result, descendant comments were failing to be filled
in cases where the top-level comments were found in the cache.

This was a minor bug previously, because comment caches were not maintained
between pageloads. Since comment caches are now persistent [37613], the problem
becomes evident anywhere that a persistent object cache is in use.

The solution is to cache parent-child relationships, so that when top-level
comments are found in the cache, descendant comments should be found there as
well.

Fixes #36487.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-comment-query.php

    r37539 r37625  
    743743        }
    744744
    745         if ( $this->query_vars['hierarchical'] && ! $this->query_vars['parent'] ) {
    746             $this->query_vars['parent'] = 0;
    747         }
    748 
    749         if ( '' !== $this->query_vars['parent'] ) {
    750             $this->sql_clauses['where']['parent'] = $wpdb->prepare( 'comment_parent = %d', $this->query_vars['parent'] );
     745        $parent = $this->query_vars['parent'];
     746        if ( $this->query_vars['hierarchical'] && ! $parent ) {
     747            $parent = 0;
     748        }
     749
     750        if ( '' !== $parent ) {
     751            $this->sql_clauses['where']['parent'] = $wpdb->prepare( 'comment_parent = %d', $parent );
    751752        }
    752753
     
    942943        }
    943944
     945        $key = md5( serialize( wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ) ) );
     946        $last_changed = wp_cache_get( 'last_changed', 'comment' );
     947        if ( ! $last_changed ) {
     948            $last_changed = microtime();
     949            wp_cache_set( 'last_changed', $last_changed, 'comment' );
     950        }
     951
    944952        // Fetch an entire level of the descendant tree at a time.
    945953        $level = 0;
    946954        do {
    947             $parent_ids = $levels[ $level ];
    948             if ( ! $parent_ids ) {
    949                 break;
    950             }
    951 
    952             $where = 'WHERE ' . $_where . ' AND comment_parent IN (' . implode( ',', array_map( 'intval', $parent_ids ) ) . ')';
    953             $comment_ids = $wpdb->get_col( "{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['groupby']} ORDER BY comment_date_gmt ASC, comment_ID ASC" );
     955            // Parent-child relationships may be cached. Only query for those that are not.
     956            $child_ids = $uncached_parent_ids = array();
     957            $_parent_ids = $levels[ $level ];
     958            foreach ( $_parent_ids as $parent_id ) {
     959                $cache_key = "get_comment_child_ids:$parent_id:$key:$last_changed";
     960                $parent_child_ids = wp_cache_get( $cache_key, 'comment' );
     961                if ( false !== $parent_child_ids ) {
     962                    $child_ids = array_merge( $child_ids, $parent_child_ids );
     963                } else {
     964                    $uncached_parent_ids[] = $parent_id;
     965                }
     966            }
     967
     968            if ( $uncached_parent_ids ) {
     969                $where = 'WHERE ' . $_where . ' AND comment_parent IN (' . implode( ',', array_map( 'intval', $uncached_parent_ids ) ) . ')';
     970                $level_comments = $wpdb->get_results( "SELECT $wpdb->comments.comment_ID, $wpdb->comments.comment_parent {$this->sql_clauses['from']} {$where} {$this->sql_clauses['groupby']} ORDER BY comment_date_gmt ASC, comment_ID ASC" );
     971
     972                // Cache parent-child relationships.
     973                $parent_map = array_fill_keys( $uncached_parent_ids, array() );
     974                foreach ( $level_comments as $level_comment ) {
     975                    $parent_map[ $level_comment->comment_parent ][] = $level_comment->comment_ID;
     976                    $child_ids[] = $level_comment->comment_ID;
     977                }
     978
     979                foreach ( $parent_map as $parent_id => $children ) {
     980                    $cache_key = "get_comment_child_ids:$parent_id:$key:$last_changed";
     981                    wp_cache_set( $cache_key, $children, 'comment' );
     982                }
     983            }
    954984
    955985            $level++;
    956             $levels[ $level ] = $comment_ids;
    957         } while ( $comment_ids );
     986            $levels[ $level ] = $child_ids;
     987        } while ( $child_ids );
    958988
    959989        // Prime comment caches for non-top-level comments.
  • trunk/tests/phpunit/tests/comment/query.php

    r37608 r37625  
    24312431        return $clauses;
    24322432    }
     2433
     2434    /**
     2435     * @ticket 36487
     2436     */
     2437    public function test_cache_should_be_hit_when_querying_descendants() {
     2438        global $wpdb;
     2439
     2440        $p = self::factory()->post->create();
     2441        $comment_1 = self::factory()->comment->create( array(
     2442            'comment_post_ID' => $p,
     2443            'comment_approved' => '1',
     2444        ) );
     2445        $comment_2 = self::factory()->comment->create( array(
     2446            'comment_post_ID' => $p,
     2447            'comment_approved' => '1',
     2448            'comment_parent' => $comment_1,
     2449        ) );
     2450        $comment_3 = self::factory()->comment->create( array(
     2451            'comment_post_ID' => $p,
     2452            'comment_approved' => '1',
     2453            'comment_parent' => $comment_1,
     2454        ) );
     2455        $comment_4 = self::factory()->comment->create( array(
     2456            'comment_post_ID' => $p,
     2457            'comment_approved' => '1',
     2458            'comment_parent' => $comment_2,
     2459        ) );
     2460
     2461        $q1 = new WP_Comment_Query( array(
     2462            'post_id' => $p,
     2463            'hierarchical' => true,
     2464        ) );
     2465        $q1_ids = wp_list_pluck( $q1->comments, 'comment_ID' );
     2466
     2467        $num_queries = $wpdb->num_queries;
     2468        $q2 = new WP_Comment_Query( array(
     2469            'post_id' => $p,
     2470            'hierarchical' => true,
     2471        ) );
     2472        $q2_ids = wp_list_pluck( $q2->comments, 'comment_ID' );
     2473
     2474        $this->assertEqualSets( $q1_ids, $q2_ids );
     2475        $this->assertSame( $num_queries, $wpdb->num_queries );
     2476    }
     2477
    24332478    /**
    24342479     * @ticket 27571
Note: See TracChangeset for help on using the changeset viewer.