From 1773cd1e760eab170a09a3f29bd5af36613c4c1e Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Sat, 24 Jul 2004 07:24:04 +0000 Subject: [PATCH] * Introducing bit field for database parameters ** Database constructor calling sequence, and Database::newFromParams() ** Including flags in the server initialisation structs ** Support for setting appropriate flags from legacy globals in Setup.php ** Moved some defines to Define.php so that they can be used in LocalSettings.php, most importantly the bit field constants * Changes related to post-parse link colouring ** Turn the link cache back on when using it for updating the links table ** No longer need to call preFill() on page view * Better synchronisation of slave servers ** Rearranged getConnection() ** System for beginning and committing transactions when multiple connections are open ** wfAbruptExit() commits transactions, wfErrorExit() does not. Various functions changed to use wfErrorAbort() * Allowed reporting of database errors during deferred updates by moving them above output() --- includes/Article.php | 16 ++-- includes/Database.php | 153 ++++++++++++++++++++++++-------- includes/DatabasePostgreSQL.php | 67 +++----------- includes/DefaultSettings.php | 14 ++- includes/GlobalFunctions.php | 38 ++++++-- includes/LinksUpdate.php | 1 + includes/LoadBalancer.php | 126 ++++++++++++++++---------- includes/Namespace.php | 24 +---- includes/OutputPage.php | 11 ++- includes/Setup.php | 5 +- includes/SpecialPage.php | 6 +- index.php | 22 +++-- maintenance/refreshLinks.inc | 4 + update.php | 3 +- 14 files changed, 292 insertions(+), 198 deletions(-) diff --git a/includes/Article.php b/includes/Article.php index bfe325a467..c42f349459 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -153,7 +153,7 @@ class Article { # Try to open, return false on failure $params = $wgKnownDBServers[$machineId]; $db = Database::newFromParams( $params['server'], $params['user'], $params['password'], - $dbName, 1, false, true, true ); + $dbName, 1, DBO_IGNORE ); } } if ( $db->isOpen() ) { @@ -711,8 +711,6 @@ class Article { $pcache = false; } - $wgLinkCache->preFill( $this->mTitle ); - # wrap user css and user js in pre and don't parse # XXX: use $this->mTitle->usCssJsSubpage() when php is fixed/ a workaround is found if ( @@ -743,7 +741,7 @@ class Article { /* private */ function insertNewArticle( $text, $summary, $isminor, $watchthis ) { - global $wgOut, $wgUser, $wgLinkCache, $wgMwRedir; + global $wgOut, $wgUser, $wgMwRedir; global $wgUseSquid, $wgDeferredUpdateList, $wgInternalServer; $fname = 'Article::insertNewArticle'; @@ -888,7 +886,7 @@ class Article { function updateArticle( $text, $summary, $minor, $watchthis, $forceBot = false, $sectionanchor = '' ) { - global $wgOut, $wgUser, $wgLinkCache; + global $wgOut, $wgUser; global $wgDBtransactions, $wgMwRedir; global $wgUseSquid, $wgInternalServer; @@ -1036,7 +1034,11 @@ class Article { # Get old version of link table to allow incremental link updates $wgLinkCache->preFill( $this->mTitle ); $wgLinkCache->clear(); - + + # Switch on use of link cache in the skin + $sk =& $wgUser->getSkin(); + $sk->postParseLinkColour( false ); + # Now update the link cache by parsing the text $wgOut = new OutputPage(); $wgOut->addWikiText( $text ); @@ -1509,7 +1511,7 @@ class Article { $linkID = $titleObj->getArticleID(); $brokenLinks[] = array( 'bl_from' => $linkID, 'bl_to' => $t ); } - $dbw->insertArray( 'brokenlinks', $brokenLinks, $fname ); + $dbw->insert( 'brokenlinks', $brokenLinks, $fname, 'IGNORE' ); # Delete live links $dbw->delete( 'links', array( 'l_to' => $id ) ); diff --git a/includes/Database.php b/includes/Database.php index 2771995020..b76b2f2a3e 100644 --- a/includes/Database.php +++ b/includes/Database.php @@ -22,43 +22,61 @@ class Database { # Variables #------------------------------------------------------------------------------ /* private */ var $mLastQuery = ""; - /* private */ var $mBufferResults = true; - /* private */ var $mIgnoreErrors = false; /* private */ var $mServer, $mUser, $mPassword, $mConn, $mDBname; - /* private */ var $mOut, $mDebug, $mOpened = false; + /* private */ var $mOut, $mOpened = false; /* private */ var $mFailFunction; /* private */ var $mTablePrefix; + /* private */ var $mFlags; + /* private */ var $mTrxLevel = 0; #------------------------------------------------------------------------------ # Accessors #------------------------------------------------------------------------------ - # Set functions - # These set a variable and return the previous state + # These optionally set a variable and return the previous state # Fail function, takes a Database as a parameter # Set to false for default, 1 for ignore errors - function setFailFunction( $function ) { return wfSetVar( $this->mFailFunction, $function ); } + function failFunction( $function = NULL ) { + return wfSetVar( $this->mFailFunction, $function ); + } # Output page, used for reporting errors # FALSE means discard output - function &setOutputPage( &$out ) { $this->mOut =& $out; } + function &setOutputPage( &$out ) { + $this->mOut =& $out; + } # Boolean, controls output of large amounts of debug information - function setDebug( $debug ) { return wfSetVar( $this->mDebug, $debug ); } + function debug( $debug = NULL ) { + return wfSetBit( $this->mFlags, DBO_DEBUG, $debug ); + } # Turns buffering of SQL result sets on (true) or off (false). Default is # "on" and it should not be changed without good reasons. - function setBufferResults( $buffer ) { return wfSetVar( $this->mBufferResults, $buffer ); } + function bufferResults( $buffer = NULL ) { + if ( is_null( $buffer ) ) { + return !(bool)( $this->mFlags & DBO_NOBUFFER ); + } else { + return !wfSetBit( $this->mFlags, DBO_NOBUFFER, !$buffer ); + } + } # Turns on (false) or off (true) the automatic generation and sending # of a "we're sorry, but there has been a database error" page on # database errors. Default is on (false). When turned off, the # code should use wfLastErrno() and wfLastError() to handle the # situation as appropriate. - function setIgnoreErrors( $ignoreErrors ) { return wfSetVar( $this->mIgnoreErrors, $ignoreErrors ); } + function ignoreErrors( $ignoreErrors = NULL ) { + return wfSetBit( $this->mFlags, DBO_IGNORE, $ignoreErrors ); + } + # The current depth of nested transactions + function trxLevel( $level = NULL ) { + return wfSetVar( $this->mTrxLevel, $level ); + } + # Get functions function lastQuery() { return $this->mLastQuery; } @@ -69,10 +87,9 @@ class Database { #------------------------------------------------------------------------------ function Database( $server = false, $user = false, $password = false, $dbName = false, - $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false, - $tablePrefix = 'get from global' ) + $failFunction = false, $flags = 0, $tablePrefix = 'get from global' ) { - global $wgOut, $wgDBprefix; + global $wgOut, $wgDBprefix, $wgCommandLineMode; # Can't get a reference if it hasn't been set yet if ( !isset( $wgOut ) ) { $wgOut = NULL; @@ -80,9 +97,16 @@ class Database { $this->mOut =& $wgOut; $this->mFailFunction = $failFunction; - $this->mIgnoreErrors = $ignoreErrors; - $this->mDebug = $debug; - $this->mBufferResults = $bufferResults; + $this->mFlags = $flags; + + if ( $this->mFlags & DBO_DEFAULT ) { + if ( $wgCommandLineMode ) { + $this->mFlags &= ~DBO_TRX; + } else { + $this->mFlags |= DBO_TRX; + } + } + if ( $tablePrefix == 'get from global' ) { $this->mTablePrefix = $wgDBprefix; } else { @@ -95,10 +119,9 @@ class Database { } /* static */ function newFromParams( $server, $user, $password, $dbName, - $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false ) + $failFunction = false, $flags = 0 ) { - return new Database( $server, $user, $password, $dbName, $failFunction, $debug, - $bufferResults, $ignoreErrors ); + return new Database( $server, $user, $password, $dbName, $failFunction, $flags ); } # Usually aborts on failure @@ -146,11 +169,15 @@ class Database { } # Closes a database connection, if it is open + # Commits any open transactions # Returns success, true if already closed function close() { $this->mOpened = false; if ( $this->mConn ) { + if ( $this->trxLevel() ) { + $this->immediateCommit(); + } return mysql_close( $this->mConn ); } else { return true; @@ -184,7 +211,7 @@ class Database { $this->mLastQuery = $sql; - if ( $this->mDebug ) { + if ( $this->debug() ) { $sqlx = substr( $sql, 0, 500 ); $sqlx = wordwrap(strtr($sqlx,"\t\n"," ")); wfDebug( "SQL: $sqlx\n" ); @@ -196,26 +223,37 @@ class Database { $commentedSql = $sql; } - if( $this->mBufferResults ) { - $ret = mysql_query( $commentedSql, $this->mConn ); - } else { - $ret = mysql_unbuffered_query( $commentedSql, $this->mConn ); + # If DBO_TRX is set, start a transaction + if ( ( $this->mFlags & DBO_TRX ) && !$this->trxLevel() && $sql != 'BEGIN' ) { + $this->begin(); } - + + # Do the query and handle errors + $ret = $this->doQuery( $commentedSql ); if ( false === $ret ) { - $this->reportQueryError( mysql_error(), mysql_errno(), $sql, $fname, $tempIgnore ); + $this->reportQueryError( $this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore ); } - + if ( $wgProfiling ) { wfProfileOut( $profName ); } return $ret; } + # The DBMS-dependent part of query() + function doQuery( $sql ) { + if( $this->bufferResults() ) { + $ret = mysql_query( $sql, $this->mConn ); + } else { + $ret = mysql_unbuffered_query( $sql, $this->mConn ); + } + return $ret; + } + function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) { global $wgCommandLineMode, $wgFullyInitialised; # Ignore errors during error handling to avoid infinite recursion - $ignore = $this->setIgnoreErrors( true ); + $ignore = $this->ignoreErrors( true ); if( $ignore || $tempIgnore ) { wfDebug("SQL ERROR (ignored): " . $error . "\n"); @@ -237,7 +275,7 @@ class Database { $this->mOut->databaseError( $fname, $sql, $error, $errno ); } } - $this->setIgnoreErrors( $ignore ); + $this->ignoreErrors( $ignore ); } function freeResult( $res ) { @@ -470,10 +508,9 @@ class Database { function tableExists( $table ) { $table = $this->tableName( $table ); - $old = $this->mIgnoreErrors; - $this->mIgnoreErrors = true; + $old = $this->ignoreErrors( true ); $res = $this->query( "SELECT 1 FROM $table LIMIT 1" ); - $this->mIgnoreErrors = $old; + $this->ignoreErrors( $old ); if( $res ) { $this->freeResult( $res ); return true; @@ -521,6 +558,11 @@ class Database { # If errors are explicitly ignored, returns success function insert( $table, $a, $fname = "Database::insert", $options = array() ) { + # No rows to insert, easy just return now + if ( !count( $a ) ) { + return true; + } + $table = $this->tableName( $table ); if ( !is_array( $options ) ) { $options = array( $options ); @@ -532,7 +574,7 @@ class Database { $multi = false; $keys = array_keys( $a ); } - + $sql = 'INSERT ' . implode( ' ', $options ) . " INTO $table (" . implode( ',', $keys ) . ') VALUES '; @@ -784,7 +826,7 @@ class Database { $this->query( "BEGIN", $myFname ); $args = func_get_args(); $function = array_shift( $args ); - $oldIgnore = $dbw->setIgnoreErrors( true ); + $oldIgnore = $dbw->ignoreErrors( true ); $tries = DEADLOCK_TRIES; if ( is_array( $function ) ) { $fname = $function[0]; @@ -806,7 +848,7 @@ class Database { } } } while( $dbw->wasDeadlock && --$tries > 0 ); - $this->setIgnoreErrors( $oldIgnore ); + $this->ignoreErrors( $oldIgnore ); if ( $tries <= 0 ) { $this->query( "ROLLBACK", $myFname ); $this->reportQueryError( $error, $errno, $sql, $fname ); @@ -851,6 +893,43 @@ class Database { return array( false, false ); } } + + # Begin a transaction, or if a transaction has already started, continue it + function begin( $fname = 'Database::begin' ) { + if ( !$this->mTrxLevel ) { + $this->immediateBegin( $fname ); + } else { + $this->mTrxLevel++; + } + } + + # End a transaction, or decrement the nest level if transactions are nested + function commit( $fname = 'Database::commit' ) { + if ( $this->mTrxLevel ) { + $this->mTrxLevel--; + } + if ( !$this->mTrxLevel ) { + $this->immediateCommit( $fname ); + } + } + + # Rollback a transaction + function rollback( $fname = 'Database::rollback' ) { + $this->query( 'ROLLBACK', $fname ); + $this->mTrxLevel = 0; + } + + # Begin a transaction, committing any previously open transaction + function immediateBegin( $fname = 'Database::immediateBegin' ) { + $this->query( 'BEGIN', $fname ); + $this->mTrxLevel = 1; + } + + # Commit transaction, if one is open + function immediateCommit( $fname = 'Database::immediateCommit' ) { + $this->query( 'COMMIT', $fname ); + $this->mTrxLevel = 0; + } } class DatabaseMysql extends Database { @@ -887,7 +966,7 @@ function wfEmergencyAbort( &$conn, $error ) { $search = $_REQUEST['search']; echo wfMsgNoDB( "searchdisabled" ); echo wfMsgNoDB( "googlesearch", htmlspecialchars( $search ), $wgInputEncoding ); - wfAbruptExit(); + wfErrorExit(); } else { $t = Title::newFromText( wfMsgNoDB( "mainpage" ) ); } @@ -907,7 +986,7 @@ function wfEmergencyAbort( &$conn, $error ) { } echo $text; - wfAbruptExit(); + wfErrorExit(); } function wfLimitResult( $limit, $offset ) { diff --git a/includes/DatabasePostgreSQL.php b/includes/DatabasePostgreSQL.php index 06a79aa47f..36b622ceaf 100644 --- a/includes/DatabasePostgreSQL.php +++ b/includes/DatabasePostgreSQL.php @@ -18,17 +18,15 @@ class DatabasePgsql extends Database { var $mInsertId = NULL; function DatabasePgsql($server = false, $user = false, $password = false, $dbName = false, - $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false) + $failFunction = false, $flags = 0, $tablePrefix = 'get from global' ) { - Database::Database( $server, $user, $password, $dbName, $failFunction, $debug, - $bufferResults, $ignoreErrors ); + Database::Database( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix ); } - /* static */ function newFromParams( $server, $user, $password, $dbName, - $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false ) + /* static */ function newFromParams( $server = false, $user = false, $password = false, $dbName = false, + $failFunction = false, $flags = 0, $tablePrefix = 'get from global' ) { - return new DatabasePgsql( $server, $user, $password, $dbName, $failFunction, $debug, - $bufferResults, $ignoreErrors ); + return new DatabasePgsql( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix ); } # Usually aborts on failure @@ -74,53 +72,10 @@ class DatabasePgsql extends Database { } } - # Usually aborts on failure - # If errors are explicitly ignored, returns success - function query( $sql, $fname = "", $tempIgnore = false ) - { - global $wgProfiling; - - if ( $wgProfiling ) { - # generalizeSQL will probably cut down the query to reasonable - # logging size most of the time. The substr is really just a sanity check. - $profName = "query: " . substr( Database::generalizeSQL( $sql ), 0, 255 ); - wfProfileIn( $profName ); - } - - $this->mLastQuery = $sql; - - if ( $this->mDebug ) { - $sqlx = substr( $sql, 0, 500 ); - $sqlx = wordwrap(strtr($sqlx,"\t\n"," ")); - wfDebug( "SQL: $sqlx\n" ); - } - - $ret = pg_query( $this->mConn , $sql); - $this->mLastResult = $ret; - if ( false == $ret ) { - // Ignore errors during error handling to prevent infinite recursion - $ignore = $this->setIgnoreErrors( true ); - $error = pg_last_error( $this->mConn ); - // TODO FIXME : no error number function in postgre - // $errno = mysql_errno( $this->mConn ); - if( $ignore || $tempIgnore ) { - wfDebug("SQL ERROR (ignored): " . $error . "\n"); - } else { - wfDebug("SQL ERROR: " . $error . "\n"); - if ( $this->mOut ) { - // this calls wfAbruptExit() - $this->mOut->databaseError( $fname, $sql, $error, 0 ); - } - } - $this->setIgnoreErrors( $ignore ); - } - - if ( $wgProfiling ) { - wfProfileOut( $profName ); - } - return $ret; + function doQuery( $sql ) { + return pg_query( $this->mConn , $sql); } - + function queryIgnore( $sql, $fname = "" ) { return $this->query( $sql, $fname, true ); } @@ -168,6 +123,8 @@ class DatabasePgsql extends Database { function dataSeek( $res, $row ) { return pg_result_seek( $res, $row ); } function lastError() { return pg_last_error(); } + function lastErrno() { return 1; } + function affectedRows() { return pg_affected_rows( $this->mLastResult ); } @@ -215,14 +172,14 @@ class DatabasePgsql extends Database { # IGNORE is performed using single-row inserts, ignoring errors in each if ( in_array( 'IGNORE', $options ) ) { # FIXME: need some way to distiguish between key collision and other types of error - $oldIgnore = $this->setIgnoreErrors( true ); + $oldIgnore = $this->ignoreErrors( true ); if ( !is_array( reset( $a ) ) ) { $a = array( $a ); } foreach ( $a as $row ) { parent::insertArray( $table, $row, $fname, array() ); } - $this->setIgnoreErrors( $oldIgnore ); + $this->ignoreErrors( $oldIgnore ); $retVal = true; } else { $retVal = parent::insertArray( $table, $a, $fname, array() ); diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 905518e42b..1b8d94457d 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -70,6 +70,13 @@ $wgDBprefix = ''; # Table name prefix # password: DB password # type: "mysql" or "pgsql" # load: ratio of DB_SLAVE load, must be >=0, the sum of all loads must be >0 +# flags: bit field +# DBO_DEFAULT -- turns on DBO_TRX only if !$wgCommandLineMode (recommended) +# DBO_DEBUG -- equivalent of $wgDebugDumpSql +# DBO_TRX -- wrap entire request in a transaction +# DBO_IGNORE -- ignore errors (not useful in LocalSettings.php) +# DBO_NOBUFFER -- turn off buffering (not useful in LocalSettings.php) +# # Leave at false to use the single-server variables above $wgDBservers = false; @@ -139,6 +146,7 @@ $wgReadOnlyFile = "{$wgUploadDirectory}/lock_yBgMBwiR"; # used, as it may contain private data. $wgDebugLogFile = ''; $wgDebugRedirects = false; +$wgDebugRawPage = false; # Avoid overlapping debug entries by leaving out CSS $wgDebugComments = false; $wgReadOnly = false; @@ -160,7 +168,7 @@ $wgUseCategoryMagic = true; # FIXME: need fixing $wgUseCategoryBrowser = false; -$wgEnablePersistentLC = false; # Persistent link cache in linkscc table; FAILS on MySQL 3.x +$wgEnablePersistentLC = false; # Obsolete, do not use $wgCompressedPersistentLC = true; # use gzcompressed blobs $wgEnableParserCache = false; # requires that php was compiled --with-zlib @@ -445,4 +453,8 @@ $wgAllowPageInfo = false; # Maximum indent level of toc. $wgMaxTocLevel = 999; + +# Recognise longitude/latitude coordinates +$wgUseGeoMode = false; + ?> diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index a00ba73a95..3cf9ebe33f 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -219,8 +219,13 @@ function wfUtf8ToHTML($string) { function wfDebug( $text, $logonly = false ) { - global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly; + global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage; + # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet + if ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' && !$wgDebugRawPage ) { + return; + } + if ( isset( $wgOut ) && $wgDebugComments && !$logonly ) { $wgOut->debug( $text ); } @@ -240,7 +245,7 @@ function wfLogDBError( $text ) { function logProfilingData() { - global $wgRequestTime, $wgDebugLogFile; + global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest; global $wgProfiling, $wgProfileStack, $wgProfileLimit, $wgUser; $now = wfTime(); @@ -263,7 +268,7 @@ function logProfilingData() $log = sprintf( "%s\t%04.3f\t%s\n", gmdate( 'YmdHis' ), $elapsed, urldecode( $_SERVER['REQUEST_URI'] . $forward ) ); - if ( '' != $wgDebugLogFile ) { + if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) { error_log( $log . $prof, 3, $wgDebugLogFile ); } } @@ -360,7 +365,9 @@ function wfGo( $s ) } # Just like exit() but makes a note of it. -function wfAbruptExit(){ +# Commits open transactions except if the error parameter is set +function wfAbruptExit( $error = false ){ + global $wgLoadBalancer; static $called = false; if ( $called ){ exit(); @@ -377,9 +384,16 @@ function wfAbruptExit(){ } else { wfDebug('WARNING: Abrupt exit\n'); } + if ( !$error ) { + $wgLoadBalancer->closeAll(); + } exit(); } +function wfErrorExit() { + wfAbruptExit( true ); +} + function wfDebugDieBacktrace( $msg = '' ) { global $wgCommandLineMode; @@ -773,11 +787,17 @@ function wfSetVar( &$dest, $source ) return $temp; } -# Sets dest to a reference to source and returns the original dest -# Pity that doesn't work in PHP -function &wfSetRef( &$dest, &$source ) -{ - die( "You can't rebind a variable in the caller's scope" ); +# As for wfSetVar except setting a bit +function wfSetBit( &$dest, $bit, $state = true ) { + $temp = (bool)($dest & $bit ); + if ( !is_null( $state ) ) { + if ( $state ) { + $dest |= $bit; + } else { + $dest &= ~$bit; + } + } + return $temp; } # This function takes two arrays as input, and returns a CGI-style string, e.g. diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php index 31a1f486bf..50afb971ca 100644 --- a/includes/LinksUpdate.php +++ b/includes/LinksUpdate.php @@ -85,6 +85,7 @@ class LinksUpdate { } $sql .= $dbw->addQuotes( $badTitle ); } + $sql .= ")"; $dbw->query( $sql, $fname ); } } else { diff --git a/includes/LoadBalancer.php b/includes/LoadBalancer.php index b52cabb59d..adefa2ab1e 100644 --- a/includes/LoadBalancer.php +++ b/includes/LoadBalancer.php @@ -29,7 +29,7 @@ define( "MASTER_WAIT_TIMEOUT", 15 ); # Time to wait for a slave to synchronise class LoadBalancer { /* private */ var $mServers, $mConnections, $mLoads; /* private */ var $mFailFunction; - /* private */ var $mForce, $mReadIndex, $mLastConn; + /* private */ var $mForce, $mReadIndex, $mLastIndex; /* private */ var $mWaitForFile, $mWaitForPos; function LoadBalancer() @@ -39,7 +39,7 @@ class LoadBalancer { $this->mFailFunction = false; $this->mReadIndex = -1; $this->mForce = -1; - $this->mLastConn = false; + $this->mLastIndex = -1; } function newFromParams( $servers, $failFunction = false ) @@ -57,7 +57,7 @@ class LoadBalancer { $this->mWriteIndex = -1; $this->mForce = -1; $this->mConnections = array(); - $this->mLastConn = false; + $this->mLastIndex = 1; $this->mLoads = array(); $this->mWaitForFile = false; $this->mWaitForPos = false; @@ -92,13 +92,14 @@ class LoadBalancer { return $i; } - function &getReader() + function getReaderIndex() { + $i = false; if ( $this->mForce >= 0 ) { - $conn =& $this->getConnection( $this->mForce ); + $i = $this->mForce; } else { if ( $this->mReadIndex >= 0 ) { - $conn =& $this->getConnection( $this->mReadIndex ); + $i = $this->mReadIndex; } else { # $loads is $this->mLoads except with elements knocked out if they # don't work @@ -109,6 +110,8 @@ class LoadBalancer { wfDebug( "Using reader #$i: {$this->mServers[$i]['host']}\n" ); $conn =& $this->getConnection( $i ); + $this->mConnections[$i] =& $conn; + if ( !$conn->isOpen() ) { unset( $loads[$i] ); } @@ -116,14 +119,12 @@ class LoadBalancer { } while ( $i !== false && !$conn->isOpen() ); if ( $conn->isOpen() ) { $this->mReadIndex = $i; + } else { + $i = false; } } } - if ( $conn === false || !$conn->isOpen() ) { - $this->reportConnectionError( $conn ); - $conn = false; - } - return $conn; + return $i; } # Set the master wait position @@ -142,7 +143,10 @@ class LoadBalancer { $this->mWaitForPos = $pos; if ( $this->mReadIndex > 0 ) { - $this->doWait( $this->mReadIndex ); + if ( !$this->doWait( $this->mReadIndex ) ) { + # Use master instead + $this->mReadIndex = 0; + } } } @@ -156,7 +160,7 @@ class LoadBalancer { list( $file, $pos ) = explode( ' ', $memcPos ); # If the saved position is later than the requested position, return now if ( $file == $this->mWaitForFile && $this->mWaitForPos <= $pos ) { - return; + return true; } } @@ -166,16 +170,16 @@ class LoadBalancer { if ( $result == -1 || is_null( $result ) ) { # Timed out waiting for slave, use master instead - # This is not the ideal solution. If there are a large number of slaves, a slow - # replicated write query will cause the master to be swamped with reads. However - # that's a relatively graceful failure mode, so it will do for now. wfDebug( "Timed out waiting for slave #$index pos {$this->mWaitForFile} {$this->mWaitForPos}\n" ); - $this->mReadIndex = 0; + $retVal = false; } else { + $retVal = true; wfDebug( "Done\n" ); } + return $retVal; } + # Get a connection by index function &getConnection( $i, $fail = false ) { /* @@ -191,37 +195,55 @@ class LoadBalancer { }*/ # Operation-based index - # Note, getReader() and getWriter() will re-enter this function - if ( $i == DB_SLAVE ) { - $this->mLastConn =& $this->getReader(); + if ( $i == DB_SLAVE ) { + # Note: re-entrant + $i = $this->getReaderIndex(); } elseif ( $i == DB_MASTER ) { - $this->mLastConn =& $this->getWriter(); + $i = $this->getWriterIndex(); } elseif ( $i == DB_LAST ) { - # Just use $this->mLastConn, which should already be set - if ( $this->mLastConn === false ) { + # Just use $this->mLastIndex, which should already be set + $i = $this->mLastIndex; + if ( $i === -1 ) { # Oh dear, not set, best to use the writer for safety - $this->mLastConn =& $this->getWriter(); + $i = $this->getWriterIndex(); } - } else { - # Explicit index - if ( !array_key_exists( $i, $this->mConnections ) || !$this->mConnections[$i]->isOpen() ) { - $this->mConnections[$i] = $this->makeConnection( $this->mServers[$i] ); - if ( $i != 0 && $this->mWaitForFile ) { - $this->doWait( $i ); + } + # Now we have an explicit index into the servers array + if ( !$this->isOpen( $i ) ) { + $this->mConnections[$i] = $this->makeConnection( $this->mServers[$i] ); + + if ( $i != 0 && $this->mWaitForFile ) { + if ( !$this->doWait( $i ) ) { + # Error waiting for this slave, use master instead + $this->mReadIndex = 0; + $i = 0; + if ( !$this->isOpen( 0 ) ) { + $this->mConnections[0] = $this->makeConnection( $this->mServers[0] ); + } + wfDebug( "Failed over to {$this->mConnections[0]->mServer}\n" ); } } - if ( !$this->mConnections[$i]->isOpen() ) { - wfDebug( "Failed to connect to database $i at {$this->mServers[$i]['host']}\n" ); - if ( $fail ) { - $this->reportConnectionError( $this->mConnections[$i] ); - } - $this->mConnections[$i] = false; + } + if ( !$this->isOpen( $i ) ) { + wfDebug( "Failed to connect to database $i at {$this->mServers[$i]['host']}\n" ); + if ( $fail ) { + $this->reportConnectionError( $this->mConnections[$i] ); } - $this->mLastConn =& $this->mConnections[$i]; + $this->mConnections[$i] = false; } - return $this->mLastConn; + $this->mLastIndex = $i; + + return $this->mConnections[$i]; } + /* private */ function isOpen( $index ) { + if ( array_key_exists( $index, $this->mConnections ) && $this->mConnections[$index]->isOpen() ) { + return true; + } else { + return false; + } + } + /* private */ function makeConnection( &$server ) { extract( $server ); # Get class for this database type @@ -231,7 +253,7 @@ class LoadBalancer { } # Create object - return new $class( $host, $user, $password, $dbname, 1 ); + return new $class( $host, $user, $password, $dbname, 1, $flags ); } function reportConnectionError( &$conn ) @@ -247,14 +269,9 @@ class LoadBalancer { $conn->reportConnectionError(); } - function &getWriter() + function getWriterIndex() { - $c =& $this->getConnection( 0 ); - if ( $c === false || !$c->isOpen() ) { - $this->reportConnectionError( $c ); - $c = false; - } - return $c; + return 0; } function force( $i ) @@ -300,4 +317,21 @@ class LoadBalancer { $this->waitFor( $_SESSION['master_log_file'], $_SESSION['master_pos'] ); } } + + # Close all open connections + function closeAll() { + foreach( $this->mConnections as $i => $conn ) { + if ( $this->isOpen( $i ) ) { + $conn->close(); + } + } + } + + function commitAll() { + foreach( $this->mConnections as $i => $conn ) { + if ( $this->isOpen( $i ) ) { + $conn->immediateCommit(); + } + } + } } diff --git a/includes/Namespace.php b/includes/Namespace.php index e3fdab57a9..a72d24adf7 100644 --- a/includes/Namespace.php +++ b/includes/Namespace.php @@ -4,29 +4,7 @@ # "magic" behaviors of them based on index. The textual # names of the namespaces are handled by Language.php. -# Virtual namespaces; these don't appear in the page database: -define("NS_MEDIA", -2); -define("NS_SPECIAL", -1); - -# Real namespaces: -define("NS_MAIN", 0); -define("NS_TALK", 1); -define("NS_USER", 2); -define("NS_USER_TALK", 3); -define("NS_WP", 4); -define("NS_WIKIPEDIA", 4); -define("NS_WP_TALK", 5); -define("NS_WIKIPEDIA_TALK", 5); -define("NS_IMAGE", 6); -define("NS_IMAGE_TALK", 7); -define("NS_MEDIAWIKI", 8); -define("NS_MEDIAWIKI_TALK", 9); -define("NS_TEMPLATE", 10); -define("NS_TEMPLATE_TALK", 11); -define("NS_HELP", 12); -define("NS_HELP_TALK", 13); -define("NS_CATEGORY", 14); -define("NS_CATEGORY_TALK", 15); +# Definitions of the NS_ constants are in Defines.php # These are synonyms for the names given in the language file # Users and translators should not change them diff --git a/includes/OutputPage.php b/includes/OutputPage.php index 5f6fc1ef37..e58974e493 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -465,13 +465,14 @@ class OutputPage { $this->setRobotpolicy( "noindex,nofollow" ); $this->setArticleRelated( false ); $this->enableClientCache( false ); + $this->mRedirect = ""; $this->mBodytext = ""; $this->addHTML( "

" . wfMsg( $msg ) . "

\n" ); $this->returnToMain( false ); $this->output(); - wfAbruptExit(); + wfErrorExit(); } function sysopRequired() @@ -534,6 +535,7 @@ class OutputPage { $this->setRobotpolicy( "noindex,nofollow" ); $this->setArticleRelated( false ); $this->enableClientCache( false ); + $this->mRedirect = ""; if ( $wgCommandLineMode ) { $msg = wfMsgNoDB( "dberrortextcl" ); @@ -548,7 +550,7 @@ class OutputPage { if ( $wgCommandLineMode || !is_object( $wgUser )) { print "$msg\n"; - wfAbruptExit(); + wfErrorExit(); } $sk = $wgUser->getSkin(); $shlink = $sk->makeKnownLink( wfMsgNoDB( "searchhelppage" ), @@ -556,7 +558,7 @@ class OutputPage { $msg = str_replace( "$5", $shlink, $msg ); $this->mBodytext = $msg; $this->output(); - wfAbruptExit(); + wfErrorExit(); } function readOnlyPage( $source = null, $protected = false ) @@ -595,10 +597,11 @@ class OutputPage { $this->setRobotpolicy( "noindex,nofollow" ); $this->setArticleRelated( false ); $this->enableClientCache( false ); + $this->mRedirect = ""; $this->mBodytext = $message; $this->output(); - wfAbruptExit(); + wfErrorExit(); } function unexpectedValueError( $name, $val ) diff --git a/includes/Setup.php b/includes/Setup.php index 747d1c393b..02ae3689c5 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -72,7 +72,7 @@ global $wgMemc, $wgMagicWords, $wgMwRedir, $wgDebugLogFile; global $wgMessageCache, $wgUseMemCached, $wgUseDatabaseMessages; global $wgMsgCacheExpiry, $wgCommandLineMode; global $wgBlockCache, $wgParserCache, $wgParser, $wgDBConnections; -global $wgLoadBalancer, $wgDBservers; +global $wgLoadBalancer, $wgDBservers, $wgDebugDumpSql; global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype; global $wgFullyInitialised; @@ -157,7 +157,8 @@ if ( !$wgDBservers ) { 'password' => $wgDBpassword, 'dbname' => $wgDBname, 'type' => $wgDBtype, - 'load' => 1 + 'load' => 1, + 'flags' => ($wgDebugDumpSql ? DBO_DEBUG : 0) | DBO_DEFAULT )); } $wgLoadBalancer = LoadBalancer::newFromParams( $wgDBservers ); diff --git a/includes/SpecialPage.php b/includes/SpecialPage.php index dd4d4c2137..9cccc3f7ef 100644 --- a/includes/SpecialPage.php +++ b/includes/SpecialPage.php @@ -37,8 +37,8 @@ $wgSpecialPages = array_merge($wgSpecialPages, array ( "Recentchangeslinked" => new UnlistedSpecialPage( "Recentchangeslinked" ), "Movepage" => new UnlistedSpecialPage( "Movepage" ), "Blockme" => new UnlistedSpecialPage( "Blockme" ), - "Geo" => new SpecialPage( "Geo" ), - "Validate" => new SpecialPage( "Validate" ), + "Geo" => new UnlistedSpecialPage( "Geo" ), + "Validate" => new UnlistedSpecialPage( "Validate" ), "Booksources" => new SpecialPage( "Booksources" ), "Categories" => new SpecialPage( "Categories" ), "Export" => new SpecialPage( "Export" ), @@ -49,7 +49,7 @@ $wgSpecialPages = array_merge($wgSpecialPages, array ( "Asksql" => new SpecialPage( "Asksql", "sysop" ), "Undelete" => new SpecialPage( "Undelete", "sysop" ), "Makesysop" => new SpecialPage( "Makesysop", "sysop" ), - "Import" => new SpecialPage( "Import", "sysop" ), +# "Import" => new SpecialPage( "Import", "sysop" ), "Lockdb" => new SpecialPage( "Lockdb", "developer" ), "Unlockdb" => new SpecialPage( "Unlockdb", "developer" ) )); diff --git a/index.php b/index.php index 7680dbb3d7..85403d9d54 100644 --- a/index.php +++ b/index.php @@ -11,6 +11,7 @@ if(!file_exists("LocalSettings.php")) { } define( "MEDIAWIKI", true ); +require_once( "./includes/Defines.php" ); require_once( "./LocalSettings.php" ); require_once( "includes/Setup.php" ); @@ -54,8 +55,6 @@ if ( !is_null( $wgTitle ) && !$wgTitle->userCanRead() ) { exit; } -$db =& wfGetDB( DB_MASTER ); - if ( $search = $wgRequest->getText( 'search' ) ) { $wgTitle = Title::makeTitle( NS_SPECIAL, "Search" ); if( $wgRequest->getVal( 'fulltext' ) || !is_null( $wgRequest->getVal( 'offset' ) ) ) { @@ -97,7 +96,6 @@ if ( $search = $wgRequest->getText( 'search' ) ) { $wgArticle = new Article( $wgTitle ); } - $db->query("BEGIN"); switch( $action ) { case "view": $wgOut->setSquidMaxage( $wgSquidMaxage ); @@ -168,17 +166,23 @@ if ( $search = $wgRequest->getText( 'search' ) ) { default: $wgOut->errorpage( "nosuchaction", "nosuchactiontext" ); } - $db->query("COMMIT"); } -$wgLoadBalancer->saveMasterPos(); -$wgOut->output(); - +# Deferred updates aren't really deferred anymore. It's important to report errors to the +# user, and that means doing this before OutputPage::output(). Note that for page saves, +# the client will wait until the script exits anyway before following the redirect. foreach ( $wgDeferredUpdateList as $up ) { - $db->query("BEGIN"); $up->doUpdate(); - $db->query("COMMIT"); } + +$wgLoadBalancer->saveMasterPos(); + +# Now commit any transactions, so that unreported errors after output() don't roll back the whole thing +$wgLoadBalancer->commitAll(); + +$wgOut->output(); + logProfilingData(); +$wgLoadBalancer->closeAll(); wfDebug( "Request ended normally\n" ); ?> diff --git a/maintenance/refreshLinks.inc b/maintenance/refreshLinks.inc index 285aba764a..5a18a166a8 100644 --- a/maintenance/refreshLinks.inc +++ b/maintenance/refreshLinks.inc @@ -14,6 +14,10 @@ function refreshLinks( $start ) { # Don't generate TeX PNGs (lack of a sensible current directory causes errors anyway) $wgUser->setOption("math", 3); + + # Turn on link cache in skin + $sk =& $wgUser->getSkin(); + $sk->postParseLinkColour( false ); for ($id = $start; $id <= $end; $id++) { if ( !($id % REPORTING_INTERVAL) ) { diff --git a/update.php b/update.php index ce96eaba60..3bd3363a56 100644 --- a/update.php +++ b/update.php @@ -50,8 +50,7 @@ $wgTitle = Title::newFromText( "Update script" ); # print "Checking database for necessary updates...\n"; -$wgDatabase = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname, - 1, false, true, false); +$wgDatabase = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname, 1 ); if ( !$wgDatabase->isOpen() ) { print "Unable to connect to database: " . $wgDatabase->lastError() . "\n"; exit(); -- 2.20.1