From a8f204894f8dd6d3259e892b6d61711ebf2505ab Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 15 Dec 2006 00:10:33 +0000 Subject: [PATCH] Add replication-friendly background batch update mode. Rip out the ugly MySQL 4.0 mode and use the background mode for it, it should be compatible too. Add a little help text --- maintenance/initEditCount.php | 83 +++++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 19 deletions(-) diff --git a/maintenance/initEditCount.php b/maintenance/initEditCount.php index a975f35616..9d165cfba5 100644 --- a/maintenance/initEditCount.php +++ b/maintenance/initEditCount.php @@ -2,35 +2,80 @@ require_once "commandLine.inc"; -// @fixme: add replication-friendly batch mode +if( isset( $options['help'] ) ) { + die( "Batch-recalculate user_editcount fields from the revision table. +Options: + --quick Force the update to be done in a single query. + --background Force replication-friendly mode; may be inefficient but + avoids locking tables or lagging slaves with large updates; + calculates counts on a slave if possible. +Background mode will be automatically used if the server is MySQL 4.0 +(which does not support subqueries) or if multiple servers are listed +in \$wgDBservers, usually indicating a replication environment. + +"); +} $dbw = wfGetDB( DB_MASTER ); $user = $dbw->tableName( 'user' ); $revision = $dbw->tableName( 'revision' ); $dbver = $dbw->getServerVersion(); -if( ($dbw instanceof DatabaseMySql && version_compare( $dbver, '4.1' ) < 0) - || isset( $options['force-mysql4'] ) ) { + +// Autodetect mode... +$backgroundMode = count( $wgDBservers ) > 1 || + ($dbw instanceof DatabaseMySql && version_compare( $dbver, '4.1' ) < 0); + +if( isset( $options['background'] ) ) { + $backgroundMode = true; +} elseif( isset( $options['quick'] ) ) { + $backgroundMode = false; +} + +if( $backgroundMode ) { + echo "Using replication-friendly background mode...\n"; - echo "Warning: MySQL $dbver; using hacky MySQL 4.0 compatibility query...\n"; - $sql = "CREATE TEMPORARY TABLE temp_editcount ( - temp_user_id INT, - temp_user_editcount INT - )"; - $dbw->query( $sql ); + $dbr = wfGetDB( DB_SLAVE ); + $chunkSize = 100; + $lastUser = $dbr->selectField( 'user', 'MAX(user_id)', '', __FUNCTION__ ); - $sql = "INSERT INTO temp_editcount - (temp_user_id, temp_user_editcount) - SELECT rev_user, COUNT(rev_user) - FROM $revision GROUP BY rev_user"; - $dbw->query( $sql ); - - $sql = "UPDATE $user - LEFT OUTER JOIN temp_editcount ON user_id=temp_user_id - SET user_editcount=IF(temp_user_editcount IS NULL,0,temp_user_editcount)"; - $dbw->query( $sql ); + $start = microtime( true ); + $migrated = 0; + for( $min = 0; $min <= $lastUser; $min += $chunkSize ) { + $max = $min + $chunkSize; + $result = $dbr->query( + "SELECT + user_id, + COUNT(rev_user) AS user_editcount + FROM $user + LEFT OUTER JOIN $revision ON user_id=rev_user + WHERE user_id > $min AND user_id <= $max + GROUP BY user_id", + __FUNCTION__ ); + + while( $row = $dbr->fetchObject( $result ) ) { + $dbw->update( 'user', + array( 'user_editcount' => $row->user_editcount ), + array( 'user_id' => $row->user_id ), + __FUNCTION__ ); + ++$migrated; + } + $dbr->freeResult( $result ); + + $delta = microtime( true ) - $start; + $rate = ($delta == 0.0) ? 0.0 : $migrated / $delta; + printf( "%s %d (%0.1f%%) done in %0.1f secs (%0.3f accounts/sec).\n", + $wgDBname, + $migrated, + min( $max, $lastUser ) / $lastUser * 100.0, + $delta, + $rate ); + + wfWaitForSlaves( 10 ); + } } else { // Subselect should work on modern MySQLs etc + echo "Using single-query mode...\n"; $sql = "UPDATE $user SET user_editcount=(SELECT COUNT(*) FROM $revision WHERE rev_user=user_id)"; $dbw->query( $sql ); } -- 2.20.1