Skip to content

Commit

Permalink
Options, Meta APIs: Prime transient options prior to use.
Browse files Browse the repository at this point in the history
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.



git-svn-id: https://develop.svn.wordpress.org/trunk@58134 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
peterwilsoncc committed May 11, 2024
1 parent 5e2cba2 commit ea8bc97
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/wp-includes/option.php
Expand Up @@ -1330,7 +1330,8 @@ function get_transient( $transient ) {

if ( ! isset( $alloptions[ $transient_option ] ) ) {
$transient_timeout = '_transient_timeout_' . $transient;
$timeout = get_option( $transient_timeout );
wp_prime_option_caches( array( $transient_option, $transient_timeout ) );
$timeout = get_option( $transient_timeout );
if ( false !== $timeout && $timeout < time() ) {
delete_option( $transient_option );
delete_option( $transient_timeout );
Expand Down Expand Up @@ -1410,6 +1411,7 @@ function set_transient( $transient, $value, $expiration = 0 ) {
} else {
$transient_timeout = '_transient_timeout_' . $transient;
$transient_option = '_transient_' . $transient;
wp_prime_option_caches( array( $transient_option, $transient_timeout ) );

if ( false === get_option( $transient_option ) ) {
$autoload = 'on';
Expand Down
81 changes: 81 additions & 0 deletions tests/phpunit/tests/option/transient.php
Expand Up @@ -80,6 +80,87 @@ public function test_transient_data_with_timeout() {
$this->assertFalse( get_transient( $key ) );
}

/**
* Ensure get_transient() makes a single database request.
*
* @ticket 61193
*
* @covers ::get_transient
*/
public function test_get_transient_with_timeout_makes_a_single_database_call() {
global $wpdb;
$key = 'test_transient';
$value = 'test_value';
$timeout = 100;
$expected_query = "SELECT option_name, option_value FROM $wpdb->options WHERE option_name IN ('_transient_{$key}','_transient_timeout_{$key}')";
$unexpected_query_transient = "SELECT option_value FROM $wpdb->options WHERE option_name = '_transient_{$key}' LIMIT 1";
$unexpected_query_timeout = "SELECT option_value FROM $wpdb->options WHERE option_name = '_transient_timeout_{$key}' LIMIT 1";
$queries = array();

set_transient( $key, $value, $timeout );

// Clear the cache of both the transient and the timeout.
$option_names = array(
'_transient_' . $key,
'_transient_timeout_' . $key,
);
foreach ( $option_names as $option_name ) {
wp_cache_delete( $option_name, 'options' );
}

add_filter(
'query',
function ( $query ) use ( &$queries ) {
$queries[] = $query;
return $query;
}
);

$before_queries = get_num_queries();
$this->assertSame( $value, get_transient( $key ) );
$transient_queries = get_num_queries() - $before_queries;
$this->assertSame( 1, $transient_queries, 'Expected a single database query to retrieve the transient.' );
$this->assertContains( $expected_query, $queries, 'Expected query to prime both transient options in a single call.' );
// Note: Some versions of PHPUnit and/or the test suite may report failures as asserting to contain rather than not to contain.
$this->assertNotContains( $unexpected_query_transient, $queries, 'Unexpected query of transient option individually.' );
$this->assertNotContains( $unexpected_query_timeout, $queries, 'Unexpected query of transient timeout option individually.' );
}

/**
* Ensure set_transient() primes the option cache checking for an existing transient.
*
* @ticket 61193
*
* @covers ::set_transient
*/
public function test_set_transient_primes_option_cache() {
global $wpdb;
$key = 'test_transient';
$value = 'test_value';
$timeout = 100;
$expected_query = "SELECT option_name, option_value FROM $wpdb->options WHERE option_name IN ('_transient_{$key}','_transient_timeout_{$key}')";
$unexpected_query_transient = "SELECT option_value FROM $wpdb->options WHERE option_name = '_transient_{$key}' LIMIT 1";
$unexpected_query_timeout = "SELECT option_value FROM $wpdb->options WHERE option_name = '_transient_timeout_{$key}' LIMIT 1";
$queries = array();

add_filter(
'query',
function ( $query ) use ( &$queries ) {
$queries[] = $query;
return $query;
}
);

$before_queries = get_num_queries();
$this->assertTrue( set_transient( $key, $value, $timeout ) );
$transient_queries = get_num_queries() - $before_queries;
$this->assertSame( 3, $transient_queries, 'Expected three database queries setting the transient.' );
$this->assertContains( $expected_query, $queries, 'Expected query to prime both transient options in a single call.' );
// Note: Some versions of PHPUnit and/or the test suite may report failures as asserting to contain rather than not to contain.
$this->assertNotContains( $unexpected_query_transient, $queries, 'Unexpected query of transient option individually.' );
$this->assertNotContains( $unexpected_query_timeout, $queries, 'Unexpected query of transient timeout option individually.' );
}

/**
* @ticket 22807
*
Expand Down

0 comments on commit ea8bc97

Please sign in to comment.