Make WordPress Core

Opened 18 months ago

Last modified 12 days ago

#55969 new enhancement

The function set_transient should have the autoload argument

Reported by: giuse's profile giuse Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 6.0
Component: Options, Meta APIs Keywords: needs-patch
Focuses: performance Cc:

Description

The function set_transient should have an argument to decide if a specific transient should be autoloaded or not so.
At the moment every transient that is set with the function set_transient is autoloaded.
Not all the transients have the need to be autoloaded. For example, many times transients that are used only in the backend don't need the autoload, but they slow down the frontend in some cases.

Change History (10)

#1 @galbaras
18 months ago

The set_transient() manual states:

NB: transients that never expire are autoloaded, whereas transients with an expiration time are not autoloaded. Consider this when adding transients that may not be needed on every page, and thus do not need to be autoloaded, impacting page performance.

This seems to make sense. Temporary values should definitely not be autoloaded, but permanent ones can help save some time by being autoloaded.

I've just checked one of my sites and it has 14 autoloaded transients vs. 138 that aren't autoloaded.

You can find out about your own site using the following SQL query:

SELECT autoload, count(*) FROM wp_options WHERE option_name LIKE '_transient_%' group by autoload

#2 @giuse
18 months ago

Temporary values should definitely not be autoloaded

It makes sense, I would totally agree if the sentence was without "definitely". You may have some rare situations where you want the autoload also for this kind of transients. Why don't give me the possibility to choose? Imagine transients that expire after 1-2 months. For 1-2 months I want them autoloaded.
You can't predict all the cases. It's perfect to have as default the autoload that has a sense for most cases, but why don't give the possibility to choose?
If I don't explicitly assign the autoload parameter it would be as it is now, and if I assign it, it means that I need to assign it.

Should permanent transients always be autoloaded? Also in this case, as a default yes, but you should be able to choose. You may need permanent transients that are not autoloaded. For example, permanent transients that run in the backend.

I would assign it as a default as it is now, but give the possibility to change the autoload if needed. The same as you do with normal options.

#3 @galbaras
18 months ago

Since this is all done in code, you can create a transient without expiry and manage updates to it yourself.

I'm wondering about the use case of this, though. Why do you need this, specifically?

Version 1, edited 18 months ago by galbaras (previous) (next) (diff)

#4 @giuse
18 months ago

Do you see any downsides in modifying the function with something like this?

<?php
function set_transient( $transient, $value, $expiration = 0,$custom_autoload = null ) {
 
    $expiration = (int) $expiration;
 
    /**
     * Filters a specific transient before its value is set.
     *
     * The dynamic portion of the hook name, `$transient`, refers to the transient name.
     *
     * @since 3.0.0
     * @since 4.2.0 The `$expiration` parameter was added.
     * @since 4.4.0 The `$transient` parameter was added.
     *
     * @param mixed  $value      New value of transient.
     * @param int    $expiration Time until expiration in seconds.
     * @param string $transient  Transient name.
     */
    $value = apply_filters( "pre_set_transient_{$transient}", $value, $expiration, $transient );
 
    /**
     * Filters the expiration for a transient before its value is set.
     *
     * The dynamic portion of the hook name, `$transient`, refers to the transient name.
     *
     * @since 4.4.0
     *
     * @param int    $expiration Time until expiration in seconds. Use 0 for no expiration.
     * @param mixed  $value      New value of transient.
     * @param string $transient  Transient name.
     */
    $expiration = apply_filters( "expiration_of_transient_{$transient}", $expiration, $value, $transient );
 
    if ( wp_using_ext_object_cache() || wp_installing() ) {
        $result = wp_cache_set( $transient, $value, 'transient', $expiration );
    } else {
        $transient_timeout = '_transient_timeout_' . $transient;
        $transient_option  = '_transient_' . $transient;
        $custom_autoload_is_valid = null !== $custom_autoload && in_array( $custom_autoload,array( 'no','yes',true,false ) );
 
        if ( false === get_option( $transient_option ) ) {

            $autoload = $custom_autoload_is_valid ? $custom_autoload : 'yes';
            if ( $expiration ) {
                $autoload = $ccustom_autoload_is_valid ? $custom_autoload : 'no';
                add_option( $transient_timeout, time() + $expiration, '', $autoload );
            }
            $result = add_option( $transient_option, $value, '', $autoload );
        } else {
            // If expiration is requested, but the transient has no timeout option,
            // delete, then re-create transient rather than update.
            $update = true;
 
            if ( $expiration ) {
                $autoload = $ccustom_autoload_is_valid ? $custom_autoload : 'no';
                if ( false === get_option( $transient_timeout ) ) {
                    delete_option( $transient_option );
                    add_option( $transient_timeout, time() + $expiration, '', $autoload );
                    $result = add_option( $transient_option, $value, '', $autoload );
                    $update = false;
                } else {
                    update_option( $transient_timeout, time() + $expiration,$autoload );
                }
            }
 
            if ( $update ) {
                $result = update_option( $transient_option, $value );
            }
        }
    }
 
    if ( $result ) {
 
        /**
         * Fires after the value for a specific transient has been set.
         *
         * The dynamic portion of the hook name, `$transient`, refers to the transient name.
         *
         * @since 3.0.0
         * @since 3.6.0 The `$value` and `$expiration` parameters were added.
         * @since 4.4.0 The `$transient` parameter was added.
         *
         * @param mixed  $value      Transient value.
         * @param int    $expiration Time until expiration in seconds.
         * @param string $transient  The name of the transient.
         */
        do_action( "set_transient_{$transient}", $value, $expiration, $transient );
 
        /**
         * Fires after the value for a transient has been set.
         *
         * @since 3.0.0
         * @since 3.6.0 The `$value` and `$expiration` parameters were added.
         *
         * @param string $transient  The name of the transient.
         * @param mixed  $value      Transient value.
         * @param int    $expiration Time until expiration in seconds.
         */
        do_action( 'setted_transient', $transient, $value, $expiration );
    }
 
    return $result;
}

