Make WordPress Core

Changeset 33615


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