Add CollationFa
[lhc/web/wiklou.git] / includes / db / loadbalancer / LBFactory.php
index f39596b..053f9f8 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup Database
  */
 
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Services\DestructibleService;
 use Psr\Log\LoggerInterface;
 use MediaWiki\Logger\LoggerFactory;
 
@@ -28,7 +30,8 @@ use MediaWiki\Logger\LoggerFactory;
  * An interface for generating database load balancers
  * @ingroup Database
  */
-abstract class LBFactory {
+abstract class LBFactory implements DestructibleService {
+
        /** @var ChronologyProtector */
        protected $chronProt;
 
@@ -38,9 +41,6 @@ abstract class LBFactory {
        /** @var LoggerInterface */
        protected $logger;
 
-       /** @var LBFactory */
-       private static $instance;
-
        /** @var string|bool Reason all LBs are read-only or false if not */
        protected $readOnlyReason = false;
 
@@ -60,38 +60,40 @@ abstract class LBFactory {
                $this->logger = LoggerFactory::getInstance( 'DBTransaction' );
        }
 
+       /**
+        * Disables all load balancers. All connections are closed, and any attempt to
+        * open a new connection will result in a DBAccessError.
+        * @see LoadBalancer::disable()
+        */
+       public function destroy() {
+               $this->shutdown();
+               $this->forEachLBCallMethod( 'disable' );
+       }
+
        /**
         * Disables all access to the load balancer, will cause all database access
         * to throw a DBAccessError
         */
        public static function disableBackend() {
-               global $wgLBFactoryConf;
-               self::$instance = new LBFactoryFake( $wgLBFactoryConf );
+               MediaWikiServices::disableStorageBackend();
        }
 
        /**
         * Get an LBFactory instance
         *
+        * @deprecated since 1.27, use MediaWikiServices::getDBLoadBalancerFactory() instead.
+        *
         * @return LBFactory
         */
        public static function singleton() {
-               global $wgLBFactoryConf;
-
-               if ( is_null( self::$instance ) ) {
-                       $class = self::getLBFactoryClass( $wgLBFactoryConf );
-                       $config = $wgLBFactoryConf;
-                       if ( !isset( $config['readOnlyReason'] ) ) {
-                               $config['readOnlyReason'] = wfConfiguredReadOnlyReason();
-                       }
-                       self::$instance = new $class( $config );
-               }
-
-               return self::$instance;
+               return MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
        }
 
        /**
         * Returns the LBFactory class to use and the load balancer configuration.
         *
+        * @todo instead of this, use a ServiceContainer for managing the different implementations.
+        *
         * @param array $config (e.g. $wgLBFactoryConf)
         * @return string Class name
         */
@@ -120,23 +122,11 @@ abstract class LBFactory {
 
        /**
         * Shut down, close connections and destroy the cached instance.
-        */
-       public static function destroyInstance() {
-               if ( self::$instance ) {
-                       self::$instance->shutdown();
-                       self::$instance->forEachLBCallMethod( 'closeAll' );
-                       self::$instance = null;
-               }
-       }
-
-       /**
-        * Set the instance to be the given object
         *
-        * @param LBFactory $instance
+        * @deprecated since 1.27, use LBFactory::destroy()
         */
-       public static function setInstance( $instance ) {
-               self::destroyInstance();
-               self::$instance = $instance;
+       public static function destroyInstance() {
+               self::singleton()->destroy();
        }
 
        /**
@@ -214,15 +204,12 @@ abstract class LBFactory {
         * 1. To commit changes to the masters.
         * 2. To release the snapshot on all connections, master and slave.
         * @param string $fname Caller name
+        * @param array $options Options map:
+        *   - maxWriteDuration: abort if more than this much time was spent in write queries
         */
-       public function commitAll( $fname = __METHOD__ ) {
-               $this->logMultiDbTransaction();
-
-               $start = microtime( true );
+       public function commitAll( $fname = __METHOD__, array $options = [] ) {
+               $this->commitMasterChanges( $fname, $options );
                $this->forEachLBCallMethod( 'commitAll', [ $fname ] );
-               $timeMs = 1000 * ( microtime( true ) - $start );
-
-               RequestContext::getMain()->getStats()->timing( "db.commit-all", $timeMs );
        }
 
        /**
@@ -232,21 +219,17 @@ abstract class LBFactory {
         *   - maxWriteDuration: abort if more than this much time was spent in write queries
         */
        public function commitMasterChanges( $fname = __METHOD__, array $options = [] ) {
-               $limit = isset( $options['maxWriteDuration'] ) ? $options['maxWriteDuration'] : 0;
-
-               $this->logMultiDbTransaction();
-               $this->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 )->text()
-                                       );
-                               }
-                       } );
-               } );
-
+               // Perform all pre-commit callbacks, aborting on failure
+               $this->forEachLBCallMethod( 'runMasterPreCommitCallbacks' );
+               // Perform all pre-commit checks, aborting on failure
+               $this->forEachLBCallMethod( 'approveMasterChanges', [ $options ] );
+               // Log the DBs and methods involved in multi-DB transactions
+               $this->logIfMultiDbTransaction();
+               // Actually perform the commit on all master DB connections
+               $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname ] );
+               // Run all post-commit callbacks
+               $this->forEachLBCallMethod( 'runMasterPostCommitCallbacks' );
+               // Commit any dangling DBO_TRX transactions from callbacks on one DB to another DB
                $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname ] );
        }
 
@@ -262,7 +245,7 @@ abstract class LBFactory {
        /**
         * Log query info if multi DB transactions are going to be committed now
         */
-       private function logMultiDbTransaction() {
+       private function logIfMultiDbTransaction() {
                $callersByDB = [];
                $this->forEachLB( function ( LoadBalancer $lb ) use ( &$callersByDB ) {
                        $masterName = $lb->getServerName( $lb->getWriterIndex() );
@@ -462,6 +445,15 @@ abstract class LBFactory {
                        }
                } );
        }
+
+       /**
+        * Close all open database connections on all open load balancers.
+        * @since 1.28
+        */
+       public function closeAll() {
+               $this->forEachLBCallMethod( 'closeAll', [] );
+       }
+
 }
 
 /**
@@ -470,7 +462,7 @@ abstract class LBFactory {
 class DBAccessError extends MWException {
        public function __construct() {
                parent::__construct( "Mediawiki tried to access the database via wfGetDB(). " .
-                       "This is not allowed." );
+                       "This is not allowed, because database access has been disabled." );
        }
 }