SpecialContributions: Use PoolCounter to limit concurrency
authorBrad Jorsch <bjorsch@wikimedia.org>
Tue, 19 Nov 2019 19:36:35 +0000 (14:36 -0500)
committerReedy <reedy@wikimedia.org>
Tue, 2 Jun 2020 13:29:08 +0000 (14:29 +0100)
Allow using PoolCounter to limit the number of times a user or IP can
concurrently load Special:Contributions.

By default no limitation is applied. Key 'SpecialContributions' in
$wgPoolCounterConf must be set to configure the concurrency.

Bug: T234450
Change-Id: Ie769fa170093bfb6d281c651d3857545d139e009

RELEASE-NOTES-1.31
includes/specials/SpecialContributions.php
languages/i18n/en.json
languages/i18n/qqq.json

index bde7d57..88f43aa 100644 (file)
@@ -3,6 +3,8 @@
 THIS IS NOT A RELEASE YET
 
 === Changes since MediaWiki 1.31.7 ===
+* Per-user concurrency in SpecialContributions can now be limited by setting
+  $wgPoolCounterConf['SpecialContributions'] appropriately.
 
 == MediaWiki 1.31.7 ==
 
index 6fc8306..2812541 100644 (file)
@@ -230,20 +230,39 @@ class SpecialContributions extends IncludableSpecialPage {
                        } elseif ( !$pager->getNumRows() ) {
                                $out->addWikiMsg( 'nocontribs', $target );
                        } else {
-                               # Show a message about replica DB lag, if applicable
-                               $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
-                               $lag = $lb->safeGetLag( $pager->getDatabase() );
-                               if ( $lag > 0 ) {
-                                       $out->showLagWarning( $lag );
-                               }
-
-                               $output = $pager->getBody();
-                               if ( !$this->including() ) {
-                                       $output = '<p>' . $pager->getNavigationBar() . '</p>' .
-                                               $output .
-                                               '<p>' . $pager->getNavigationBar() . '</p>';
+                               // @todo We just want a wiki ID here, not a "DB domain", but
+                               // current status of MediaWiki conflates the two. See T235955.
+                               $poolKey = WikiMap::getCurrentWikiDbDomain() . ':SpecialContributions:';
+                               if ( $this->getUser()->isAnon() ) {
+                                       $poolKey .= 'a:' . $this->getUser()->getName();
+                               } else {
+                                       $poolKey .= 'u:' . $this->getUser()->getId();
                                }
-                               $out->addHTML( $output );
+                               $work = new PoolCounterWorkViaCallback( 'SpecialContributions', $poolKey, [
+                                       'doWork' => function () use ( $pager, $out ) {
+                                               # Show a message about replica DB lag, if applicable
+                                               $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+                                               $lag = $lb->safeGetLag( $pager->getDatabase() );
+                                               if ( $lag > 0 ) {
+                                                       $out->showLagWarning( $lag );
+                                               }
+
+                                               $output = $pager->getBody();
+                                               if ( !$this->including() ) {
+                                                       $output = '<p>' . $pager->getNavigationBar() . '</p>' .
+                                                               $output .
+                                                               '<p>' . $pager->getNavigationBar() . '</p>';
+                                               }
+                                               $out->addHTML( $output );
+                                       },
+                                       'error' => function () use ( $out ) {
+                                               $msg = $this->getUser()->isAnon()
+                                                       ? 'sp-contributions-concurrency-ip'
+                                                       : 'sp-contributions-concurrency-user';
+                                               $out->wrapWikiMsg( "<div class='errorbox'>\n$1\n</div>", $msg );
+                                       }
+                               ] );
+                               $work->execute();
                        }
 
                        $out->preventClickjacking( $pager->getPreventClickjacking() );
index 8676238..13920c0 100644 (file)
        "sp-contributions-footer-anon-range": "-",
        "sp-contributions-footer-newbies": "-",
        "sp-contributions-outofrange": "Unable to show any results. The requested IP range is larger than the CIDR limit of /$1.",
+       "sp-contributions-concurrency-user": "Sorry, too many requests are being made from your user account. Please try again later.",
+       "sp-contributions-concurrency-ip": "Sorry, too many requests are being made from your IP address. Please try again later.",
        "whatlinkshere": "What links here",
        "whatlinkshere-title": "Pages that link to \"$1\"",
        "whatlinkshere-summary": "",
index 576c2bf..e745375 100644 (file)
        "sp-contributions-footer-anon-range": "{{ignored}}This is the footer for IP ranges on [[Special:Contributions]].",
        "sp-contributions-footer-newbies": "{{ignored}}This is the footer for newbie users on [[Special:Contributions]].",
        "sp-contributions-outofrange": "Message shown when a user tries to view contributions of an IP range that's too large. $1 is the numerical limit imposed on the CIDR range.",
+       "sp-contributions-concurrency-user": "Message shown when a logged-in user tries to load [[Special:Contributions]] too many times at once.",
+       "sp-contributions-concurrency-ip": "Message shown when a logged-out user tries to load [[Special:Contributions]] too many times at once.",
        "whatlinkshere": "The text of the link in the toolbox (on the left, below the search menu) going to [[Special:WhatLinksHere]].\n\nSee also:\n* {{msg-mw|Whatlinkshere}}\n* {{msg-mw|Accesskey-t-whatlinkshere}}\n* {{msg-mw|Tooltip-t-whatlinkshere}}",
        "whatlinkshere-title": "Title of the special page [[Special:WhatLinksHere]]. This page appears when you click on the 'What links here' button in the toolbox. $1 is the name of the page concerned.",
        "whatlinkshere-summary": "{{doc-specialpagesummary|whatlinkshere}}",