Make WordPress Core


Ignore:
Timestamp:
08/14/2015 03:58:41 AM (10 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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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.