WordPress.org

Make WordPress Core

Changeset 33615


Ignore:
Timestamp:
08/14/2015 03:58:41 AM (4 years ago)
Author:
boonebgorges
Message:

Term splitting routine should be run in a separate process, triggered via wp-cron.

[32814] introduced a routine to split shared terms, which was run during the
regular WP database upgrade. This turned out to be problematic because plugins
are not loaded during the db upgrade (due to WP_INSTALLING), with the result
that plugins were not able to hook into the 'split_shared_term' action during
the bulk split. We work around this limitation by moving the term splitting
routine to a separate process, triggered by a wp-cron hook.

Props boonebgorges, Chouby, peterwilsoncc, pento, dd32.
Fixes #30261.

Location:
trunk/src
Files:
3 edited

Legend:

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

    r33609 r33615  
    15061506    }
    15071507
     1508    // Shared terms are split in a separate process.
    15081509    if ( $wp_current_db_version < 32814 ) {
    1509         split_all_shared_terms();
     1510        wp_schedule_single_event( time() + ( 1 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' );
    15101511    }
    15111512
     
    18791880
    18801881    return $wpdb->query( "ALTER TABLE $table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" );
    1881 }
    1882 
    1883 /**
    1884  * Splits all shared taxonomy terms.
    1885  *
    1886  * @since 4.3.0
    1887  *
    1888  * @global wpdb $wpdb WordPress database abstraction object.
    1889  */
    1890 function split_all_shared_terms() {
    1891     global $wpdb;
    1892 
    1893     // Get a list of shared terms (those with more than one associated row in term_taxonomy).
    1894     $shared_terms = $wpdb->get_results(
    1895         "SELECT tt.term_id, t.*, count(*) as term_tt_count FROM {$wpdb->term_taxonomy} tt
    1896          LEFT JOIN {$wpdb->terms} t ON t.term_id = tt.term_id
    1897          GROUP BY t.term_id
    1898          HAVING term_tt_count > 1"
    1899     );
    1900 
    1901     if ( empty( $shared_terms ) ) {
    1902         return;
    1903     }
    1904 
    1905     // Rekey shared term array for faster lookups.
    1906     $_shared_terms = array();
    1907     foreach ( $shared_terms as $shared_term ) {
    1908         $term_id = intval( $shared_term->term_id );
    1909         $_shared_terms[ $term_id ] = $shared_term;
    1910     }
    1911     $shared_terms = $_shared_terms;
    1912 
    1913     // Get term taxonomy data for all shared terms.
    1914     $shared_term_ids = implode( ',', array_keys( $shared_terms ) );
    1915     $shared_tts = $wpdb->get_results( "SELECT * FROM {$wpdb->term_taxonomy} WHERE `term_id` IN ({$shared_term_ids})" );
    1916 
    1917     // Split term data recording is slow, so we do it just once, outside the loop.
    1918     $suspend = wp_suspend_cache_invalidation( true );
    1919     $split_term_data = get_option( '_split_terms', array() );
    1920     $skipped_first_term = $taxonomies = array();
    1921     foreach ( $shared_tts as $shared_tt ) {
    1922         $term_id = intval( $shared_tt->term_id );
    1923 
    1924         // Don't split the first tt belonging to a given term_id.
    1925         if ( ! isset( $skipped_first_term[ $term_id ] ) ) {
    1926             $skipped_first_term[ $term_id ] = 1;
    1927             continue;
    1928         }
    1929 
    1930         if ( ! isset( $split_term_data[ $term_id ] ) ) {
    1931             $split_term_data[ $term_id ] = array();
    1932         }
    1933 
    1934         // Keep track of taxonomies whose hierarchies need flushing.
    1935         if ( ! isset( $taxonomies[ $shared_tt->taxonomy ] ) ) {
    1936             $taxonomies[ $shared_tt->taxonomy ] = 1;
    1937         }
    1938 
    1939         // Split the term.
    1940         $split_term_data[ $term_id ][ $shared_tt->taxonomy ] = _split_shared_term( $shared_terms[ $term_id ], $shared_tt, false );
    1941     }
    1942 
    1943     // Rebuild the cached hierarchy for each affected taxonomy.
    1944     foreach ( array_keys( $taxonomies ) as $tax ) {
    1945         delete_option( "{$tax}_children" );
    1946         _get_term_hierarchy( $tax );
    1947     }
    1948 
    1949     wp_suspend_cache_invalidation( $suspend );
    1950     update_option( '_split_terms', $split_term_data );
    19511882}
    19521883
  • trunk/src/wp-includes/default-filters.php

    r33611 r33615  
    329329
    330330// Split term updates.
     331add_action( 'admin_init',        '_wp_check_for_scheduled_split_terms' );
    331332add_action( 'split_shared_term', '_wp_check_split_default_terms',  10, 4 );
    332333add_action( 'split_shared_term', '_wp_check_split_terms_in_menus', 10, 4 );
    333334add_action( 'split_shared_term', '_wp_check_split_nav_menu_terms', 10, 4 );
     335add_action( 'wp_split_shared_term_batch', '_wp_batch_split_terms' );
    334336
    335337/**
  • trunk/src/wp-includes/taxonomy.php

    r33611 r33615  
    42504250    }
    42514251
    4252     // Don't try to split terms if database schema does not support shared slugs.
    4253     $current_db_version = get_option( 'db_version' );
    4254     if ( $current_db_version < 30133 ) {
    4255         return $term_id;
    4256     }
    4257 
    42584252    // If there are no shared term_taxonomy rows, there's nothing to do here.
    42594253    $shared_tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy tt WHERE tt.term_id = %d AND tt.term_taxonomy_id != %d", $term_id, $term_taxonomy_id ) );
     
    42614255    if ( ! $shared_tt_count ) {
    42624256        return $term_id;
     4257    }
     4258
     4259    /*
     4260     * Verify that the term_taxonomy_id passed to the function is actually associated with the term_id.
     4261     * If there's a mismatch, it may mean that the term is already split. Return the actual term_id from the db.
     4262     */
     4263    $check_term_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $term_taxonomy_id ) );
     4264    if ( $check_term_id != $term_id ) {
     4265        return $check_term_id;
    42634266    }
    42644267
     
    43374340
    43384341    return $new_term_id;
     4342}
     4343
     4344/**
     4345 * Splits a batch of shared taxonomy terms.
     4346 *
     4347 * @since 4.3.0
     4348 *
     4349 * @global wpdb $wpdb WordPress database abstraction object.
     4350 */
     4351function _wp_batch_split_terms() {
     4352    global $wpdb;
     4353
     4354    $lock_name = 'term_split.lock';
     4355
     4356    // Try to lock.
     4357    $lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", $lock_name, time() ) );
     4358
     4359    if ( ! $lock_result ) {
     4360        $lock_result = get_option( $lock_name );
     4361
     4362        // Bail if we were unable to create a lock, or if the existing lock is still valid.
     4363        if ( ! $lock_result || ( $lock_result > ( time() - HOUR_IN_SECONDS ) ) ) {
     4364            wp_schedule_single_event( time() + ( 5 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' );
     4365            return;
     4366        }
     4367    }
     4368
     4369    // Update the lock, as by this point we've definitely got a lock, just need to fire the actions.
     4370    update_option( $lock_name, time() );
     4371
     4372    // Get a list of shared terms (those with more than one associated row in term_taxonomy).
     4373    $shared_terms = $wpdb->get_results(
     4374        "SELECT tt.term_id, t.*, count(*) as term_tt_count FROM {$wpdb->term_taxonomy} tt
     4375         LEFT JOIN {$wpdb->terms} t ON t.term_id = tt.term_id
     4376         GROUP BY t.term_id
     4377         HAVING term_tt_count > 1
     4378         LIMIT 20"
     4379    );
     4380
     4381    // No more terms, we're done here.
     4382    if ( ! $shared_terms ) {
     4383        update_option( 'finished_splitting_shared_terms', true );
     4384        delete_option( $lock_name );
     4385        return;
     4386    }
     4387
     4388    // Shared terms found? We'll need to run this script again.
     4389    wp_schedule_single_event( time() + ( 2 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' );
     4390
     4391    // Rekey shared term array for faster lookups.
     4392    $_shared_terms = array();
     4393    foreach ( $shared_terms as $shared_term ) {
     4394        $term_id = intval( $shared_term->term_id );
     4395        $_shared_terms[ $term_id ] = $shared_term;
     4396    }
     4397    $shared_terms = $_shared_terms;
     4398
     4399    // Get term taxonomy data for all shared terms.
     4400    $shared_term_ids = implode( ',', array_keys( $shared_terms ) );
     4401    $shared_tts = $wpdb->get_results( "SELECT * FROM {$wpdb->term_taxonomy} WHERE `term_id` IN ({$shared_term_ids})" );
     4402
     4403    // Split term data recording is slow, so we do it just once, outside the loop.
     4404    $suspend = wp_suspend_cache_invalidation( true );
     4405    $split_term_data = get_option( '_split_terms', array() );
     4406    $skipped_first_term = $taxonomies = array();
     4407    foreach ( $shared_tts as $shared_tt ) {
     4408        $term_id = intval( $shared_tt->term_id );
     4409
     4410        // Don't split the first tt belonging to a given term_id.
     4411        if ( ! isset( $skipped_first_term[ $term_id ] ) ) {
     4412            $skipped_first_term[ $term_id ] = 1;
     4413            continue;
     4414        }
     4415
     4416        if ( ! isset( $split_term_data[ $term_id ] ) ) {
     4417            $split_term_data[ $term_id ] = array();
     4418        }
     4419
     4420        // Keep track of taxonomies whose hierarchies need flushing.
     4421        if ( ! isset( $taxonomies[ $shared_tt->taxonomy ] ) ) {
     4422            $taxonomies[ $shared_tt->taxonomy ] = 1;
     4423        }
     4424
     4425        // Split the term.
     4426        $split_term_data[ $term_id ][ $shared_tt->taxonomy ] = _split_shared_term( $shared_terms[ $term_id ], $shared_tt, false );
     4427    }
     4428
     4429    // Rebuild the cached hierarchy for each affected taxonomy.
     4430    foreach ( array_keys( $taxonomies ) as $tax ) {
     4431        delete_option( "{$tax}_children" );
     4432        _get_term_hierarchy( $tax );
     4433    }
     4434
     4435    wp_suspend_cache_invalidation( $suspend );
     4436    update_option( '_split_terms', $split_term_data );
     4437
     4438    delete_option( $lock_name );
     4439}
     4440
     4441/**
     4442 * In order to avoid the wp_batch_split_terms() job being accidentally removed,
     4443 * check that it's still scheduled while we haven't finished splitting terms.
     4444 *
     4445 * @ignore
     4446 * @since 4.3.0
     4447 */
     4448function _wp_check_for_scheduled_split_terms() {
     4449    if ( ! get_option( 'finished_splitting_shared_terms' ) && ! wp_next_scheduled( 'wp_batch_split_terms' ) ) {
     4450        wp_schedule_single_event( 'wp_batch_split_terms', time() + MINUTE_IN_SECONDS );
     4451    }
    43394452}
    43404453
Note: See TracChangeset for help on using the changeset viewer.