Make WordPress Core

Changeset 58134


Ignore:
Timestamp:
05/11/2024 11:51:55 PM (7 months ago)
Author:
peterwilsoncc
Message:

Options, Meta APIs: Prime transient options prior to use.

Reduce the number of queries getting and setting transients on sites without a persistent cache.

Transients are stored in two options: one each for the transient value and timeout. Priming the cache reduces the database queries for getting a transient from two to one.

Props peterwilsoncc, swissspidy.
Fixes #61193.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/option.php

    r58105 r58134  
    13311331            if ( ! isset( $alloptions[ $transient_option ] ) ) {
    13321332                $transient_timeout = '_transient_timeout_' . $transient;
    1333                 $timeout           = get_option( $transient_timeout );
     1333                wp_prime_option_caches( array( $transient_option, $transient_timeout ) );
     1334                $timeout = get_option( $transient_timeout );
    13341335                if ( false !== $timeout && $timeout < time() ) {
    13351336                    delete_option( $transient_option );
     
    14111412        $transient_timeout = '_transient_timeout_' . $transient;
    14121413        $transient_option  = '_transient_' . $transient;
     1414        wp_prime_option_caches( array( $transient_option, $transient_timeout ) );
    14131415
    14141416        if ( false === get_option( $transient_option ) ) {
  • trunk/tests/phpunit/tests/option/transient.php

    r54668 r58134  
    7979        update_option( '_transient_timeout_' . $key, $now - 1 );
    8080        $this->assertFalse( get_transient( $key ) );
     81    }
     82
     83    /**
     84     * Ensure get_transient() makes a single database request.
     85     *
     86     * @ticket 61193
     87     *
     88     * @covers ::get_transient
     89     */
     90    public function test_get_transient_with_timeout_makes_a_single_database_call() {
     91        global $wpdb;
     92        $key                        = 'test_transient';
     93        $value                      = 'test_value';
     94        $timeout                    = 100;
     95        $expected_query             = "SELECT option_name, option_value FROM $wpdb->options WHERE option_name IN ('_transient_{$key}','_transient_timeout_{$key}')";
     96        $unexpected_query_transient = "SELECT option_value FROM $wpdb->options WHERE option_name = '_transient_{$key}' LIMIT 1";
     97        $unexpected_query_timeout   = "SELECT option_value FROM $wpdb->options WHERE option_name = '_transient_timeout_{$key}' LIMIT 1";
     98        $queries                    = array();
     99
     100        set_transient( $key, $value, $timeout );
     101
     102        // Clear the cache of both the transient and the timeout.
     103        $option_names = array(
     104            '_transient_' . $key,
     105            '_transient_timeout_' . $key,
     106        );
     107        foreach ( $option_names as $option_name ) {
     108            wp_cache_delete( $option_name, 'options' );
     109        }
     110
     111        add_filter(
     112            'query',
     113            function ( $query ) use ( &$queries ) {
     114                $queries[] = $query;
     115                return $query;
     116            }
     117        );
     118
     119        $before_queries = get_num_queries();
     120        $this->assertSame( $value, get_transient( $key ) );
     121        $transient_queries = get_num_queries() - $before_queries;
     122        $this->assertSame( 1, $transient_queries, 'Expected a single database query to retrieve the transient.' );
     123        $this->assertContains( $expected_query, $queries, 'Expected query to prime both transient options in a single call.' );
     124        // Note: Some versions of PHPUnit and/or the test suite may report failures as asserting to contain rather than not to contain.
     125        $this->assertNotContains( $unexpected_query_transient, $queries, 'Unexpected query of transient option individually.' );
     126        $this->assertNotContains( $unexpected_query_timeout, $queries, 'Unexpected query of transient timeout option individually.' );
     127    }
     128
     129    /**
     130     * Ensure set_transient() primes the option cache checking for an existing transient.
     131     *
     132     * @ticket 61193
     133     *
     134     * @covers ::set_transient
     135     */
     136    public function test_set_transient_primes_option_cache() {
     137        global $wpdb;
     138        $key                        = 'test_transient';
     139        $value                      = 'test_value';
     140        $timeout                    = 100;
     141        $expected_query             = "SELECT option_name, option_value FROM $wpdb->options WHERE option_name IN ('_transient_{$key}','_transient_timeout_{$key}')";
     142        $unexpected_query_transient = "SELECT option_value FROM $wpdb->options WHERE option_name = '_transient_{$key}' LIMIT 1";
     143        $unexpected_query_timeout   = "SELECT option_value FROM $wpdb->options WHERE option_name = '_transient_timeout_{$key}' LIMIT 1";
     144        $queries                    = array();
     145
     146        add_filter(
     147            'query',
     148            function ( $query ) use ( &$queries ) {
     149                $queries[] = $query;
     150                return $query;
     151            }
     152        );
     153
     154        $before_queries = get_num_queries();
     155        $this->assertTrue( set_transient( $key, $value, $timeout ) );
     156        $transient_queries = get_num_queries() - $before_queries;
     157        $this->assertSame( 3, $transient_queries, 'Expected three database queries setting the transient.' );
     158        $this->assertContains( $expected_query, $queries, 'Expected query to prime both transient options in a single call.' );
     159        // Note: Some versions of PHPUnit and/or the test suite may report failures as asserting to contain rather than not to contain.
     160        $this->assertNotContains( $unexpected_query_transient, $queries, 'Unexpected query of transient option individually.' );
     161        $this->assertNotContains( $unexpected_query_timeout, $queries, 'Unexpected query of transient timeout option individually.' );
    81162    }
    82163
Note: See TracChangeset for help on using the changeset viewer.