Then, if I want to set a transient that expires for instance in 90 days, and I want the autoload (e.g. for a transient that is needed for 90 days and has to be autoloaded:

<?php
set_transient( 'my_transient',$my_value, 60*60*24*90,'yes' );

Without any workaround and any additional code.

IF I want a transient without expiration but also without autoload (e.g. for a transient that is needed only in the backend:

<?php
set_transient( 'my_transient',$my_value,false,'yes' );

Without workarounds and without additional code.

Last edited 18 months ago by giuse (previous) (diff)

#5 follow-up: @desrosj
17 months ago

  • Keywords needs-patch added

@giuse if you could, please attach your suggested changes in the form of a patch or a pull request on GitHub. It's very difficult to see what is being changed from a full snippet.

#6 @desrosj
17 months ago

  • Component changed from Database to Options, Meta APIs

#7 @iandunn
15 months ago

Related: #54221 is one example of where this might be useful in Core.

Temporary values should definitely not be autoloaded, but permanent ones can help save some time by being autoloaded.

The correlation between expiration of a transient and how many pages it's used on isn't obvious to me; can you articulate it?

#8 @iandunn
15 months ago

ticket:22844#comment:6 alludes to a discussion in the 2.8 development phase. I searched around, though, and didn't find anything on Make/Core or wp-hackers. The logs for #wordpress-dev only go back to 2009-05-14 (2.7 was released on 2008-12-10, and 2.8 on 2009-6-11).


The new Optimized Autoloaded Options proposal is related.

#9 @oglekler
13 days ago

I stumbled upon the same problem. I am getting some data from a third-party API that is needed for all front-end pages. I need this data to be valid, but I need to preserve it until the site does not get new data to avoid issues. So, I added a transient without expiration time and wanted to add another transient with expiration, but it turned out that it would not be autoloaded and would require an additional request to the DB. Finally, I decided to add a timestamp to the data I am receiving and save it in the same transient, but it would have been simpler just to check the second transient for existence.

#10 in reply to: ↑ 5 @galbaras
12 days ago

Replying to giuse:

Do you see any downsides in modifying the function with something like this?

I don't think there is a (significant) downside.

Also, your code refers to $ccustom_autoload_is_valid, which should be $custom_autoload_is_valid.

Replying to desrosj:

@giuse if you could, please attach your suggested changes in the form of a patch or a pull request on GitHub. It's very difficult to see what is being changed from a full snippet.

Since the suggested code is already in this ticket, can you help with this? Personally, I wouldn't know what to do.

Note: See TracTickets for help on using tickets.