'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',
* 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 --
*/
$wgParserConf = array(
'class' => 'Parser',
- 'preprocessorClass' => 'Preprocessor_Hash',
+ #'preprocessorClass' => 'Preprocessor_Hash',
);
/**
*/
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();
}
}
));
}
- $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 );
}
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];
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;
}
/* 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(
$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 ) {
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
*/
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;
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;
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;
}
}
}
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 ) ) {
/**
* 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'] ) ) {
}
}
+ /**
+ * 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
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.
* 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;
}
}
--- /dev/null
+<?php
+
+/**
+ * An interface for database load monitoring
+ */
+
+interface LoadMonitor {
+ /**
+ * Construct a new LoadMonitor with a given LoadBalancer parent
+ */
+ function __construct( $parent );
+
+ /**
+ * Perform pre-connection load ratio adjustment.
+ * @param array $loads
+ * @param string $group The selected query group
+ * @param string $wiki
+ */
+ function scaleLoads( &$loads, $group = false, $wiki = false );
+
+ /**
+ * Perform post-connection backoff.
+ *
+ * If the connection is in overload, this should return a backoff factor
+ * which will be used to control polling time. The number of threads
+ * connected is a good measure.
+ *
+ * If there is no overload, zero can be returned.
+ *
+ * A threshold thread count is given, the concrete class may compare this
+ * to the running thread count. The threshold may be false, which indicates
+ * that the sysadmin has not configured this feature.
+ *
+ * @param Database $conn
+ * @param float $threshold
+ */
+ function postConnectionBackoff( $conn, $threshold );
+
+ /**
+ * Return an estimate of replication lag for each server
+ */
+ function getLagTimes( $serverIndexes, $wiki );
+}
+
+
+/**
+ * Basic MySQL load monitor with no external dependencies
+ * Uses memcached to cache the replication lag for a short time
+ */
+
+class LoadMonitor_MySQL implements LoadMonitor {
+ var $parent; // LoadBalancer
+
+ function __construct( $parent ) {
+ $this->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;
+ }
+ }
+}
+
$this->mVarCache = array();
if ( isset( $conf['preprocessorClass'] ) ) {
$this->mPreprocessorClass = $conf['preprocessorClass'];
+ } elseif ( class_exists( 'DOMDocument' ) ) {
+ $this->mPreprocessorClass = 'Preprocessor_DOM';
} else {
$this->mPreprocessorClass = 'Preprocessor_Hash';
}
}
$this->progress( "$this->pageCount ($rate pages/sec $revrate revs/sec)" );
}
+ wfWaitForSlaves(5);
}
function progress( $string ) {
*/
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).
* @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 ) {
}
$this->recorder->report();
$this->recorder->end();
+ $this->teardownDatabase();
return $ok;
}
* 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();
}
$GLOBALS['wgContLang'] = $langObj;
//$GLOBALS['wgMessageCache'] = new MessageCache( new BagOStuff(), false, 0, $GLOBALS['wgDBname'] );
- $this->setupDatabase();
global $wgUser;
$wgUser = new User();
* 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.