From d6f0a95465a72c9c0e59feada8f3670f4a850987 Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Mon, 7 Jul 2008 03:31:00 +0000 Subject: [PATCH] * Split off DB load monitoring logic into a LoadMonitor class hierarchy, to allow for plugins in this area. * Use an associative array to initialise LoadBalancer objects * By default, use Preprocessor_DOM if available, otherwise use Preprocessor_Hash. Preprocessor_Hash has worse performance. * Fix parserTests.php for replicated databases. Use CREATE TABLE instead of CREATE TEMPORARY TABLE if there is more than one server configured. * Log exceptions even in command-line mode. --- includes/AutoLoader.php | 2 + includes/DefaultSettings.php | 16 ++- includes/Exception.php | 8 +- includes/db/LBFactory.php | 9 +- includes/db/LBFactory_Multi.php | 5 +- includes/db/LoadBalancer.php | 162 +++++++++++++---------- includes/db/LoadMonitor.php | 121 +++++++++++++++++ includes/parser/Parser.php | 2 + maintenance/importDump.php | 1 + maintenance/parserTests.inc | 227 +++++++++++++++++++------------- 10 files changed, 382 insertions(+), 171 deletions(-) create mode 100644 includes/db/LoadMonitor.php diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index f1d8a7d820..e90491b6e6 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -302,6 +302,8 @@ class AutoLoader { 'LBFactory_Multi' => 'includes/db/LBFactory_Multi.php', 'LBFactory_Simple' => 'includes/db/LBFactory.php', 'LoadBalancer' => 'includes/db/LoadBalancer.php', + 'LoadMonitor' => 'includes/db/LoadMonitor.php', + 'LoadMonitor_MySQL' => 'includes/db/LoadMonitor.php', 'MSSQLField' => 'includes/db/DatabaseMssql.php', 'MySQLField' => 'includes/db/Database.php', 'MySQLMasterPos' => 'includes/db/Database.php', diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 07423a461c..537205bde3 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -3190,11 +3190,17 @@ $wgSlaveLagCritical = 30; * Parser configuration. Associative array with the following members: * * class The class name - * preprocessorClass The preprocessor class, by default it is Preprocessor_Hash. - * Preprocessor_DOM is also available and better tested, but - * it has a dependency of the dom module of PHP. - * It has no effect with Parser_OldPP parser class. * + * preprocessorClass The preprocessor class. Two classes are currently available: + * Preprocessor_Hash, which uses plain PHP arrays for tempoarary + * storage, and Preprocessor_DOM, which uses the DOM module for + * temporary storage. Preprocessor_DOM generally uses less memory; + * the speed of the two is roughly the same. + * + * If this parameter is not given, it uses Preprocessor_DOM if the + * DOM module is available, otherwise it uses Preprocessor_Hash. + * + * Has no effect on Parser_OldPP. * * The entire associative array will be passed through to the constructor as * the first parameter. Note that only Setup.php can use this variable -- @@ -3205,7 +3211,7 @@ $wgSlaveLagCritical = 30; */ $wgParserConf = array( 'class' => 'Parser', - 'preprocessorClass' => 'Preprocessor_Hash', + #'preprocessorClass' => 'Preprocessor_Hash', ); /** diff --git a/includes/Exception.php b/includes/Exception.php index 2ba2946577..74820204ee 100644 --- a/includes/Exception.php +++ b/includes/Exception.php @@ -164,13 +164,13 @@ class MWException extends Exception { */ function report() { global $wgCommandLineMode; + $log = $this->getLogMessage(); + if ( $log ) { + wfDebugLog( 'exception', $log ); + } if ( $wgCommandLineMode ) { fwrite( STDERR, $this->getText() ); } else { - $log = $this->getLogMessage(); - if ( $log ) { - wfDebugLog( 'exception', $log ); - } $this->reportHTML(); } } diff --git a/includes/db/LBFactory.php b/includes/db/LBFactory.php index e7b2778390..b354788006 100644 --- a/includes/db/LBFactory.php +++ b/includes/db/LBFactory.php @@ -118,7 +118,10 @@ class LBFactory_Simple extends LBFactory { )); } - $this->mainLB = new LoadBalancer( $wgDBservers, false, $wgMasterWaitTimeout, true ); + $this->mainLB = new LoadBalancer( array( + 'servers' => $wgDBservers, + 'masterWaitTimeout' => $wgMasterWaitTimeout + )); $this->mainLB->parentInfo( array( 'id' => 'main' ) ); $this->chronProt->initLB( $this->mainLB ); } @@ -131,7 +134,9 @@ class LBFactory_Simple extends LBFactory { if ( !isset( $wgExternalServers[$cluster] ) ) { throw new MWException( __METHOD__.": Unknown cluster \"$cluster\"" ); } - $this->extLBs[$cluster] = new LoadBalancer( $wgExternalServers[$cluster] ); + $this->extLBs[$cluster] = new LoadBalancer( array( + 'servers' => $wgExternalServers[$cluster] + )); $this->extLBs[$cluster]->parentInfo( array( 'id' => "ext-$cluster" ) ); } return $this->extLBs[$cluster]; diff --git a/includes/db/LBFactory_Multi.php b/includes/db/LBFactory_Multi.php index b5fc1f6dd7..68eee56ee0 100644 --- a/includes/db/LBFactory_Multi.php +++ b/includes/db/LBFactory_Multi.php @@ -131,7 +131,10 @@ class LBFactory_Multi extends LBFactory { function newLoadBalancer( $template, $loads, $groupLoads, $id ) { global $wgMasterWaitTimeout; $servers = $this->makeServerArray( $template, $loads, $groupLoads ); - $lb = new LoadBalancer( $servers, false, $wgMasterWaitTimeout ); + $lb = new LoadBalancer( array( + 'servers' => $servers, + 'masterWaitTimeout' => $wgMasterWaitTimeout + )); $lb->parentInfo( array( 'id' => $id ) ); return $lb; } diff --git a/includes/db/LoadBalancer.php b/includes/db/LoadBalancer.php index 4280713b2a..42c4044df7 100644 --- a/includes/db/LoadBalancer.php +++ b/includes/db/LoadBalancer.php @@ -17,11 +17,33 @@ class LoadBalancer { /* private */ var $mWaitForPos, $mWaitTimeout; /* private */ var $mLaggedSlaveMode, $mLastError = 'Unknown error'; /* private */ var $mParentInfo, $mLagTimes; + /* private */ var $mLoadMonitorClass, $mLoadMonitor; - function __construct( $servers, $failFunction = false, $waitTimeout = 10, $unused = false ) + /** + * @param array $params Array with keys: + * servers Required. Array of server info structures. + * failFunction Deprecated, use exceptions instead. + * masterWaitTimeout Replication lag wait timeout + * loadMonitor Name of a class used to fetch server lag and load. + */ + function __construct( $params ) { - $this->mServers = $servers; - $this->mFailFunction = $failFunction; + if ( !isset( $params['servers'] ) ) { + throw new MWException( __CLASS__.': missing servers parameter' ); + } + $this->mServers = $params['servers']; + + if ( isset( $params['failFunction'] ) ) { + $this->mFailFunction = $params['failFunction']; + } else { + $this->mFailFunction = false; + } + if ( isset( $params['waitTimeout'] ) ) { + $this->mWaitTimeout = $params['waitTimeout']; + } else { + $this->mWaitTimeout = 10; + } + $this->mReadIndex = -1; $this->mWriteIndex = -1; $this->mConns = array( @@ -31,12 +53,13 @@ class LoadBalancer { $this->mLastIndex = -1; $this->mLoads = array(); $this->mWaitForPos = false; - $this->mWaitTimeout = $waitTimeout; $this->mLaggedSlaveMode = false; $this->mErrorConnection = false; $this->mAllowLag = false; + $this->mLoadMonitorClass = isset( $params['loadMonitor'] ) + ? $params['loadMonitor'] : 'LoadMonitor_MySQL'; - foreach( $servers as $i => $server ) { + foreach( $params['servers'] as $i => $server ) { $this->mLoads[$i] = $server['load']; if ( isset( $server['groupLoads'] ) ) { foreach ( $server['groupLoads'] as $group => $ratio ) { @@ -54,6 +77,17 @@ class LoadBalancer { return new LoadBalancer( $servers, $failFunction, $waitTimeout ); } + /** + * Get a LoadMonitor instance + */ + function getLoadMonitor() { + if ( !isset( $this->mLoadMonitor ) ) { + $class = $this->mLoadMonitorClass; + $this->mLoadMonitor = new $class( $this ); + } + return $this->mLoadMonitor; + } + /** * Get or set arbitrary data used by the parent object, usually an LBFactory */ @@ -178,6 +212,9 @@ class LoadBalancer { throw new MWException( "Empty server array given to LoadBalancer" ); } + # Scale the configured load ratios according to the dynamic load (if the load monitor supports it) + $this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $wiki ); + $i = false; $found = false; $laggedSlaveMode = false; @@ -203,7 +240,8 @@ class LoadBalancer { if ( $i === false ) { # pickRandom() returned false - # This is permanent and means the configuration wants us to return false + # This is permanent and means the configuration or the load monitor + # wants us to return false. wfDebugLog( 'connect', __METHOD__.": pickRandom() returned false\n" ); wfProfileOut( __METHOD__ ); return false; @@ -219,25 +257,24 @@ class LoadBalancer { continue; } - if ( isset( $this->mServers[$i]['max threads'] ) ) { - $status = $conn->getStatus("Thread%"); - if ( $wiki !== false ) { - $this->reuseConnection( $conn ); - } - if ( $status['Threads_running'] > $this->mServers[$i]['max threads'] ) { - $totalThreadsConnected += $status['Threads_connected']; - $overloadedServers++; - unset( $currentLoads[$i] ); - } else { - # Max threads satisfied, return this server - break 2; - } + // Perform post-connection backoff + $threshold = isset( $this->mServers[$i]['max threads'] ) + ? $this->mServers[$i]['max threads'] : false; + $backoff = $this->getLoadMonitor()->postConnectionBackoff( $conn, $threshold ); + + // Decrement reference counter, we are finished with this connection. + // It will be incremented for the caller later. + if ( $wiki !== false ) { + $this->reuseConnection( $conn ); + } + + if ( $backoff ) { + # Post-connection overload, don't use this server for now + $totalThreadsConnected += $backoff; + $overloadedServers++; + unset( $currentLoads[$i] ); } else { - # No maximum, return this server - if ( $wiki !== false ) { - $this->reuseConnection( $conn ); - } - $found = true; + # Return this server break 2; } } @@ -269,6 +306,7 @@ class LoadBalancer { } if ( $i !== false ) { + # Slave connection successful # Wait for the session master pos for a short time if ( $this->mWaitForPos && $i > 0 ) { if ( !$this->doWait( $i ) ) { @@ -679,6 +717,7 @@ class LoadBalancer { /** * Get the host name or IP address of the server with the specified index + * Prefer a readable name if available. */ function getServerName( $i ) { if ( isset( $this->mServers[$i]['hostName'] ) ) { @@ -690,6 +729,17 @@ class LoadBalancer { } } + /** + * Return the server info structure for a given index, or false if the index is invalid. + */ + function getServerInfo( $i ) { + if ( isset( $this->mServers[$i] ) ) { + return $this->mServers[$i]; + } else { + return false; + } + } + /** * Get the current master position for chronology control purposes * @return mixed @@ -813,6 +863,20 @@ class LoadBalancer { return $success; } + /** + * Call a function with each open connection object + */ + function forEachOpenConnection( $callback, $params = array() ) { + foreach ( $this->mConns as $conns2 ) { + foreach ( $conns2 as $conns3 ) { + foreach ( $conns3 as $conn ) { + $mergedParams = array_merge( array( $conn ), $params ); + call_user_func_array( $callback, $mergedParams ); + } + } + } + } + /** * Get the hostname and lag time of the most-lagged slave. * This is useful for maintenance scripts that need to throttle their updates. @@ -843,52 +907,12 @@ class LoadBalancer { * Results are cached for a short time in memcached, and indefinitely in the process cache */ function getLagTimes( $wiki = false ) { - wfProfileIn( __METHOD__ ); - - if ( !isset( $this->mLagTimes ) ) { - $expiry = 5; - $requestRate = 10; - - global $wgMemc; - $masterName = $this->getServerName( 0 ); - $memcKey = wfMemcKey( 'lag_times', $masterName ); - $times = $wgMemc->get( $memcKey ); - if ( $times ) { - # Randomly recache with probability rising over $expiry - $elapsed = time() - $times['timestamp']; - $chance = max( 0, ( $expiry - $elapsed ) * $requestRate ); - if ( mt_rand( 0, $chance ) != 0 ) { - unset( $times['timestamp'] ); - wfProfileOut( __METHOD__ ); - return $times; - } - wfIncrStats( 'lag_cache_miss_expired' ); - } else { - wfIncrStats( 'lag_cache_miss_absent' ); - } - - # Cache key missing or expired - - $times = array(); - foreach ( $this->mServers as $i => $conn ) { - if ($i == 0) { # Master - $times[$i] = 0; - } elseif ( false !== ( $conn = $this->getAnyOpenConnection( $i ) ) ) { - $times[$i] = $conn->getLag(); - } elseif ( false !== ( $conn = $this->openConnection( $i, $wiki ) ) ) { - $times[$i] = $conn->getLag(); - } - } - - # Add a timestamp key so we know when it was cached - $times['timestamp'] = time(); - $wgMemc->set( $memcKey, $times, $expiry ); - - # But don't give the timestamp to the caller - unset($times['timestamp']); - $this->mLagTimes = $times; + # Try process cache + if ( isset( $this->mLagTimes ) ) { + return $this->mLagTimes; } - wfProfileOut( __METHOD__ ); + # No, send the request to the load monitor + $this->mLagTimes = $this->getLoadMonitor()->getLagTimes( array_keys( $this->mServers ), $wiki ); return $this->mLagTimes; } } diff --git a/includes/db/LoadMonitor.php b/includes/db/LoadMonitor.php new file mode 100644 index 0000000000..8e16f1a1a7 --- /dev/null +++ b/includes/db/LoadMonitor.php @@ -0,0 +1,121 @@ +parent = $parent; + } + + function scaleLoads( &$loads, $group = false, $wiki = false ) { + } + + function getLagTimes( $serverIndexes, $wiki ) { + wfProfileIn( __METHOD__ ); + $expiry = 5; + $requestRate = 10; + + global $wgMemc; + $masterName = $this->parent->getServerName( 0 ); + $memcKey = wfMemcKey( 'lag_times', $masterName ); + $times = $wgMemc->get( $memcKey ); + if ( $times ) { + # Randomly recache with probability rising over $expiry + $elapsed = time() - $times['timestamp']; + $chance = max( 0, ( $expiry - $elapsed ) * $requestRate ); + if ( mt_rand( 0, $chance ) != 0 ) { + unset( $times['timestamp'] ); + wfProfileOut( __METHOD__ ); + return $times; + } + wfIncrStats( 'lag_cache_miss_expired' ); + } else { + wfIncrStats( 'lag_cache_miss_absent' ); + } + + # Cache key missing or expired + + $times = array(); + foreach ( $serverIndexes as $i ) { + if ($i == 0) { # Master + $times[$i] = 0; + } elseif ( false !== ( $conn = $this->parent->getAnyOpenConnection( $i ) ) ) { + $times[$i] = $conn->getLag(); + } elseif ( false !== ( $conn = $this->parent->openConnection( $i, $wiki ) ) ) { + $times[$i] = $conn->getLag(); + } + } + + # Add a timestamp key so we know when it was cached + $times['timestamp'] = time(); + $wgMemc->set( $memcKey, $times, $expiry ); + + # But don't give the timestamp to the caller + unset($times['timestamp']); + $lagTimes = $times; + + wfProfileOut( __METHOD__ ); + return $lagTimes; + } + + function postConnectionBackoff( $conn, $threshold ) { + if ( !$threshold ) { + return 0; + } + $status = $conn->getStatus("Thread%"); + if ( $status['Threads_running'] > $threshold ) { + return $status['Threads_connected']; + } else { + return 0; + } + } +} + diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php index f7235255ff..c1c48866ea 100644 --- a/includes/parser/Parser.php +++ b/includes/parser/Parser.php @@ -133,6 +133,8 @@ class Parser $this->mVarCache = array(); if ( isset( $conf['preprocessorClass'] ) ) { $this->mPreprocessorClass = $conf['preprocessorClass']; + } elseif ( class_exists( 'DOMDocument' ) ) { + $this->mPreprocessorClass = 'Preprocessor_DOM'; } else { $this->mPreprocessorClass = 'Preprocessor_Hash'; } diff --git a/maintenance/importDump.php b/maintenance/importDump.php index d74e32bfe2..99e69ce843 100644 --- a/maintenance/importDump.php +++ b/maintenance/importDump.php @@ -97,6 +97,7 @@ class BackupReader { } $this->progress( "$this->pageCount ($rate pages/sec $revrate revs/sec)" ); } + wfWaitForSlaves(5); } function progress( $string ) { diff --git a/maintenance/parserTests.inc b/maintenance/parserTests.inc index 1e1c483a0b..d1ac875a59 100644 --- a/maintenance/parserTests.inc +++ b/maintenance/parserTests.inc @@ -47,6 +47,16 @@ class ParserTest { */ private $showOutput; + /** + * boolean $useTemporaryTables Use temporary tables for the temporary database + */ + private $useTemporaryTables = true; + + /** + * boolean $databaseSetupDone True if the database has been set up + */ + private $databaseSetupDone = false; + /** * Sets terminal colorization and diff/quick modes depending on OS and * command-line options (--color and --quick). @@ -126,6 +136,7 @@ class ParserTest { * @return bool True if passed all tests, false if any tests failed. */ public function runTestsFromFiles( $filenames ) { + $this->setupDatabase(); $this->recorder->start(); $ok = true; foreach( $filenames as $filename ) { @@ -133,6 +144,7 @@ class ParserTest { } $this->recorder->report(); $this->recorder->end(); + $this->teardownDatabase(); return $ok; } @@ -351,12 +363,6 @@ class ParserTest { * Ideally this should replace the global configuration entirely. */ private function setupGlobals($opts = '') { - # Save the prefixed / quoted table names for later use when we make the temporaries. - $db = wfGetDB( DB_SLAVE ); - $this->oldTableNames = array(); - foreach( $this->listTables() as $table ) { - $this->oldTableNames[$table] = $db->tableName( $table ); - } if( !isset( $this->uploadDir ) ) { $this->uploadDir = $this->setupUploadDir(); } @@ -424,7 +430,6 @@ class ParserTest { $GLOBALS['wgContLang'] = $langObj; //$GLOBALS['wgMessageCache'] = new MessageCache( new BagOStuff(), false, 0, $GLOBALS['wgDBname'] ); - $this->setupDatabase(); global $wgUser; $wgUser = new User(); @@ -462,97 +467,139 @@ class ParserTest { * the db will be visible to later tests in the run. */ private function setupDatabase() { - static $setupDB = false; global $wgDBprefix; + if ( $this->databaseSetupDone ) { + return; + } + if ( $wgDBprefix === 'parsertest_' ) { + throw new MWException( 'setupDatabase should be called before setupGlobals' ); + } + $this->databaseSetupDone = true; - # Make sure we don't mess with the live DB - if (!$setupDB && $wgDBprefix === 'parsertest_') { - # oh teh horror - LBFactory::destroy(); - $db = wfGetDB( DB_MASTER ); - - $tables = $this->listTables(); - - if (!(strcmp($db->getServerVersion(), '4.1') < 0 and stristr($db->getSoftwareLink(), 'MySQL'))) { - # Database that supports CREATE TABLE ... LIKE - global $wgDBtype; - if( $wgDBtype == 'postgres' ) { - $def = 'INCLUDING DEFAULTS'; - } else { - $def = ''; - } - foreach ($tables as $tbl) { - $newTableName = $db->tableName( $tbl ); - $tableName = $this->oldTableNames[$tbl]; - $db->query("CREATE TEMPORARY TABLE $newTableName (LIKE $tableName $def)"); - } + # CREATE TEMPORARY TABLE breaks if there is more than one server + if ( wfGetLB()->getServerCount() != 1 ) { + $this->useTemporaryTables = false; + } + + $temporary = $this->useTemporaryTables ? 'TEMPORARY' : ''; + + $db = wfGetDB( DB_MASTER ); + $tables = $this->listTables(); + + if (!(strcmp($db->getServerVersion(), '4.1') < 0 and stristr($db->getSoftwareLink(), 'MySQL'))) { + # Database that supports CREATE TABLE ... LIKE + global $wgDBtype; + if( $wgDBtype == 'postgres' ) { + $def = 'INCLUDING DEFAULTS'; } else { - # Hack for MySQL versions < 4.1, which don't support - # "CREATE TABLE ... LIKE". Note that - # "CREATE TEMPORARY TABLE ... SELECT * FROM ... LIMIT 0" - # would not create the indexes we need.... - foreach ($tables as $tbl) { - $res = $db->query("SHOW CREATE TABLE {$this->oldTableNames[$tbl]}"); - $row = $db->fetchRow($res); - $create = $row[1]; - $create_tmp = preg_replace('/CREATE TABLE `(.*?)`/', 'CREATE TEMPORARY TABLE `' - . $wgDBprefix . $tbl .'`', $create); - if ($create === $create_tmp) { - # Couldn't do replacement - wfDie("could not create temporary table $tbl"); - } - $db->query($create_tmp); + $def = ''; + } + foreach ($tables as $tbl) { + $oldTableName = $db->tableName( $tbl ); + # Clean up from previous aborted run + if ( $db->tableExists( "`parsertest_$tbl`" ) ) { + $db->query("DROP TABLE `parsertest_$tbl`"); } - + # Create new table + $db->query("CREATE $temporary TABLE `parsertest_$tbl` (LIKE $oldTableName $def)"); + } + } else { + # Hack for MySQL versions < 4.1, which don't support + # "CREATE TABLE ... LIKE". Note that + # "CREATE TEMPORARY TABLE ... SELECT * FROM ... LIMIT 0" + # would not create the indexes we need.... + foreach ($tables as $tbl) { + $oldTableName = $db->tableName( $tbl ); + $res = $db->query("SHOW CREATE TABLE $oldTableName"); + $row = $db->fetchRow($res); + $create = $row[1]; + $create_tmp = preg_replace('/CREATE TABLE `(.*?)`/', + "CREATE $temporary TABLE `parsertest_$tbl`", $create); + if ($create === $create_tmp) { + # Couldn't do replacement + wfDie("could not create temporary table $tbl"); + } + $db->query($create_tmp); } - - # Hack: insert a few Wikipedia in-project interwiki prefixes, - # for testing inter-language links - $db->insert( 'interwiki', array( - array( 'iw_prefix' => 'Wikipedia', - 'iw_url' => 'http://en.wikipedia.org/wiki/$1', - 'iw_local' => 0 ), - array( 'iw_prefix' => 'MeatBall', - 'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1', - 'iw_local' => 0 ), - array( 'iw_prefix' => 'zh', - 'iw_url' => 'http://zh.wikipedia.org/wiki/$1', - 'iw_local' => 1 ), - array( 'iw_prefix' => 'es', - 'iw_url' => 'http://es.wikipedia.org/wiki/$1', - 'iw_local' => 1 ), - array( 'iw_prefix' => 'fr', - 'iw_url' => 'http://fr.wikipedia.org/wiki/$1', - 'iw_local' => 1 ), - array( 'iw_prefix' => 'ru', - 'iw_url' => 'http://ru.wikipedia.org/wiki/$1', - 'iw_local' => 1 ), - ) ); - - # Hack: Insert an image to work with - $db->insert( 'image', array( - 'img_name' => 'Foobar.jpg', - 'img_size' => 12345, - 'img_description' => 'Some lame file', - 'img_user' => 1, - 'img_user_text' => 'WikiSysop', - 'img_timestamp' => $db->timestamp( '20010115123500' ), - 'img_width' => 1941, - 'img_height' => 220, - 'img_bits' => 24, - 'img_media_type' => MEDIATYPE_BITMAP, - 'img_major_mime' => "image", - 'img_minor_mime' => "jpeg", - 'img_metadata' => serialize( array() ), - ) ); - - # Update certain things in site_stats - $db->insert( 'site_stats', array( 'ss_row_id' => 1, 'ss_images' => 1, 'ss_good_articles' => 1 ) ); - - $setupDB = true; } + + # Hack: insert a few Wikipedia in-project interwiki prefixes, + # for testing inter-language links + $db->insert( '`parsertest_interwiki`', array( + array( 'iw_prefix' => 'Wikipedia', + 'iw_url' => 'http://en.wikipedia.org/wiki/$1', + 'iw_local' => 0 ), + array( 'iw_prefix' => 'MeatBall', + 'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1', + 'iw_local' => 0 ), + array( 'iw_prefix' => 'zh', + 'iw_url' => 'http://zh.wikipedia.org/wiki/$1', + 'iw_local' => 1 ), + array( 'iw_prefix' => 'es', + 'iw_url' => 'http://es.wikipedia.org/wiki/$1', + 'iw_local' => 1 ), + array( 'iw_prefix' => 'fr', + 'iw_url' => 'http://fr.wikipedia.org/wiki/$1', + 'iw_local' => 1 ), + array( 'iw_prefix' => 'ru', + 'iw_url' => 'http://ru.wikipedia.org/wiki/$1', + 'iw_local' => 1 ), + ) ); + + # Hack: Insert an image to work with + $db->insert( '`parsertest_image`', array( + 'img_name' => 'Foobar.jpg', + 'img_size' => 12345, + 'img_description' => 'Some lame file', + 'img_user' => 1, + 'img_user_text' => 'WikiSysop', + 'img_timestamp' => $db->timestamp( '20010115123500' ), + 'img_width' => 1941, + 'img_height' => 220, + 'img_bits' => 24, + 'img_media_type' => MEDIATYPE_BITMAP, + 'img_major_mime' => "image", + 'img_minor_mime' => "jpeg", + 'img_metadata' => serialize( array() ), + ) ); + + # Update certain things in site_stats + $db->insert( '`parsertest_site_stats`', array( 'ss_row_id' => 1, 'ss_images' => 1, 'ss_good_articles' => 1 ) ); + + # Change the table prefixes on all open connections + LBFactory::singleton()->forEachLB( array( $this, 'changeLBPrefix' ) ); + $wgDBprefix = 'parsertest_'; } + public function changeLBPrefix( $lb ) { + $lb->forEachOpenConnection( array( $this, 'changeDBPrefix' ) ); + } + + public function changeDBPrefix( $db ) { + $db->tablePrefix( 'parsertest_' ); + } + + private function teardownDatabase() { + if ( !$this->databaseSetupDone ) { + return; + } + $this->databaseSetupDone = false; + if ( $this->useTemporaryTables ) { + # Don't need to do anything + return; + } + + $tables = $this->listTables(); + $db = wfGetDB( DB_MASTER ); + foreach ( $tables as $table ) { + $db->query( "DROP TABLE `parsertest_$table`" ); + } + + # Close all connections which are open with the parsertest_ prefix, so that setupDatabase() will work + LBFactory::singleton()->forEachLBCallMethod( 'closeAll' ); + LBFactory::destroy(); + } + /** * Create a dummy uploads directory which will contain a couple * of files in order to pass existence tests. -- 2.20.1