Merge "Add $wgMaxUserDBWriteDuration to limit user-generated transactions"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 7 Dec 2015 21:25:24 +0000 (21:25 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 7 Dec 2015 21:25:24 +0000 (21:25 +0000)
RELEASE-NOTES-1.27
autoload.php
includes/DefaultSettings.php
includes/MediaWiki.php
includes/db/DatabaseError.php
languages/i18n/en.json
languages/i18n/qqq.json

index 8d0853e..ead82f3 100644 (file)
@@ -87,6 +87,8 @@ production.
 * Added CentralIdLookup, a service that allows extensions needing a concept of
   "central" users to get that without having to know about specific central
   authentication extensions.
+* $wgMaxUserDBWriteDuration added to limit huge user-generated transactions.
+  Regular web request transactions that takes longer than this are aborted.
 
 === External library changes in 1.27 ===
 ==== Upgraded external libraries ====
index 4e8b4e6..ed765b0 100644 (file)
@@ -288,6 +288,7 @@ $wgAutoloadLocalClasses = array(
        'DBQueryError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DBReadOnlyError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DBSiteStore' => __DIR__ . '/includes/site/DBSiteStore.php',
+       'DBTransactionError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DBUnexpectedError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DataUpdate' => __DIR__ . '/includes/deferred/DataUpdate.php',
        'Database' => __DIR__ . '/includes/db/Database.php',
index e76b627..ee8c8c7 100644 (file)
@@ -7811,6 +7811,15 @@ $wgSearchRunSuggestedQuery = true;
  */
 $wgPopularPasswordFile = __DIR__ . '/../serialized/commonpasswords.cdb';
 
+/*
+ * Max time (in seconds) a user-generated transaction can spend in writes.
+ * If exceeded, the transaction is rolled back with an error instead of being committed.
+ *
+ * @var int|bool Disabled if false
+ * @since 1.27
+ */
+$wgMaxUserDBWriteDuration = false;
+
 /**
  * For really cool vim folding this needs to be at the end:
  * vim: foldmarker=@{,@} foldmethod=marker
index ecfd8f8..c4ea536 100644 (file)
@@ -505,9 +505,25 @@ class MediaWiki {
                // Either all DBs should commit or none
                ignore_user_abort( true );
 
-               // Commit all changes and record ChronologyProtector positions
+               $config = $context->getConfig();
+
                $factory = wfGetLBFactory();
+               // Check if any transaction was too big
+               $limit = $config->get( 'MaxUserDBWriteDuration' );
+               $factory->forEachLB( function ( LoadBalancer $lb ) use ( $limit ) {
+                       $lb->forEachOpenConnection( function ( IDatabase $db ) use ( $limit ) {
+                               $time = $db->pendingWriteQueryDuration();
+                               if ( $limit > 0 && $time > $limit ) {
+                                       throw new DBTransactionError(
+                                               $db,
+                                               wfMessage( 'transaction-duration-limit-exceeded', $time, $limit )->plain()
+                                       );
+                               }
+                       } );
+               } );
+               // Commit all changes
                $factory->commitMasterChanges();
+               // Record ChronologyProtector positions
                $factory->shutdown();
                wfDebug( __METHOD__ . ': all transactions committed' );
 
@@ -517,7 +533,6 @@ class MediaWiki {
                // Set a cookie to tell all CDN edge nodes to "stick" the user to the
                // DC that handles this POST request (e.g. the "master" data center)
                $request = $context->getRequest();
-               $config = $context->getConfig();
                if ( $request->wasPosted() && $factory->hasOrMadeRecentMasterChanges() ) {
                        $expires = time() + $config->get( 'DataCenterUpdateStickTTL' );
                        $request->response()->setCookie( 'UseDC', 'master', $expires, array( 'prefix' => '' ) );
index 78d26ae..4993eac 100644 (file)
@@ -464,3 +464,9 @@ class DBReadOnlyError extends DBExpectedError {
                return $this->msg( 'readonly', 'Database is locked' );
        }
 }
+
+/**
+ * @ingroup Database
+ */
+class DBTransactionError extends DBExpectedError {
+}
index 4d66f44..70a2b80 100644 (file)
        "databaseerror-query": "Query: $1",
        "databaseerror-function": "Function: $1",
        "databaseerror-error": "Error: $1",
+       "transaction-duration-limit-exceeded": "In order to avoid creating high replication lag, this transaction was aborted because the write duration ($1) exceeded the $2 second limit.\nIf you are changing many items at once, trying doing multiple smaller operations instead.",
        "laggedslavemode": "<strong>Warning:</strong> Page may not contain recent updates.",
        "readonly": "Database locked",
        "enterlockreason": "Enter a reason for the lock, including an estimate of when the lock will be released",
        "spam_blanking": "All revisions contained links to $1, blanking",
        "spam_deleting": "All revisions contained links to $1, deleting",
        "simpleantispam-label": "Anti-spam check.\nDo <strong>not</strong> fill this in!",
-       "autochange-username": "MediaWiki automatic change",
        "pageinfo-header": "-",
        "pageinfo-title": "Information for \"$1\"",
        "pageinfo-not-current": "Sorry, it's impossible to provide this information for old revisions.",
index b8e7791..fc72a4b 100644 (file)
        "databaseerror-query": "Identifies, in the list of technical details, the [[wikipedia:SQL|SQL]] statement that failed.\nParameters:\n* $1 - SQL statement (shown within a box)\n{{Identical|Query}}",
        "databaseerror-function": "Identifies, in the list of technical details, the function that tried to execute the database query.\nParameters:\n* $1 - Name of function\n{{Identical|Function}}",
        "databaseerror-error": "Identifies, in the list of technical details, the error message the database server returned.\nParameters:\n* $1 - Error message from the database server, probably in English\n{{Identical|Error}}",
+       "transaction-duration-limit-exceeded": "Plain text error shown when DB updates take too long. Parameters:\n* $1 - time spent in database updates\n* $2 - maximum time allowed in database updates",
        "laggedslavemode": "Used as warning when getting the timestamp of the latest version, if in LaggedSlaveMode.",
        "readonly": "Used as title of error message when database is locked.",
        "enterlockreason": "For developers when locking the database",