Merge "Protect WAN cache sets() against uncommitted data"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 27 Oct 2015 10:18:52 +0000 (10:18 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 27 Oct 2015 10:18:52 +0000 (10:18 +0000)
1  2 
includes/DefaultSettings.php
includes/db/Database.php
includes/objectcache/ObjectCache.php

@@@ -2212,7 -2212,7 +2212,7 @@@ $wgObjectCaches = array
  
        CACHE_ANYTHING => array( 'factory' => 'ObjectCache::newAnything' ),
        CACHE_ACCEL => array( 'factory' => 'ObjectCache::newAccelerator' ),
 -      CACHE_MEMCACHED => array( 'factory' => 'ObjectCache::newMemcached', 'loggroup' => 'memcached' ),
 +      CACHE_MEMCACHED => array( 'class' => 'MemcachedPhpBagOStuff', 'loggroup' => 'memcached' ),
  
        'db-replicated' => array(
                'class'       => 'ReplicatedBagOStuff',
@@@ -2263,7 -2263,8 +2263,8 @@@ $wgMainWANCache = false
   * the value is an associative array of parameters. The "cacheId" parameter is
   * a cache identifier from $wgObjectCaches. The "relayerConfig" parameter is an
   * array used to construct an EventRelayer object. The "pool" parameter is a
-  * string that is used as a PubSub channel prefix.
+  * string that is used as a PubSub channel prefix. The "loggroup" parameter
+  * controls where log events are sent.
   *
   * @since 1.26
   */
@@@ -5202,85 -5203,80 +5203,85 @@@ $wgApplyIpBlocksToXff = false
   * @par Example:
   * To set a generic maximum of 4 hits in 60 seconds:
   * @code
 - * $wgRateLimits = array( 4, 60 );
 + *     $wgRateLimits = array( 4, 60 );
   * @endcode
   *
 - * You could also limit per action and then type of users. See the inline
 - * code for a template to use.
 - *
 - * This option set is experimental and likely to change.
 + * @par Example:
 + * You could also limit per action and then type of users.
 + * @code
 + *     $wgRateLimits = array(
 + *         'edit' => array(
 + *             'anon' => array( x, y ), // any and all anonymous edits (aggregate)
 + *             'user' => array( x, y ), // each logged-in user
 + *             'newbie' => array( x, y ), // each new autoconfirmed accounts; overrides 'user'
 + *             'ip' => array( x, y ), // each anon and recent account
 + *             'subnet' => array( x, y ), // ... within a /24 subnet in IPv4 or /64 in IPv6
 + *         )
 + *     )
 + * @endcode
   *
 - * @warning Requires memcached.
 + * @warning Requires that $wgMainCacheType is set to something persistent
   */
  $wgRateLimits = array(
 +      // Page edits
        'edit' => array(
 -              'anon' => null, // for any and all anonymous edits (aggregate)
 -              'user' => null, // for each logged-in user
 -              'newbie' => null, // for each recent (autoconfirmed) account; overrides 'user'
 -              'ip' => null, // for each anon and recent account
 -              'subnet' => null, // ... within a /24 subnet in IPv4 or /64 in IPv6
 +              'ip' => array( 8, 60 ),
 +              'newbie' => array( 8, 60 ),
        ),
 +      // Page moves
 +      'move' => array(
 +              'newbie' => array( 2, 120 ),
 +              'user' => array( 8, 60 ),
 +      ),
 +      // File uploads
        'upload' => array(
 -              'user' => null,
 -              'newbie' => null,
 -              'ip' => null,
 -              'subnet' => null,
 +              'ip' => array( 8, 60 ),
 +              'newbie' => array( 8, 60 ),
        ),
 -      'move' => array(
 -              'user' => null,
 -              'newbie' => null,
 -              'ip' => null,
 -              'subnet' => null,
 +      // Page rollbacks
 +      'rollback' => array(
 +              'user' => array( 10, 60 ),
 +              'newbie' => array( 5, 120 )
        ),
 -      'mailpassword' => array( // triggering password resets emails
 -              'anon' => null,
 +      // Triggering password resets emails
 +      'mailpassword' => array(
 +              'ip' => array( 5, 3600 ),
        ),
 -      'emailuser' => array( // emailing other users using MediaWiki
 -              'user' => null,
 +      // Emailing other users using MediaWiki
 +      'emailuser' => array(
 +              'ip' => array( 5, 86400 ),
 +              'newbie' => array( 5, 86400 ),
 +              'user' => array( 20, 86400 ),
        ),
 -      'linkpurge' => array( // purges of link tables
 -              'anon' => null,
 -              'user' => null,
 -              'newbie' => null,
 -              'ip' => null,
 -              'subnet' => null,
 +      // Purging pages
 +      'purge' => array(
 +              'ip' => array( 30, 60 ),
 +              'user' => array( 30, 60 ),
        ),
 -      'renderfile' => array( // files rendered via thumb.php or thumb_handler.php
 -              'anon' => null,
 -              'user' => null,
 -              'newbie' => null,
 -              'ip' => null,
 -              'subnet' => null,
 +      // Purges of link tables
 +      'linkpurge' => array(
 +              'ip' => array( 30, 60 ),
 +              'user' => array( 30, 60 ),
        ),
 -      'renderfile-nonstandard' => array( // same as above but for non-standard thumbnails
 -              'anon' => null,
 -              'user' => null,
 -              'newbie' => null,
 -              'ip' => null,
 -              'subnet' => null,
 +      // Files rendered via thumb.php or thumb_handler.php
 +      'renderfile' => array(
 +              'ip' => array( 700, 30 ),
 +              'user' => array( 700, 30 ),
        ),
 -      'stashedit' => array( // stashing edits into cache before save
 -              'anon' => null,
 -              'user' => null,
 -              'newbie' => null,
 -              'ip' => null,
 -              'subnet' => null,
 +      // Same as above but for non-standard thumbnails
 +      'renderfile-nonstandard' => array(
 +              'ip' => array( 70, 30 ),
 +              'user' => array( 70, 30 ),
        ),
 -      'changetag' => array( // adding or removing change tags
 -              'user' => null,
 -              'newbie' => null,
 +      // Stashing edits into cache before save
 +      'stashedit' => array(
 +              'ip' => array( 30, 60 ),
 +              'newbie' => array( 30, 60 ),
        ),
 -      'purge' => array( // purging pages
 -              'anon' => null,
 -              'user' => null,
 -              'newbie' => null,
 -              'ip' => null,
 -              'subnet' => null,
 +      // Adding or removing change tags
 +      'changetag' => array(
 +              'ip' => array( 8, 60 ),
 +              'newbie' => array( 8, 60 ),
        ),
  );
  
@@@ -5512,7 -5508,7 +5513,7 @@@ $wgTrxProfilerLimits = array
        'JobRunner' => array(
                'readQueryTime' => 30,
                'writeQueryTime' => 5,
 -              'maxAffected' => 500
 +              'maxAffected' => 1000
        ),
        // Command-line scripts
        'Maintenance' => array(
@@@ -6741,7 -6737,6 +6742,7 @@@ $wgJobClasses = array
        'ThumbnailRender' => 'ThumbnailRenderJob',
        'recentChangesUpdate' => 'RecentChangesUpdateJob',
        'refreshLinksPrioritized' => 'RefreshLinksJob', // for cascading protection
 +      'refreshLinksDynamic' => 'RefreshLinksJob', // for pages with dynamic content
        'activityUpdateJob' => 'ActivityUpdateJob',
        'enqueue' => 'EnqueueJob', // local queue for multi-DC setups
        'null' => 'NullJob'
diff --combined includes/db/Database.php
@@@ -441,6 -441,14 +441,14 @@@ abstract class DatabaseBase implements 
                return $this->mDoneWrites ?: false;
        }
  
+       /**
+        * @return bool Whether there is a transaction open with possible write queries
+        * @since 1.27
+        */
+       public function writesPending() {
+               return $this->mTrxLevel && $this->mTrxDoneWrites;
+       }
        /**
         * Returns true if there is a transaction open with possible write
         * queries or transaction pre-commit/idle callbacks waiting on it to finish.
        function __construct( array $params ) {
                global $wgDBprefix, $wgDBmwschema, $wgCommandLineMode;
  
 -              $this->mTrxAtomicLevels = new SplStack;
                $this->srvCache = ObjectCache::newAccelerator( 'hash' );
  
                $server = $params['host'];
         *
         * @param IDatabase $db1
         * @param IDatabase ...
-        * @return array ('lag': highest lag, 'since': lowest estimate UNIX timestamp)
+        * @return array Map of values:
+        *   - lag: highest lag of any of the DBs
+        *   - since: oldest UNIX timestamp of any of the DB lag estimates
+        *   - pending: whether any of the DBs have uncommitted changes
         * @since 1.27
         */
        public static function getCacheSetOptions( IDatabase $db1 ) {
-               $res = array( 'lag' => 0, 'since' => INF );
+               $res = array( 'lag' => 0, 'since' => INF, 'pending' => false );
                foreach ( func_get_args() as $db ) {
                        /** @var IDatabase $db */
                        $status = $db->getSessionLagStatus();
                        $res['lag'] = max( $res['lag'], $status['lag'] );
                        $res['since'] = min( $res['since'], $status['since'] );
+                       $res['pending'] = $res['pending'] ?: $db->writesPending();
                }
  
                return $res;
@@@ -167,8 -167,6 +167,6 @@@ class ObjectCache 
                if ( isset( $params['loggroup'] ) ) {
                        $params['logger'] = LoggerFactory::getInstance( $params['loggroup'] );
                } else {
-                       // For backwards-compatability with custom parameters, lets not
-                       // have all logging suddenly disappear
                        $params['logger'] = LoggerFactory::getInstance( 'objectcache' );
                }
                if ( !isset( $params['keyspace'] ) ) {
                        return call_user_func( $params['factory'], $params );
                } elseif ( isset( $params['class'] ) ) {
                        $class = $params['class'];
 -                      if ( $class === 'MultiWriteBagOStuff' && !isset( $params['asyncHandler'] ) ) {
 -                              $params['asyncHandler'] = 'DeferredUpdates::addCallableUpdate';
 +                      // Automatically set the 'async' update handler
 +                      if ( $class === 'MultiWriteBagOStuff' ) {
 +                              $params['asyncHandler'] = isset( $params['asyncHandler'] )
 +                                      ? $params['asyncHandler']
 +                                      : 'DeferredUpdates::addCallableUpdate';
 +                      }
 +                      // Do b/c logic for MemcachedBagOStuff
 +                      if ( is_subclass_of( $class, 'MemcachedBagOStuff' ) ) {
 +                              if ( !isset( $params['servers'] ) ) {
 +                                      $params['servers'] = $GLOBALS['wgMemCachedServers'];
 +                              }
 +                              if ( !isset( $params['debug'] ) ) {
 +                                      $params['debug'] = $GLOBALS['wgMemCachedDebug'];
 +                              }
 +                              if ( !isset( $params['persistent'] ) ) {
 +                                      $params['persistent'] = $GLOBALS['wgMemCachedPersistent'];
 +                              }
 +                              if ( !isset( $params['timeout'] ) ) {
 +                                      $params['timeout'] = $GLOBALS['wgMemCachedTimeout'];
 +                              }
                        }
                        return new $class( $params );
                } else {
                return self::newFromId( $id );
        }
  
 -      /**
 -       * Factory function that creates a memcached client object.
 -       *
 -       * This always uses the PHP client, since the PECL client has a different
 -       * hashing scheme and a different interpretation of the flags bitfield, so
 -       * switching between the two clients randomly would be disastrous.
 -       *
 -       * @param array $params
 -       * @return MemcachedPhpBagOStuff
 -       */
 -      public static function newMemcached( $params ) {
 -              return new MemcachedPhpBagOStuff( $params );
 -      }
 -
        /**
         * Create a new cache object of the specified type.
         *
                $class = $params['relayerConfig']['class'];
                $params['relayer'] = new $class( $params['relayerConfig'] );
                $params['cache'] = self::newFromId( $params['cacheId'] );
+               if ( isset( $params['loggroup'] ) ) {
+                       $params['logger'] = LoggerFactory::getInstance( $params['loggroup'] );
+               } else {
+                       $params['logger'] = LoggerFactory::getInstance( 'objectcache' );
+               }
                $class = $params['class'];
  
                return new $class( $params );