MWLBFactory.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. <?php
  2. /**
  3. * Generator of database load balancing objects.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. * @ingroup Database
  22. */
  23. use MediaWiki\Logger\LoggerFactory;
  24. use MediaWiki\MediaWikiServices;
  25. use Wikimedia\Rdbms\LBFactory;
  26. use Wikimedia\Rdbms\DatabaseDomain;
  27. /**
  28. * MediaWiki-specific class for generating database load balancers
  29. * @ingroup Database
  30. */
  31. abstract class MWLBFactory {
  32. /** @var array Cache of already-logged deprecation messages */
  33. private static $loggedDeprecations = [];
  34. /**
  35. * @param array $lbConf Config for LBFactory::__construct()
  36. * @param Config $mainConfig Main config object from MediaWikiServices
  37. * @param ConfiguredReadOnlyMode $readOnlyMode
  38. * @return array
  39. */
  40. public static function applyDefaultConfig( array $lbConf, Config $mainConfig,
  41. ConfiguredReadOnlyMode $readOnlyMode
  42. ) {
  43. global $wgCommandLineMode;
  44. static $typesWithSchema = [ 'postgres', 'msssql' ];
  45. $lbConf += [
  46. 'localDomain' => new DatabaseDomain(
  47. $mainConfig->get( 'DBname' ),
  48. $mainConfig->get( 'DBmwschema' ),
  49. $mainConfig->get( 'DBprefix' )
  50. ),
  51. 'profiler' => Profiler::instance(),
  52. 'trxProfiler' => Profiler::instance()->getTransactionProfiler(),
  53. 'replLogger' => LoggerFactory::getInstance( 'DBReplication' ),
  54. 'queryLogger' => LoggerFactory::getInstance( 'DBQuery' ),
  55. 'connLogger' => LoggerFactory::getInstance( 'DBConnection' ),
  56. 'perfLogger' => LoggerFactory::getInstance( 'DBPerformance' ),
  57. 'errorLogger' => [ MWExceptionHandler::class, 'logException' ],
  58. 'deprecationLogger' => [ static::class, 'logDeprecation' ],
  59. 'cliMode' => $wgCommandLineMode,
  60. 'hostname' => wfHostname(),
  61. 'readOnlyReason' => $readOnlyMode->getReason(),
  62. ];
  63. // When making changes here, remember to also specify MediaWiki-specific options
  64. // for Database classes in the relevant Installer subclass.
  65. // Such as MysqlInstaller::openConnection and PostgresInstaller::openConnectionWithParams.
  66. if ( $lbConf['class'] === Wikimedia\Rdbms\LBFactorySimple::class ) {
  67. if ( isset( $lbConf['servers'] ) ) {
  68. // Server array is already explicitly configured; leave alone
  69. } elseif ( is_array( $mainConfig->get( 'DBservers' ) ) ) {
  70. foreach ( $mainConfig->get( 'DBservers' ) as $i => $server ) {
  71. if ( $server['type'] === 'sqlite' ) {
  72. $server += [ 'dbDirectory' => $mainConfig->get( 'SQLiteDataDir' ) ];
  73. } elseif ( $server['type'] === 'postgres' ) {
  74. $server += [
  75. 'port' => $mainConfig->get( 'DBport' ),
  76. // Work around the reserved word usage in MediaWiki schema
  77. 'keywordTableMap' => [ 'user' => 'mwuser', 'text' => 'pagecontent' ]
  78. ];
  79. } elseif ( $server['type'] === 'mssql' ) {
  80. $server += [
  81. 'port' => $mainConfig->get( 'DBport' ),
  82. 'useWindowsAuth' => $mainConfig->get( 'DBWindowsAuthentication' )
  83. ];
  84. }
  85. if ( in_array( $server['type'], $typesWithSchema, true ) ) {
  86. $server += [ 'schema' => $mainConfig->get( 'DBmwschema' ) ];
  87. }
  88. $server += [
  89. 'tablePrefix' => $mainConfig->get( 'DBprefix' ),
  90. 'flags' => DBO_DEFAULT,
  91. 'sqlMode' => $mainConfig->get( 'SQLMode' ),
  92. 'utf8Mode' => $mainConfig->get( 'DBmysql5' )
  93. ];
  94. $lbConf['servers'][$i] = $server;
  95. }
  96. } else {
  97. $flags = DBO_DEFAULT;
  98. $flags |= $mainConfig->get( 'DebugDumpSql' ) ? DBO_DEBUG : 0;
  99. $flags |= $mainConfig->get( 'DBssl' ) ? DBO_SSL : 0;
  100. $flags |= $mainConfig->get( 'DBcompress' ) ? DBO_COMPRESS : 0;
  101. $server = [
  102. 'host' => $mainConfig->get( 'DBserver' ),
  103. 'user' => $mainConfig->get( 'DBuser' ),
  104. 'password' => $mainConfig->get( 'DBpassword' ),
  105. 'dbname' => $mainConfig->get( 'DBname' ),
  106. 'tablePrefix' => $mainConfig->get( 'DBprefix' ),
  107. 'type' => $mainConfig->get( 'DBtype' ),
  108. 'load' => 1,
  109. 'flags' => $flags,
  110. 'sqlMode' => $mainConfig->get( 'SQLMode' ),
  111. 'utf8Mode' => $mainConfig->get( 'DBmysql5' )
  112. ];
  113. if ( in_array( $server['type'], $typesWithSchema, true ) ) {
  114. $server += [ 'schema' => $mainConfig->get( 'DBmwschema' ) ];
  115. }
  116. if ( $server['type'] === 'sqlite' ) {
  117. $server[ 'dbDirectory'] = $mainConfig->get( 'SQLiteDataDir' );
  118. } elseif ( $server['type'] === 'postgres' ) {
  119. $server['port'] = $mainConfig->get( 'DBport' );
  120. // Work around the reserved word usage in MediaWiki schema
  121. $server['keywordTableMap'] = [ 'user' => 'mwuser', 'text' => 'pagecontent' ];
  122. } elseif ( $server['type'] === 'mssql' ) {
  123. $server['port'] = $mainConfig->get( 'DBport' );
  124. $server['useWindowsAuth'] = $mainConfig->get( 'DBWindowsAuthentication' );
  125. }
  126. $lbConf['servers'] = [ $server ];
  127. }
  128. if ( !isset( $lbConf['externalClusters'] ) ) {
  129. $lbConf['externalClusters'] = $mainConfig->get( 'ExternalServers' );
  130. }
  131. } elseif ( $lbConf['class'] === Wikimedia\Rdbms\LBFactoryMulti::class ) {
  132. if ( isset( $lbConf['serverTemplate'] ) ) {
  133. if ( in_array( $lbConf['serverTemplate']['type'], $typesWithSchema, true ) ) {
  134. $lbConf['serverTemplate']['schema'] = $mainConfig->get( 'DBmwschema' );
  135. }
  136. $lbConf['serverTemplate']['sqlMode'] = $mainConfig->get( 'SQLMode' );
  137. $lbConf['serverTemplate']['utf8Mode'] = $mainConfig->get( 'DBmysql5' );
  138. }
  139. }
  140. $services = MediaWikiServices::getInstance();
  141. // Use APC/memcached style caching, but avoids loops with CACHE_DB (T141804)
  142. $sCache = $services->getLocalServerObjectCache();
  143. if ( $sCache->getQoS( $sCache::ATTR_EMULATION ) > $sCache::QOS_EMULATION_SQL ) {
  144. $lbConf['srvCache'] = $sCache;
  145. }
  146. $mStash = $services->getMainObjectStash();
  147. if ( $mStash->getQoS( $mStash::ATTR_EMULATION ) > $mStash::QOS_EMULATION_SQL ) {
  148. $lbConf['memStash'] = $mStash;
  149. }
  150. $wCache = $services->getMainWANObjectCache();
  151. if ( $wCache->getQoS( $wCache::ATTR_EMULATION ) > $wCache::QOS_EMULATION_SQL ) {
  152. $lbConf['wanCache'] = $wCache;
  153. }
  154. return $lbConf;
  155. }
  156. /**
  157. * Returns the LBFactory class to use and the load balancer configuration.
  158. *
  159. * @todo instead of this, use a ServiceContainer for managing the different implementations.
  160. *
  161. * @param array $config (e.g. $wgLBFactoryConf)
  162. * @return string Class name
  163. */
  164. public static function getLBFactoryClass( array $config ) {
  165. // For configuration backward compatibility after removing
  166. // underscores from class names in MediaWiki 1.23.
  167. $bcClasses = [
  168. 'LBFactory_Simple' => 'LBFactorySimple',
  169. 'LBFactory_Single' => 'LBFactorySingle',
  170. 'LBFactory_Multi' => 'LBFactoryMulti'
  171. ];
  172. $class = $config['class'];
  173. if ( isset( $bcClasses[$class] ) ) {
  174. $class = $bcClasses[$class];
  175. wfDeprecated(
  176. '$wgLBFactoryConf must be updated. See RELEASE-NOTES for details',
  177. '1.23'
  178. );
  179. }
  180. // For configuration backward compatibility after moving classes to namespaces (1.29)
  181. $compat = [
  182. 'LBFactorySingle' => Wikimedia\Rdbms\LBFactorySingle::class,
  183. 'LBFactorySimple' => Wikimedia\Rdbms\LBFactorySimple::class,
  184. 'LBFactoryMulti' => Wikimedia\Rdbms\LBFactoryMulti::class
  185. ];
  186. if ( isset( $compat[$class] ) ) {
  187. $class = $compat[$class];
  188. }
  189. return $class;
  190. }
  191. public static function setSchemaAliases( LBFactory $lbFactory, Config $config ) {
  192. if ( $config->get( 'DBtype' ) === 'mysql' ) {
  193. /**
  194. * When SQLite indexes were introduced in r45764, it was noted that
  195. * SQLite requires index names to be unique within the whole database,
  196. * not just within a schema. As discussed in CR r45819, to avoid the
  197. * need for a schema change on existing installations, the indexes
  198. * were implicitly mapped from the new names to the old names.
  199. *
  200. * This mapping can be removed if DB patches are introduced to alter
  201. * the relevant tables in existing installations. Note that because
  202. * this index mapping applies to table creation, even new installations
  203. * of MySQL have the old names (except for installations created during
  204. * a period where this mapping was inappropriately removed, see
  205. * T154872).
  206. */
  207. $lbFactory->setIndexAliases( [
  208. 'ar_usertext_timestamp' => 'usertext_timestamp',
  209. 'un_user_id' => 'user_id',
  210. 'un_user_ip' => 'user_ip',
  211. ] );
  212. }
  213. }
  214. /**
  215. * Log a database deprecation warning
  216. * @param string $msg Deprecation message
  217. */
  218. public static function logDeprecation( $msg ) {
  219. global $wgDevelopmentWarnings;
  220. if ( isset( self::$loggedDeprecations[$msg] ) ) {
  221. return;
  222. }
  223. self::$loggedDeprecations[$msg] = true;
  224. if ( $wgDevelopmentWarnings ) {
  225. trigger_error( $msg, E_USER_DEPRECATED );
  226. }
  227. wfDebugLog( 'deprecated', $msg, 'private' );
  228. }
  229. }