Merge "rdbms: add setTempTablesOnlyMode() to suppress CONN_TRX_AUTOCOMMIT during...
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sun, 25 Aug 2019 15:02:48 +0000 (15:02 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sun, 25 Aug 2019 15:02:48 +0000 (15:02 +0000)
1  2 
includes/Storage/NameTableStore.php
includes/libs/rdbms/loadbalancer/ILoadBalancer.php
includes/libs/rdbms/loadbalancer/LoadBalancer.php
tests/phpunit/MediaWikiIntegrationTestCase.php

@@@ -111,7 -111,7 +111,7 @@@ class NameTableStore 
         * @return IDatabase
         */
        private function getDBConnection( $index, $flags = 0 ) {
 -              return $this->loadBalancer->getConnection( $index, [], $this->domain, $flags );
 +              return $this->loadBalancer->getConnectionRef( $index, [], $this->domain, $flags );
        }
  
        /**
                        if ( $id === null ) {
                                // RACE: $name was already in the db, probably just inserted, so load from master.
                                // Use DBO_TRX to avoid missing inserts due to other threads or REPEATABLE-READs.
-                               // ...but not during unit tests, because we need the fake DB tables of the default
-                               // connection.
-                               $connFlags = defined( 'MW_PHPUNIT_TEST' ) ? 0 : ILoadBalancer::CONN_TRX_AUTOCOMMIT;
-                               $table = $this->reloadMap( $connFlags );
+                               $table = $this->reloadMap( ILoadBalancer::CONN_TRX_AUTOCOMMIT );
  
                                $searchResult = array_search( $name, $table, true );
                                if ( $searchResult === false ) {
@@@ -155,6 -155,18 +155,18 @@@ interface ILoadBalancer 
         */
        public function redefineLocalDomain( $domain );
  
+       /**
+        * Indicate whether the tables on this domain are only temporary tables for testing
+        *
+        * In "temporary tables mode", the ILoadBalancer::CONN_TRX_AUTOCOMMIT flag is ignored
+        *
+        * @param bool $value
+        * @param string $domain
+        * @return bool Whether "temporary tables mode" was active
+        * @since 1.34
+        */
+       public function setTempTablesOnlyMode( $value, $domain );
        /**
         * Get the server index of the reader connection for a given group
         *
         *
         * @param string|bool $group Query group or false for the generic group
         * @param string|bool $domain DB domain ID or false for the local domain
 -       * @throws DBError If no live handle can be obtained
 -       * @return bool|int|string
 +       * @return int|bool Returns false if no live handle can be obtained
         */
        public function getReaderIndex( $group = false, $domain = false );
  
@@@ -101,6 -101,8 +101,8 @@@ class LoadBalancer implements ILoadBala
        private $indexAliases = [];
        /** @var array[] Map of (name => callable) */
        private $trxRecurringCallbacks = [];
+       /** @var bool[] Map of (domain => whether to use "temp tables only" mode) */
+       private $tempTablesOnlyMode = [];
  
        /** @var Database Connection handle that caused a problem */
        private $errorConnection;
        /** @var bool Whether any connection has been attempted yet */
        private $connectionAttempted = false;
  
 +      /** var int An identifier for this class instance */
 +      private $id;
        /** @var int|null Integer ID of the managing LBFactory instance or null if none */
        private $ownerId;
        /** @var string|bool Explicit DBO_TRX transaction round active or false if none */
                        if ( ++$listKey !== $i ) {
                                throw new UnexpectedValueException( 'List expected for "servers" parameter' );
                        }
 -                      if ( $i == 0 ) {
 -                              $server['master'] = true;
 -                      } else {
 -                              $server['replica'] = true;
 -                      }
                        $this->servers[$i] = $server;
                        foreach ( ( $server['groupLoads'] ?? [] ) as $group => $ratio ) {
                                $this->groupLoads[$group][$i] = $ratio;
                $group = $params['defaultGroup'] ?? self::GROUP_GENERIC;
                $this->defaultGroup = isset( $this->groupLoads[$group] ) ? $group : self::GROUP_GENERIC;
  
 +              static $nextId;
 +              $this->id = $nextId = ( is_int( $nextId ) ? $nextId++ : mt_rand() );
                $this->ownerId = $params['ownerId'] ?? null;
        }
  
        /**
         * @param int $flags Bitfield of class CONN_* constants
         * @param int $i Specific server index or DB_MASTER/DB_REPLICA
+        * @param string $domain Database domain
         * @return int Sanitized bitfield
         */
-       private function sanitizeConnectionFlags( $flags, $i ) {
+       private function sanitizeConnectionFlags( $flags, $i, $domain ) {
                // Whether an outside caller is explicitly requesting the master database server
                if ( $i === self::DB_MASTER || $i === $this->getWriterIndex() ) {
                        $flags |= self::CONN_INTENT_WRITABLE;
                                $flags &= ~self::CONN_TRX_AUTOCOMMIT;
                                $type = $this->getServerType( $this->getWriterIndex() );
                                $this->connLogger->info( __METHOD__ . ": CONN_TRX_AUTOCOMMIT disallowed ($type)" );
+                       } elseif ( isset( $this->tempTablesOnlyMode[$domain] ) ) {
+                               // T202116: integration tests are active and queries should be all be using
+                               // temporary clone tables (via prefix). Such tables are not visible accross
+                               // different connections nor can there be REPEATABLE-READ snapshot staleness,
+                               // so use the same connection for everything.
+                               $flags &= ~self::CONN_TRX_AUTOCOMMIT;
                        }
                }
  
                        $this->connLogger->debug( __METHOD__ . ": Using reader #$i: $serverName..." );
  
                        // Get a connection to this server without triggering other server connections
 -                      $flags = self::CONN_SILENCE_ERRORS;
 -                      $conn = $this->getServerConnection( $i, $domain, $flags );
 +                      $conn = $this->getServerConnection( $i, $domain, self::CONN_SILENCE_ERRORS );
                        if ( !$conn ) {
                                $this->connLogger->warning( __METHOD__ . ": Failed connecting to $i/$domain" );
                                unset( $currentLoads[$i] ); // avoid this server next iteration
        public function getConnection( $i, $groups = [], $domain = false, $flags = 0 ) {
                $domain = $this->resolveDomainID( $domain );
                $groups = $this->resolveGroups( $groups, $i );
-               $flags = $this->sanitizeConnectionFlags( $flags, $i );
+               $flags = $this->sanitizeConnectionFlags( $flags, $i, $domain );
                // If given DB_MASTER/DB_REPLICA, resolve it to a specific server index. Resolving
                // DB_REPLICA might trigger getServerConnection() calls due to the getReaderIndex()
                // connectivity checks or LoadMonitor::scaleLoads() server state cache regeneration.
                $serverIndex = $conn->getLBInfo( 'serverIndex' );
                $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
                if ( $serverIndex === null || $refCount === null ) {
 -                      /**
 -                       * This can happen in code like:
 -                       *   foreach ( $dbs as $db ) {
 -                       *     $conn = $lb->getConnection( $lb::DB_REPLICA, [], $db );
 -                       *     ...
 -                       *     $lb->reuseConnection( $conn );
 -                       *   }
 -                       * When a connection to the local DB is opened in this way, reuseConnection()
 -                       * should be ignored
 -                       */
 -                      return;
 +                      return; // non-foreign connection; no domain-use tracking to update
                } elseif ( $conn instanceof DBConnRef ) {
                        // DBConnRef already handles calling reuseConnection() and only passes the live
                        // Database instance to this method. Any caller passing in a DBConnRef is broken.
         *
         * @note If disable() was called on this LoadBalancer, this method will throw a DBAccessError.
         *
 -       * @param int $i Server index
 +       * @param int $i Specific server index
         * @param int $flags Class CONN_* constant bitfield
         * @return Database
         * @throws InvalidArgumentException When the server index is invalid
         * @throws UnexpectedValueException When the DB domain of the connection is corrupted
         */
        private function getLocalConnection( $i, $flags = 0 ) {
 +              $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
                // Connection handles required to be in auto-commit mode use a separate connection
                // pool since the main pool is effected by implicit and explicit transaction rounds
 -              $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
 -
                $connKey = $autoCommit ? self::KEY_LOCAL_NOROUND : self::KEY_LOCAL;
 +
                if ( isset( $this->conns[$connKey][$i][0] ) ) {
                        $conn = $this->conns[$connKey][$i][0];
                } else {
 -                      // Open a new connection
 -                      $server = $this->getServerInfoStrict( $i );
 -                      $server['serverIndex'] = $i;
 -                      $server['autoCommitOnly'] = $autoCommit;
 -                      $conn = $this->reallyOpenConnection( $server, $this->localDomain );
 -                      $host = $this->getServerName( $i );
 +                      $conn = $this->reallyOpenConnection(
 +                              $i,
 +                              $this->localDomain,
 +                              [ 'autoCommitOnly' => $autoCommit ]
 +                      );
                        if ( $conn->isOpen() ) {
 -                              $this->connLogger->debug(
 -                                      __METHOD__ . ": connected to database $i at '$host'." );
 +                              $this->connLogger->debug( __METHOD__ . ": opened new connection for $i" );
                                $this->conns[$connKey][$i][0] = $conn;
                        } else {
 -                              $this->connLogger->warning(
 -                                      __METHOD__ . ": failed to connect to database $i at '$host'." );
 +                              $this->connLogger->warning( __METHOD__ . ": connection error for $i" );
                                $this->errorConnection = $conn;
                                $conn = false;
                        }
                }
  
 -              // Final sanity check to make sure the right domain is selected
 +              // Sanity check to make sure that the right domain is selected
                if (
                        $conn instanceof IDatabase &&
                        !$this->localDomain->isCompatible( $conn->getDomainID() )
                ) {
                        throw new UnexpectedValueException(
                                "Got connection to '{$conn->getDomainID()}', " .
 -                              "but expected local domain ('{$this->localDomain}')" );
 +                              "but expected local domain ('{$this->localDomain}')"
 +                      );
                }
  
                return $conn;
         *
         * @note If disable() was called on this LoadBalancer, this method will throw a DBAccessError.
         *
 -       * @param int $i Server index
 +       * @param int $i Specific server index
         * @param string $domain Domain ID to open
         * @param int $flags Class CONN_* constant bitfield
         * @return Database|bool Returns false on connection error
         */
        private function getForeignConnection( $i, $domain, $flags = 0 ) {
                $domainInstance = DatabaseDomain::newFromId( $domain );
 +              $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
                // Connection handles required to be in auto-commit mode use a separate connection
                // pool since the main pool is effected by implicit and explicit transaction rounds
 -              $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
 -
                if ( $autoCommit ) {
                        $connFreeKey = self::KEY_FOREIGN_FREE_NOROUND;
                        $connInUseKey = self::KEY_FOREIGN_INUSE_NOROUND;
                }
  
                if ( !$conn ) {
 -                      // Open a new connection
 -                      $server = $this->getServerInfoStrict( $i );
 -                      $server['serverIndex'] = $i;
 -                      $server['foreignPoolRefCount'] = 0;
 -                      $server['foreign'] = true;
 -                      $server['autoCommitOnly'] = $autoCommit;
 -                      $conn = $this->reallyOpenConnection( $server, $domainInstance );
 -                      if ( !$conn->isOpen() ) {
 -                              $this->connLogger->warning( __METHOD__ . ": connection error for $i/$domain" );
 -                              $this->errorConnection = $conn;
 -                              $conn = false;
 -                      } else {
 +                      $conn = $this->reallyOpenConnection(
 +                              $i,
 +                              $domainInstance,
 +                              [
 +                                      'autoCommitOnly' => $autoCommit,
 +                                      'foreign' => true,
 +                                      'foreignPoolRefCount' => 0
 +                              ]
 +                      );
 +                      if ( $conn->isOpen() ) {
                                // Note that if $domain is an empty string, getDomainID() might not match it
                                $this->conns[$connInUseKey][$i][$conn->getDomainID()] = $conn;
                                $this->connLogger->debug( __METHOD__ . ": opened new connection for $i/$domain" );
 +                      } else {
 +                              $this->connLogger->warning( __METHOD__ . ": connection error for $i/$domain" );
 +                              $this->errorConnection = $conn;
 +                              $conn = false;
                        }
                }
  
                if ( $conn instanceof IDatabase ) {
 -                      // Final sanity check to make sure the right domain is selected
 +                      // Sanity check to make sure that the right domain is selected
                        if ( !$domainInstance->isCompatible( $conn->getDomainID() ) ) {
                                throw new UnexpectedValueException(
                                        "Got connection to '{$conn->getDomainID()}', but expected '$domain'" );
         *
         * Returns a Database object whether or not the connection was successful.
         *
 -       * @param array $server
 +       * @param int $i Specific server index
         * @param DatabaseDomain $domain Domain the connection is for, possibly unspecified
 +       * @param array $lbInfo Additional information for setLBInfo()
         * @return Database
         * @throws DBAccessError
         * @throws InvalidArgumentException
         */
 -      protected function reallyOpenConnection( array $server, DatabaseDomain $domain ) {
 +      protected function reallyOpenConnection( $i, DatabaseDomain $domain, array $lbInfo ) {
                if ( $this->disabled ) {
                        throw new DBAccessError();
                }
  
 -              if ( $domain->getDatabase() === null ) {
 -                      // The database domain does not specify a DB name and some database systems require a
 -                      // valid DB specified on connection. The $server configuration array contains a default
 -                      // DB name to use for connections in such cases.
 -                      if ( $server['type'] === 'mysql' ) {
 -                              // For MySQL, DATABASE and SCHEMA are synonyms, connections need not specify a DB,
 -                              // and the DB name in $server might not exist due to legacy reasons (the default
 -                              // domain used to ignore the local LB domain, even when mismatched).
 -                              $server['dbname'] = null;
 -                      }
 -              } else {
 -                      $server['dbname'] = $domain->getDatabase();
 -              }
 -
 -              if ( $domain->getSchema() !== null ) {
 -                      $server['schema'] = $domain->getSchema();
 -              }
 -
 -              // It is always possible to connect with any prefix, even the empty string
 -              $server['tablePrefix'] = $domain->getTablePrefix();
 -
 -              // Let the handle know what the cluster master is (e.g. "db1052")
 -              $masterName = $this->getServerName( $this->getWriterIndex() );
 -              $server['clusterMasterHost'] = $masterName;
 -
 -              $server['srvCache'] = $this->srvCache;
 -              // Set loggers and profilers
 -              $server['connLogger'] = $this->connLogger;
 -              $server['queryLogger'] = $this->queryLogger;
 -              $server['errorLogger'] = $this->errorLogger;
 -              $server['deprecationLogger'] = $this->deprecationLogger;
 -              $server['profiler'] = $this->profiler;
 -              $server['trxProfiler'] = $this->trxProfiler;
 -              // Use the same agent and PHP mode for all DB handles
 -              $server['cliMode'] = $this->cliMode;
 -              $server['agent'] = $this->agent;
 -              // Use DBO_DEFAULT flags by default for LoadBalancer managed databases. Assume that the
 -              // application calls LoadBalancer::commitMasterChanges() before the PHP script completes.
 -              $server['flags'] = $server['flags'] ?? IDatabase::DBO_DEFAULT;
 -
 -              // Create a live connection object
 +              $server = $this->getServerInfoStrict( $i );
 +              $server = array_merge( $server, [
 +                      // Use the database specified in $domain (null means "none or entrypoint DB");
 +                      // fallback to the $server default if the RDBMs is an embedded library using a file
 +                      // on disk since there would be nothing to access to without a DB/file name.
 +                      'dbname' => $this->getServerAttributes( $i )[Database::ATTR_DB_IS_FILE]
 +                              ? ( $domain->getDatabase() ?? $server['dbname'] ?? null )
 +                              : $domain->getDatabase(),
 +                      // Override the $server default schema with that of $domain if specified
 +                      'schema' => $domain->getSchema() ?? $server['schema'] ?? null,
 +                      // Use the table prefix specified in $domain
 +                      'tablePrefix' => $domain->getTablePrefix(),
 +                      // Participate in transaction rounds if $server does not specify otherwise
 +                      'flags' => $server['flags'] ?? IDatabase::DBO_DEFAULT,
 +                      // Inject the PHP execution mode and the agent string
 +                      'cliMode' => $this->cliMode,
 +                      'agent' => $this->agent,
 +                      // Inject object and callback dependencies
 +                      'srvCache' => $this->srvCache,
 +                      'connLogger' => $this->connLogger,
 +                      'queryLogger' => $this->queryLogger,
 +                      'errorLogger' => $this->errorLogger,
 +                      'deprecationLogger' => $this->deprecationLogger,
 +                      'profiler' => $this->profiler,
 +                      'trxProfiler' => $this->trxProfiler
 +              ] );
 +
 +              $lbInfo = array_merge( $lbInfo, [
 +                      'ownerId' => $this->id,
 +                      'serverIndex' => $i,
 +                      'master' => ( $i === $this->getWriterIndex() ),
 +                      'replica' => ( $i !== $this->getWriterIndex() ),
 +                      // Name of the master server of the relevant DB cluster (e.g. "db1052")
 +                      'clusterMasterHost' => $this->getServerName( $this->getWriterIndex() )
 +              ] );
 +
 +              $conn = Database::factory( $server['type'], $server, Database::NEW_UNCONNECTED );
                try {
 -                      $db = Database::factory( $server['type'], $server );
 -                      // Log when many connection are made on requests
 +                      $conn->initConnection();
                        ++$this->connectionCounter;
 -                      $currentConnCount = $this->getCurrentConnectionCount();
 -                      if ( $currentConnCount >= self::CONN_HELD_WARN_THRESHOLD ) {
 -                              $this->perfLogger->warning(
 -                                      __METHOD__ . ": {connections}+ connections made (master={masterdb})",
 -                                      [ 'connections' => $currentConnCount, 'masterdb' => $masterName ]
 -                              );
 -                      }
                } catch ( DBConnectionError $e ) {
 -                      // FIXME: This is probably the ugliest thing I have ever done to
 -                      // PHP. I'm half-expecting it to segfault, just out of disgust. -- TS
 -                      $db = $e->db;
 +                      // ignore; let the DB handle the logging
                }
  
 -              $db->setLBInfo( $server );
 -              $db->setLazyMasterHandle(
 -                      $this->getLazyConnectionRef( self::DB_MASTER, [], $db->getDomainID() )
 -              );
 -              $db->setTableAliases( $this->tableAliases );
 -              $db->setIndexAliases( $this->indexAliases );
 -
 -              if ( $server['serverIndex'] === $this->getWriterIndex() ) {
 +              $conn->setLBInfo( $lbInfo );
 +              if ( $conn->getFlag( $conn::DBO_DEFAULT ) ) {
 +                      if ( $this->cliMode ) {
 +                              $conn->clearFlag( $conn::DBO_TRX );
 +                      } else {
 +                              $conn->setFlag( $conn::DBO_TRX );
 +                      }
 +              }
 +              if ( $i === $this->getWriterIndex() ) {
                        if ( $this->trxRoundId !== false ) {
 -                              $this->applyTransactionRoundFlags( $db );
 +                              $this->applyTransactionRoundFlags( $conn );
                        }
                        foreach ( $this->trxRecurringCallbacks as $name => $callback ) {
 -                              $db->setTransactionListener( $name, $callback );
 +                              $conn->setTransactionListener( $name, $callback );
                        }
                }
 +              $conn->setTableAliases( $this->tableAliases );
 +              $conn->setIndexAliases( $this->indexAliases );
 +
 +              $conn->setLazyMasterHandle(
 +                      $this->getLazyConnectionRef( self::DB_MASTER, [], $conn->getDomainID() )
 +              );
  
                $this->lazyLoadReplicationPositions(); // session consistency
  
 -              return $db;
 +              // Log when many connection are made on requests
 +              $count = $this->getCurrentConnectionCount();
 +              if ( $count >= self::CONN_HELD_WARN_THRESHOLD ) {
 +                      $this->perfLogger->warning(
 +                              __METHOD__ . ": {connections}+ connections made (master={masterdb})",
 +                              [
 +                                      'connections' => $count,
 +                                      'dbserver' => $conn->getServer(),
 +                                      'masterdb' => $conn->getLBInfo( 'clusterMasterHost' )
 +                              ]
 +                      );
 +              }
 +
 +              return $conn;
        }
  
        /**
                }
  
                if ( $this->hasStreamingReplicaServers() ) {
 -                      try {
 -                              // Set "laggedReplicaMode"
 -                              $this->getReaderIndex( self::GROUP_GENERIC, $domain );
 -                      } catch ( DBConnectionError $e ) {
 -                              // Sanity: avoid expensive re-connect attempts and failures
 -                              $this->laggedReplicaMode = true;
 -                      }
 +                      // This will set "laggedReplicaMode" as needed
 +                      $this->getReaderIndex( self::GROUP_GENERIC, $domain );
                }
  
                return $this->laggedReplicaMode;
        public function waitForMasterPos( IDatabase $conn, $pos = false, $timeout = null ) {
                $timeout = max( 1, $timeout ?: $this->waitTimeout );
  
 -              if ( $this->getServerCount() <= 1 || !$conn->getLBInfo( 'replica' ) ) {
 -                      return true; // server is not a replica DB
 +              if ( $conn->getLBInfo( 'serverIndex' ) === $this->getWriterIndex() ) {
 +                      return true; // not a replica DB server
                }
  
                if ( !$pos ) {
                ) );
  
                // Update the prefix for all local connections...
 -              $this->forEachOpenConnection( function ( IDatabase $db ) use ( $prefix ) {
 -                      if ( !$db->getLBInfo( 'foreign' ) ) {
 -                              $db->tablePrefix( $prefix );
 +              $this->forEachOpenConnection( function ( IDatabase $conn ) use ( $prefix ) {
 +                      if ( !$conn->getLBInfo( 'foreign' ) ) {
 +                              $conn->tablePrefix( $prefix );
                        }
                } );
        }
                $this->setLocalDomain( DatabaseDomain::newFromId( $domain ) );
        }
  
+       public function setTempTablesOnlyMode( $value, $domain ) {
+               $old = $this->tempTablesOnlyMode[$domain] ?? false;
+               if ( $value ) {
+                       $this->tempTablesOnlyMode[$domain] = true;
+               } else {
+                       unset( $this->tempTablesOnlyMode[$domain] );
+               }
+               return $old;
+       }
        /**
         * @param DatabaseDomain $domain
         */
@@@ -23,9 -23,8 +23,9 @@@ use Wikimedia\TestingAccessWrapper
  abstract class MediaWikiIntegrationTestCase extends PHPUnit\Framework\TestCase {
  
        use MediaWikiCoversValidator;
 -      use PHPUnit4And6Compat;
        use MediaWikiGroupValidator;
 +      use MediaWikiTestCaseTrait;
 +      use PHPUnit4And6Compat;
  
        /**
         * The original service locator. This is overridden during setUp().
  
                $wgRequest = new FauxRequest();
                MediaWiki\Session\SessionManager::resetCache();
 +              Language::clearCaches();
        }
  
        public function run( PHPUnit_Framework_TestResult $result = null ) {
  
                if ( !isset( $db->_originalTablePrefix ) ) {
                        $oldPrefix = $db->tablePrefix();
                        if ( $oldPrefix === $prefix ) {
                                // table already has the correct prefix, but presumably no cloned tables
                                $oldPrefix = self::$oldTablePrefix;
                        $tablesCloned = self::listTables( $db );
                        $dbClone = new CloneDatabase( $db, $tablesCloned, $prefix, $oldPrefix );
                        $dbClone->useTemporaryTables( self::$useTemporaryTables );
                        $dbClone->cloneTableStructure();
  
                        $db->tablePrefix( $prefix );
                        $db->_originalTablePrefix = $oldPrefix;
+                       $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+                       $lb->setTempTablesOnlyMode( self::$useTemporaryTables, $lb->getLocalDomainID() );
                }
  
                return true;
  
                $dbClone = new CloneDatabase( $db, $tables, $db->tablePrefix(), $db->_originalTablePrefix );
                $dbClone->useTemporaryTables( self::$useTemporaryTables );
                $dbClone->cloneTableStructure();
+               $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+               $lb->setTempTablesOnlyMode( self::$useTemporaryTables, $lb->getLocalDomainID() );
        }
  
        /**
                        'comment' => $comment,
                ] );
        }
 -
 -      /**
 -       * Returns a PHPUnit constraint that matches anything other than a fixed set of values. This can
 -       * be used to whitelist values, e.g.
 -       *   $mock->expects( $this->never() )->method( $this->anythingBut( 'foo', 'bar' ) );
 -       * which will throw if any unexpected method is called.
 -       *
 -       * @param mixed ...$values Values that are not matched
 -       */
 -      protected function anythingBut( ...$values ) {
 -              return $this->logicalNot( $this->logicalOr(
 -                      ...array_map( [ $this, 'matches' ], $values )
 -              ) );
 -      }
  }
  
  class_alias( 'MediaWikiIntegrationTestCase', 'MediaWikiTestCase' );