MediaWiki master
LoadBalancer.php
Go to the documentation of this file.
1<?php
6namespace Wikimedia\Rdbms;
7
8use InvalidArgumentException;
9use LogicException;
10use Psr\Log\LoggerInterface;
11use Psr\Log\NullLogger;
12use RuntimeException;
13use Throwable;
14use UnexpectedValueException;
19use Wikimedia\ScopedCallback;
21
28 private $loadMonitor;
30 private $srvCache;
32 private $wanCache;
34 private $databaseFactory;
35
37 private $trxProfiler;
39 private $statsFactory;
41 private $logger;
43 private $errorLogger;
45 private $localDomain;
47 private $cliMode;
48
50 private $conns;
51
53 private $clusterName;
55 private $serverInfo;
57 private $loads;
59 private $defaultGroup;
60
62 private $tableAliases = [];
64 private $domainAliases = [];
66 private $trxRecurringCallbacks = [];
68 private $tempTablesOnlyMode = [];
69
71 private $trxRoundFname = null;
73 private $trxRoundStage = self::ROUND_CURSORY;
75 private $readIndexByGroup = [];
77 private $waitForPos;
79 private $laggedReplicaMode = false;
81 private $readOnlyReason = false;
83 private $connectionCounter = 0;
85 private $disabled = false;
86 private ?ChronologyProtector $chronologyProtector = null;
88 private $chronologyProtectorCalled = false;
89
91 private $lastErrorConn;
92
94 private $nonLocalDomainCache = [];
95 private ?string $uniqueIdentifier = null;
96
101 private $modcount = 0;
102
104 private const INFO_SERVER_INDEX = 'serverIndex';
106 private const INFO_CONN_CATEGORY = 'connCategory';
107
109 private const CONN_HELD_WARN_THRESHOLD = 10;
110
112 private const MAX_WAIT_DEFAULT = 10;
114 private const TTL_CACHE_READONLY = 5;
115
117 private const CATEGORY_ROUND = 'round';
119 private const CATEGORY_AUTOCOMMIT = 'auto-commit';
121 private const CATEGORY_GAUGE = 'gauge';
122
124 private const ROUND_CURSORY = 'cursory';
126 private const ROUND_FINALIZED = 'finalized';
128 private const ROUND_APPROVED = 'approved';
130 private const ROUND_COMMIT_CALLBACKS = 'commit-callbacks';
132 private const ROUND_ROLLBACK_CALLBACKS = 'rollback-callbacks';
134 private const ROUND_ERROR = 'error';
135
137 private const READER_INDEX_NONE = -1;
138
139 public function __construct( array $params ) {
140 $this->configure( $params );
141
142 $this->conns = self::newTrackedConnectionsArray();
143 }
144
150 protected function configure( array $params ): void {
151 $this->localDomain = isset( $params['localDomain'] )
152 ? DatabaseDomain::newFromId( $params['localDomain'] )
153 : DatabaseDomain::newUnspecified();
154
155 $this->serverInfo = new ServerInfo();
156 $this->loads = [];
157 foreach ( $this->serverInfo->normalizeServerMaps( $params['servers'] ?? [] ) as $i => $server ) {
158 $this->serverInfo->addServer( $i, $server );
159 $this->loads[$i] = $server['load'];
160 }
161 // If the cluster name is not specified, fallback to the current primary name
162 $this->clusterName = $params['clusterName']
163 ?? $this->serverInfo->getServerName( ServerInfo::WRITER_INDEX );
164
165 if ( isset( $params['readOnlyReason'] ) && is_string( $params['readOnlyReason'] ) ) {
166 $this->readOnlyReason = $params['readOnlyReason'];
167 }
168
169 $this->srvCache = $params['srvCache'] ?? new EmptyBagOStuff();
170 $this->wanCache = $params['wanCache'] ?? WANObjectCache::newEmpty();
171
172 // Note: this parameter is normally absent. It is injectable for testing purposes only.
173 $this->databaseFactory = $params['databaseFactory'] ?? new DatabaseFactory( $params );
174
175 $this->errorLogger = $params['errorLogger'] ?? static function ( Throwable $e ) {
176 trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
177 };
178 $this->logger = $params['logger'] ?? new NullLogger();
179
180 $this->trxProfiler = $params['trxProfiler'] ?? new TransactionProfiler();
181 $this->statsFactory = $params['statsFactory'] ?? StatsFactory::newNull();
182
183 // Set up LoadMonitor
184 $loadMonitorConfig = $params['loadMonitor'] ?? [ 'class' => LoadMonitorNull::class ];
185 $compat = [
186 'LoadMonitor' => LoadMonitor::class,
187 'LoadMonitorNull' => LoadMonitorNull::class
188 ];
189 $class = $loadMonitorConfig['class'];
190 // @phan-suppress-next-line PhanImpossibleCondition
191 if ( isset( $compat[$class] ) ) {
192 $class = $compat[$class];
193 }
194 $this->loadMonitor = new $class(
195 $this,
196 $this->srvCache,
197 $this->wanCache,
198 $this->logger,
199 $this->statsFactory,
200 $loadMonitorConfig
201 );
202
203 if ( isset( $params['chronologyProtector'] ) ) {
204 $this->chronologyProtector = $params['chronologyProtector'];
205 }
206
207 if ( isset( $params['roundStage'] ) ) {
208 if ( $params['roundStage'] === self::STAGE_POSTCOMMIT_CALLBACKS ) {
209 $this->trxRoundStage = self::ROUND_COMMIT_CALLBACKS;
210 } elseif ( $params['roundStage'] === self::STAGE_POSTROLLBACK_CALLBACKS ) {
211 $this->trxRoundStage = self::ROUND_ROLLBACK_CALLBACKS;
212 }
213 }
214
215 $this->cliMode = $params['cliMode'] ?? ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
216
217 $this->defaultGroup = $params['defaultGroup'] ?? self::GROUP_GENERIC;
218 if ( empty( $params['shuffleSharding'] ) ) {
219 $this->uniqueIdentifier = null;
220 } else {
221 $this->uniqueIdentifier = $params['uniqueIdentifier'] ?? null;
222 }
223 }
224
225 private static function newTrackedConnectionsArray(): array {
226 // Note that CATEGORY_GAUGE connections are untracked
227 return [
228 self::CATEGORY_ROUND => [],
229 self::CATEGORY_AUTOCOMMIT => []
230 ];
231 }
232
233 public function getClusterName(): string {
234 return $this->clusterName;
235 }
236
237 public function getLocalDomainID(): string {
238 return $this->localDomain->getId();
239 }
240
242 public function resolveDomainID( DatabaseDomain|string|false $domain ): string {
243 return $this->resolveDomainInstance( $domain )->getId();
244 }
245
246 final protected function resolveDomainInstance( DatabaseDomain|string|false $domain ): DatabaseDomain {
247 if ( $domain instanceof DatabaseDomain ) {
248 return $domain; // already a domain instance
249 } elseif ( $domain === false || $domain === $this->localDomain->getId() ) {
250 return $this->localDomain;
251 } elseif ( isset( $this->domainAliases[$domain] ) ) {
252 $this->domainAliases[$domain] =
253 DatabaseDomain::newFromId( $this->domainAliases[$domain] );
254
255 return $this->domainAliases[$domain];
256 }
257
258 $cachedDomain = $this->nonLocalDomainCache[$domain] ?? null;
259 if ( $cachedDomain === null ) {
260 $cachedDomain = DatabaseDomain::newFromId( $domain );
261 $this->nonLocalDomainCache = [ $domain => $cachedDomain ];
262 }
263
264 return $cachedDomain;
265 }
266
274 protected function sanitizeConnectionFlags( $flags, $domain ) {
275 if ( self::fieldHasBit( $flags, self::CONN_TRX_AUTOCOMMIT ) ) {
276 // Callers use CONN_TRX_AUTOCOMMIT to bypass REPEATABLE-READ staleness without
277 // resorting to row locks (e.g. FOR UPDATE) or to make small out-of-band commits
278 // during larger transactions. This is useful for avoiding lock contention.
279 // Assuming all servers are of the same type (or similar), which is overwhelmingly
280 // the case, use the primary server information to get the attributes. The information
281 // for $i cannot be used since it might be DB_REPLICA, which might require connection
282 // attempts in order to be resolved into a real server index.
283 $attributes = $this->getServerAttributes( ServerInfo::WRITER_INDEX );
284 if ( $attributes[Database::ATTR_DB_LEVEL_LOCKING] ) {
285 // The RDBMS does not support concurrent writes (e.g. SQLite), so attempts
286 // to use separate connections would just cause self-deadlocks. Note that
287 // REPEATABLE-READ staleness is not an issue since DB-level locking means
288 // that transactions are Strict Serializable anyway.
289 $flags &= ~self::CONN_TRX_AUTOCOMMIT;
290 $type = $this->serverInfo->getServerType( ServerInfo::WRITER_INDEX );
291 $this->logger->info( __METHOD__ . ": CONN_TRX_AUTOCOMMIT disallowed ($type)" );
292 } elseif ( isset( $this->tempTablesOnlyMode[$domain] ) ) {
293 // T202116: integration tests are active and queries should be all be using
294 // temporary clone tables (via prefix). Such tables are not visible across
295 // different connections nor can there be REPEATABLE-READ snapshot staleness,
296 // so use the same connection for everything.
297 $flags &= ~self::CONN_TRX_AUTOCOMMIT;
298 }
299 }
300
301 return $flags;
302 }
303
309 private function enforceConnectionFlags( IDatabase $conn, $flags ) {
310 if (
311 self::fieldHasBit( $flags, self::CONN_TRX_AUTOCOMMIT ) ||
312 // Handles with open transactions are avoided since they might be subject
313 // to REPEATABLE-READ snapshots, which could affect the lag estimate query.
314 self::fieldHasBit( $flags, self::CONN_UNTRACKED_GAUGE )
315 ) {
316 if ( $conn->trxLevel() ) {
317 throw new DBUnexpectedError(
318 $conn,
319 'Handle requested with autocommit-mode yet it has a transaction'
320 );
321 }
322
323 $conn->clearFlag( $conn::DBO_TRX ); // auto-commit mode
324 }
325 }
326
334 private function getRandomNonLagged( array $loads, $sessionLagLimit = INF ) {
335 $lags = $this->getLagTimes();
336
337 $dbs = [];
338 foreach ( $loads as $i => $load ) {
339 $srvName = $this->serverInfo->getServerName( $i );
340 $dbs[$srvName] = $i;
341 }
342 // Unset excessively lagged servers from the load group
343 foreach ( $lags as $i => $lag ) {
344 if ( $i !== ServerInfo::WRITER_INDEX ) {
345 // How much lag normally counts as "excessive" for this server
346 $maxServerLag = $this->serverInfo->getServerMaxLag( $i );
347 // How much lag counts as "excessive" for this server given the session
348 $maxServerLag = min( $maxServerLag, $sessionLagLimit );
349
350 $srvName = $this->serverInfo->getServerName( $i );
351 if ( $lag === false && !is_infinite( $maxServerLag ) ) {
352 $this->logger->debug(
353 __METHOD__ . ": server {db_server} is not replicating?",
354 [ 'db_server' => $srvName ]
355 );
356 unset( $loads[$i] );
357 unset( $dbs[$srvName] );
358 } elseif ( $lag > $maxServerLag ) {
359 $this->logger->debug(
360 __METHOD__ .
361 ": server {db_server} has {lag} seconds of lag (>= {maxlag})",
362 [ 'db_server' => $srvName, 'lag' => $lag, 'maxlag' => $maxServerLag ]
363 );
364 unset( $loads[$i] );
365 unset( $dbs[$srvName] );
366 }
367 }
368 }
369
370 if ( array_sum( $loads ) == 0 ) {
371 // All the replicas with non-zero weight are lagged and the primary has zero load.
372 // Inform caller so that it can use switch to read-only mode and use a lagged replica.
373 return false;
374 }
375
376 // Return a server index based on weighted random selection
377 return $this->shuffleSharding( $dbs, $loads );
378 }
379
392 private function shuffleSharding( $dbs, $loads ) {
393 // shuffle sharding doesn't make sense in small groups
394 if ( count( $loads ) <= 3 || $this->uniqueIdentifier === null ) {
395 return ArrayUtils::pickRandom( $loads );
396 }
397
398 ArrayUtils::consistentHashSort( $dbs, $this->uniqueIdentifier );
399 $keys = array_values( $dbs );
400 return ArrayUtils::pickRandom( [
401 $keys[0] => $loads[$keys[0]],
402 $keys[1] => $loads[$keys[1]],
403 $keys[2] => $loads[$keys[2]],
404 ] );
405 }
406
408 public function getReaderIndex( $group = false ) {
409 if ( !$this->serverInfo->hasReplicaServers() ) {
410 // There is only one possible server to use (the primary)
411 return ServerInfo::WRITER_INDEX;
412 }
413
414 if (
415 $group === 'dump' ||
416 $group === 'vslow' ||
417 $group === [ 'dump' ] ||
418 $group === [ 'vslow' ] ||
419 $this->defaultGroup === 'dump' ||
420 $this->defaultGroup === 'vslow'
421 ) {
422 $group = 'vslow';
423 } else {
424 $group = self::GROUP_GENERIC;
425 }
426
427 $index = $this->getExistingReaderIndex( $group );
428 if ( $index !== self::READER_INDEX_NONE ) {
429 // A reader index was already selected for this query group. Keep using it,
430 // since any session replication position was already waited on and any
431 // active transaction will be reused (e.g. for point-in-time snapshots).
432 return $index;
433 }
434
435 // Get the server weight array for this load group
436 if ( $group === 'vslow' ) {
437 // pick the replica with the lowest weight above zero as the vslow
438 $lowestWeight = INF;
439 $loads = $this->loads;
440 foreach ( $this->loads as $index => $weight ) {
441 if ( $weight > 0 && $weight < $lowestWeight ) {
442 $loads = [ $index => $weight ];
443 $lowestWeight = $weight;
444 }
445 }
446 } else {
447 $loads = $this->loads;
448 }
449
450 if ( !$loads ) {
451 $this->logger->info( __METHOD__ . ": no loads for group $group" );
452 return false;
453 }
454
455 // Load any session replication positions, before any connection attempts,
456 // since reading them afterwards can only cause more delay due to possibly
457 // seeing even higher replication positions (e.g. from concurrent requests).
458 $this->loadSessionPrimaryPos();
459
460 // Scale the configured load weights according to each server's load/state.
461 // This can sometimes trigger server connections due to cache regeneration.
462 $this->loadMonitor->scaleLoads( $loads );
463
464 // Pick a server, accounting for load weights, lag, and session consistency
465 $i = $this->pickReaderIndex( $loads );
466 if ( $i === false ) {
467 // Connection attempts failed
468 return false;
469 }
470
471 // If data seen by queries is expected to reflect writes from a prior transaction,
472 // then wait for the chosen server to apply those changes. This is used to improve
473 // session consistency.
474 if ( !$this->awaitSessionPrimaryPos( $i ) ) {
475 // Data will be outdated compared to what was expected
476 $this->setLaggedReplicaMode();
477 }
478
479 // Keep using this server for DB_REPLICA handles for this group
480 if ( $i < 0 ) {
481 throw new UnexpectedValueException( "Cannot set a negative read server index" );
482 }
483 $this->readIndexByGroup[$group] = $i;
484
485 $serverName = $this->getServerName( $i );
486 $this->logger->debug( __METHOD__ . ": using server $serverName for group '$group'" );
487
488 return $i;
489 }
490
497 protected function getExistingReaderIndex( $group ) {
498 return $this->readIndexByGroup[$group] ?? self::READER_INDEX_NONE;
499 }
500
509 private function pickReaderIndex( array $loads ) {
510 if ( $loads === [] ) {
511 throw new InvalidArgumentException( "Load group array is empty" );
512 }
513
514 $i = false;
515 // Quickly look through the available servers for a server that meets criteria...
516 $currentLoads = $loads;
517 while ( count( $currentLoads ) ) {
518 if ( $this->waitForPos && $this->waitForPos->asOfTime() ) {
519 $this->logger->debug( __METHOD__ . ": session has replication position" );
520 // ChronologyProtector::getSessionPrimaryPos called in loadSessionPrimaryPos()
521 // sets "waitForPos" for session consistency. This triggers doWait() after
522 // connect, so it's especially good to avoid lagged servers so as to avoid
523 // excessive delay in that method.
524 $ago = microtime( true ) - $this->waitForPos->asOfTime();
525 // Aim for <= 1 second of waiting (being too picky can backfire)
526 $i = $this->getRandomNonLagged( $currentLoads, $ago + 1 );
527 } else {
528 // Any server with less lag than it's 'max lag' param is preferable
529 $i = $this->getRandomNonLagged( $currentLoads );
530 }
531
532 if ( $i === false && count( $currentLoads ) ) {
533 $this->setLaggedReplicaMode();
534 // All replica DBs lagged, just pick anything.
535 $i = ArrayUtils::pickRandom( $currentLoads );
536 }
537
538 if ( $i === false ) {
539 // pickRandom() returned false.
540 // This is permanent and means the configuration or LoadMonitor
541 // wants us to return false.
542 $this->logger->debug( __METHOD__ . ": no suitable server found" );
543 return false;
544 }
545
546 $serverName = $this->getServerName( $i );
547 $this->logger->debug( __METHOD__ . ": connecting to $serverName..." );
548
549 // Get a connection to this server without triggering complementary connections
550 // to other servers (due to things like lag or read-only checks). We want to avoid
551 // the risk of overhead and recursion here.
552 $conn = $this->getServerConnection( $i, self::DOMAIN_ANY, self::CONN_SILENCE_ERRORS );
553 if ( !$conn ) {
554 $this->logger->warning( __METHOD__ . ": failed connecting to $serverName" );
555 unset( $currentLoads[$i] ); // avoid this server next iteration
556 continue;
557 }
558
559 // Return this server
560 break;
561 }
562
563 // If all servers were down, quit now
564 if ( $currentLoads === [] ) {
565 $this->logger->error( __METHOD__ . ": all servers down" );
566 }
567
568 return $i;
569 }
570
572 public function waitForAll( DBPrimaryPos $pos, $timeout = null ) {
573 $timeout = $timeout ?: self::MAX_WAIT_DEFAULT;
574
575 $oldPos = $this->waitForPos;
576 try {
577 $this->waitForPos = $pos;
578
579 $failedReplicas = [];
580 foreach ( $this->serverInfo->getStreamingReplicaIndexes() as $i ) {
581 if ( isset( $this->loads[$i] ) && $this->loads[$i] > 0 ) {
582 $start = microtime( true );
583 $ok = $this->awaitSessionPrimaryPos( $i, $timeout );
584 if ( !$ok ) {
585 $failedReplicas[] = $this->getServerName( $i );
586 }
587 $timeout -= intval( microtime( true ) - $start );
588 }
589 }
590
591 // Stop spamming logs when only one replica is lagging and we have 5+ replicas.
592 // Mediawiki automatically stops sending queries to the lagged one.
593 $failed = $failedReplicas && ( count( $failedReplicas ) > 1 || $this->getServerCount() < 5 );
594 if ( $failed ) {
595 $this->logger->error(
596 "Timed out waiting for replication to reach {raw_pos}",
597 [
598 'raw_pos' => $pos->__toString(),
599 'failed_hosts' => $failedReplicas,
600 'timeout' => $timeout,
601 'exception' => new RuntimeException()
602 ]
603 );
604 }
605
606 return !$failed;
607 } finally {
608 // Restore the old position; this is used for throttling, not lag-protection
609 $this->waitForPos = $oldPos;
610 }
611 }
612
614 public function getAnyOpenConnection( $i, $flags = 0 ) {
615 $i = ( $i === self::DB_PRIMARY ) ? ServerInfo::WRITER_INDEX : $i;
616 $conn = false;
617 foreach ( $this->conns as $type => $poolConnsByServer ) {
618 if ( $i === self::DB_REPLICA ) {
619 // Consider all existing connections to any server
620 $applicableConnsByServer = $poolConnsByServer;
621 } else {
622 // Consider all existing connections to a specific server
623 $applicableConnsByServer = isset( $poolConnsByServer[$i] )
624 ? [ $i => $poolConnsByServer[$i] ]
625 : [];
626 }
627
628 $conn = $this->pickAnyOpenConnection( $applicableConnsByServer );
629 if ( $conn ) {
630 $this->logger->debug( __METHOD__ . ": found '$type' connection to #$i." );
631 break;
632 }
633 }
634
635 if ( $conn ) {
636 $this->enforceConnectionFlags( $conn, $flags );
637 }
638
639 return $conn;
640 }
641
646 private function pickAnyOpenConnection( array $connsByServer ) {
647 foreach ( $connsByServer as $i => $conns ) {
648 foreach ( $conns as $conn ) {
649 if ( !$conn->isOpen() ) {
650 $this->logger->warning(
651 __METHOD__ .
652 ": pooled DB handle for {db_server} (#$i) has no open connection.",
653 $this->getConnLogContext( $conn )
654 );
655 continue; // some sort of error occurred?
656 }
657 return $conn;
658 }
659 }
660
661 return false;
662 }
663
673 private function awaitSessionPrimaryPos( $index, $timeout = null ) {
674 $timeout = max( 1, intval( $timeout ?: self::MAX_WAIT_DEFAULT ) );
675
676 if ( !$this->waitForPos || $index === ServerInfo::WRITER_INDEX ) {
677 return true;
678 }
679
680 $srvName = $this->getServerName( $index );
681
682 // Check if we already know that the DB has reached this point
683 $key = $this->srvCache->makeGlobalKey( __CLASS__, 'last-known-pos', $srvName, 'v2' );
684
686 $position = $this->srvCache->get( $key );
687 if ( !is_array( $position ) ) {
688 $knownReachedPos = null;
689 } else {
690 $class = $position['_type_'];
691 $knownReachedPos = $class::newFromArray( $position );
692 }
693 if (
694 $knownReachedPos instanceof DBPrimaryPos &&
695 $knownReachedPos->hasReached( $this->waitForPos )
696 ) {
697 $this->logger->debug(
698 __METHOD__ .
699 ": replica DB {db_server} known to be caught up (pos >= $knownReachedPos).",
700 [ 'db_server' => $srvName ]
701 );
702
703 return true;
704 }
705
706 $close = false; // close the connection afterwards
707 $flags = self::CONN_SILENCE_ERRORS;
708 // Check if there is an existing connection that can be used
709 $conn = $this->getAnyOpenConnection( $index, $flags );
710 if ( !$conn ) {
711 // Get a connection to this server without triggering complementary connections
712 // to other servers (due to things like lag or read-only checks). We want to avoid
713 // the risk of overhead and recursion here.
714 $conn = $this->getServerConnection( $index, self::DOMAIN_ANY, $flags );
715 if ( !$conn ) {
716 $this->logger->warning(
717 __METHOD__ . ': failed to connect to {db_server}',
718 [ 'db_server' => $srvName ]
719 );
720
721 return false;
722 }
723 // Avoid connection spam in waitForAll() when connections
724 // are made just for the sake of doing this lag check.
725 $close = true;
726 }
727
728 $this->logger->info(
729 __METHOD__ .
730 ': waiting for replica DB {db_server} to catch up...',
731 $this->getConnLogContext( $conn )
732 );
733
734 $result = $conn->primaryPosWait( $this->waitForPos, $timeout );
735
736 $ok = ( $result !== null && $result != -1 );
737 if ( $ok ) {
738 // Remember that the DB reached this point
739 $this->srvCache->set( $key, $this->waitForPos->toArray(), BagOStuff::TTL_DAY );
740 }
741
742 if ( $close ) {
743 $this->closeConnection( $conn );
744 }
745
746 return $ok;
747 }
748
750 public function getConnection( $i, $groups = [], string|false $domain = false, $flags = 0 ) {
751 if ( self::fieldHasBit( $flags, self::CONN_SILENCE_ERRORS ) ) {
752 throw new UnexpectedValueException(
753 __METHOD__ . ' got CONN_SILENCE_ERRORS; connection is already deferred'
754 );
755 }
756
757 $domain = $this->resolveDomainID( $domain );
758 $role = ( $i === self::DB_PRIMARY || $i === ServerInfo::WRITER_INDEX )
759 ? self::DB_PRIMARY
761
762 return new DBConnRef( $this, [ $i, $groups, $domain, $flags ], $role, $this->modcount );
763 }
764
766 public function getConnectionInternal( $i, $groups = [], $domain = false, $flags = 0 ): IDatabase {
767 $domain = $this->resolveDomainID( $domain );
768 $flags = $this->sanitizeConnectionFlags( $flags, $domain );
769 // If given DB_PRIMARY/DB_REPLICA, resolve it to a specific server index. Resolving
770 // DB_REPLICA might trigger getServerConnection() calls due to the getReaderIndex()
771 // connectivity checks or LoadMonitor::scaleLoads() server state cache regeneration.
772 // The use of getServerConnection() instead of getConnection() avoids infinite loops.
773 $serverIndex = $i;
774 if ( $i === self::DB_PRIMARY ) {
775 $serverIndex = ServerInfo::WRITER_INDEX;
776 } elseif ( $i === self::DB_REPLICA ) {
777 $groupIndex = $this->getReaderIndex( $groups );
778 if ( $groupIndex !== false ) {
779 // Group connection succeeded
780 $serverIndex = $groupIndex;
781 }
782 if ( $serverIndex < 0 ) {
783 $this->reportConnectionError( 'could not connect to any replica DB server' );
784 }
785 } elseif ( !$this->serverInfo->hasServerIndex( $i ) ) {
786 throw new UnexpectedValueException( "Invalid server index index #$i" );
787 }
788 // Get an open connection to that server (might trigger a new connection)
789 return $this->getServerConnection( $serverIndex, $domain, $flags );
790 }
791
793 public function getServerConnection( $i, $domain, $flags = 0 ) {
794 $domainInstance = DatabaseDomain::newFromId( $domain );
795 // Number of connections made before getting the server index and handle
796 $priorConnectionsMade = $this->connectionCounter;
797 // Get an open connection to this server (might trigger a new connection)
798 $conn = $this->reuseOrOpenConnectionForNewRef( $i, $domainInstance, $flags );
799 // Throw an error or otherwise bail out if the connection attempt failed
800 if ( !( $conn instanceof IDatabaseForOwner ) ) {
801 if ( !self::fieldHasBit( $flags, self::CONN_SILENCE_ERRORS ) ) {
802 $this->reportConnectionError();
803 }
804
805 return false;
806 }
807
808 // Profile any new connections caused by this method
809 if ( $this->connectionCounter > $priorConnectionsMade ) {
810 $this->trxProfiler->recordConnection(
811 $conn->getServerName(),
812 $conn->getDBname(),
813 ( $i === ServerInfo::WRITER_INDEX && $this->hasStreamingReplicaServers() )
814 );
815 }
816
817 if ( !$conn->isOpen() ) {
818 $this->lastErrorConn = $conn;
819 // Connection was made but later unrecoverably lost for some reason.
820 // Do not return a handle that will just throw exceptions on use, but
821 // let the calling code, e.g. getReaderIndex(), try another server.
822 if ( !self::fieldHasBit( $flags, self::CONN_SILENCE_ERRORS ) ) {
823 $this->reportConnectionError();
824 }
825 return false;
826 }
827
828 return $conn;
829 }
830
833 $i,
834 $groups = [],
835 $domain = false,
836 $flags = 0
837 ): DBConnRef {
838 if ( self::fieldHasBit( $flags, self::CONN_SILENCE_ERRORS ) ) {
839 throw new UnexpectedValueException(
840 __METHOD__ . ' CONN_SILENCE_ERRORS is not supported'
841 );
842 }
843
844 $domain = $this->resolveDomainID( $domain );
845 $role = ( $i === self::DB_PRIMARY || $i === ServerInfo::WRITER_INDEX )
846 ? self::DB_PRIMARY
847 : self::DB_REPLICA;
848
849 return new DBConnRef( $this, [ $i, $groups, $domain, $flags ], $role, $this->modcount );
850 }
851
868 private function reuseOrOpenConnectionForNewRef( $i, DatabaseDomain $domain, $flags = 0 ) {
869 // Figure out which connection pool to use based on the flags
870 if ( $this->fieldHasBit( $flags, self::CONN_UNTRACKED_GAUGE ) ) {
871 // Use low timeouts, use autocommit mode, ignore transaction rounds
872 $category = self::CATEGORY_GAUGE;
873 } elseif ( self::fieldHasBit( $flags, self::CONN_TRX_AUTOCOMMIT ) ) {
874 // Use autocommit mode, ignore transaction rounds
875 $category = self::CATEGORY_AUTOCOMMIT;
876 } else {
877 // Respect DBO_DEFAULT, respect transaction rounds
878 $category = self::CATEGORY_ROUND;
879 }
880
881 $conn = null;
882 // Reuse a free connection in the pool from any domain if possible. There should only
883 // be one connection in this pool unless either:
884 // - a) IDatabase::databasesAreIndependent() returns true (e.g. postgres) and two
885 // or more database domains have been used during the load balancer's lifetime
886 // - b) Two or more nested function calls used getConnection() on different domains.
887 foreach ( ( $this->conns[$category][$i] ?? [] ) as $poolConn ) {
888 // Check if any required DB domain changes for the new reference are possible
889 // Calling selectDomain() would trigger a reconnect, which will break if a
890 // transaction is active or if there is any other meaningful session state.
891 $isShareable = !(
892 $poolConn->databasesAreIndependent() &&
893 $domain->getDatabase() !== null &&
894 $domain->getDatabase() !== $poolConn->getDBname()
895 );
896 if ( $isShareable ) {
897 $conn = $poolConn;
898 // Make any required DB domain changes for the new reference
899 if ( !$domain->isUnspecified() ) {
900 $conn->selectDomain( $domain );
901 }
902 $this->logger->debug( __METHOD__ . ": reusing connection for $i/$domain" );
903 break;
904 }
905 }
906
907 // If necessary, try to open a new connection and add it to the pool
908 if ( !$conn ) {
909 $conn = $this->reallyOpenConnection(
910 $i,
911 $domain,
912 [ self::INFO_CONN_CATEGORY => $category ]
913 );
914 if ( $conn->isOpen() ) {
915 // Make Database::isReadOnly() respect server-side and configuration-based
916 // read-only mode. Note that replica handles are always seen as read-only
917 // in Database::isReadOnly() and Database::assertIsWritablePrimary().
918 if ( $i === ServerInfo::WRITER_INDEX ) {
919 if ( $this->readOnlyReason !== false ) {
920 $readOnlyReason = $this->readOnlyReason;
921 } elseif ( $this->isPrimaryRunningReadOnly( $conn ) ) {
922 $readOnlyReason = 'The primary database server is running in read-only mode.';
923 } else {
924 $readOnlyReason = false;
925 }
926 $conn->setLBInfo( $conn::LB_READ_ONLY_REASON, $readOnlyReason );
927 }
928 // Connection obtained; check if it belongs to a tracked connection category
929 if ( isset( $this->conns[$category] ) ) {
930 // Track this connection for future reuse
931 $this->conns[$category][$i][] = $conn;
932 }
933 } else {
934 $this->logger->warning( __METHOD__ . ": connection error for $i/$domain" );
935 $this->lastErrorConn = $conn;
936 $conn = null;
937 }
938 }
939
940 if ( $conn instanceof IDatabaseForOwner ) {
941 // Check to make sure that the right domain is selected
942 $this->assertConnectionDomain( $conn, $domain );
943 // Check to make sure that the CONN_* flags are respected
944 $this->enforceConnectionFlags( $conn, $flags );
945 }
946
947 return $conn;
948 }
949
957 private function assertConnectionDomain( Database $conn, DatabaseDomain $domain ) {
958 if ( !$domain->isCompatible( $conn->getDomainID() ) ) {
959 throw new UnexpectedValueException(
960 "Got connection to '{$conn->getDomainID()}', but expected one for '{$domain}'"
961 );
962 }
963 }
964
966 public function getServerAttributes( $i ) {
967 return $this->databaseFactory->attributesFromType(
968 $this->getServerType( $i ),
969 $this->serverInfo->getServerDriver( $i )
970 );
971 }
972
985 protected function reallyOpenConnection( $i, DatabaseDomain $domain, array $lbInfo ) {
986 if ( $this->disabled ) {
987 throw new DBAccessError();
988 }
989
990 $server = $this->serverInfo->getServerInfoStrict( $i );
991 if ( $lbInfo[self::INFO_CONN_CATEGORY] === self::CATEGORY_GAUGE ) {
992 // Use low connection/read timeouts for connection used for gauging server health.
993 // Gauge information should be cached and used to avoid outages. Indefinite hanging
994 // while gauging servers would do the opposite.
995 $server['connectTimeout'] = min( 1, $server['connectTimeout'] ?? INF );
996 $server['receiveTimeout'] = min( 1, $server['receiveTimeout'] ?? INF );
997 // Avoid implicit transactions and avoid any SET query for session variables during
998 // Database::open(). If a server becomes slow, every extra query can cause significant
999 // delays, even with low connect/receive timeouts.
1000 $server['flags'] ??= 0;
1001 $server['flags'] &= ~IDatabase::DBO_DEFAULT;
1002 $server['flags'] |= IDatabase::DBO_GAUGE;
1003 } else {
1004 // Use implicit transactions unless explicitly configured otherwise
1005 $server['flags'] ??= IDatabase::DBO_DEFAULT;
1006 }
1007
1008 if ( !empty( $server['is static'] ) ) {
1009 $topologyRole = IDatabase::ROLE_STATIC_CLONE;
1010 } else {
1011 $topologyRole = ( $i === ServerInfo::WRITER_INDEX )
1012 ? IDatabase::ROLE_STREAMING_MASTER
1013 : IDatabase::ROLE_STREAMING_REPLICA;
1014 }
1015
1016 $conn = $this->databaseFactory->create(
1017 $server['type'],
1018 array_merge( $server, [
1019 // Basic replication role information
1020 'topologyRole' => $topologyRole,
1021 // Use the database specified in $domain (null means "none or entrypoint DB");
1022 // fallback to the $server default if the RDBMs is an embedded library using a
1023 // file on disk since there would be nothing to access to without a DB/file name.
1024 'dbname' => $this->getServerAttributes( $i )[Database::ATTR_DB_IS_FILE]
1025 ? ( $domain->getDatabase() ?? $server['dbname'] ?? null )
1026 : $domain->getDatabase(),
1027 // Override the $server default schema with that of $domain if specified
1028 'schema' => $domain->getSchema(),
1029 // Use the table prefix specified in $domain
1030 'tablePrefix' => $domain->getTablePrefix(),
1031 'srvCache' => $this->srvCache,
1032 'logger' => $this->logger,
1033 'errorLogger' => $this->errorLogger,
1034 'trxProfiler' => $this->trxProfiler,
1035 'lbInfo' => [ self::INFO_SERVER_INDEX => $i ] + $lbInfo
1036 ] ),
1037 Database::NEW_UNCONNECTED
1038 );
1039 // Set alternative table names before any queries can be issued
1040 $conn->setTableAliases( $this->tableAliases );
1041 // Account for any active transaction round and listeners
1042 $this->syncConnectionRoundState( $conn );
1043 if ( $i === ServerInfo::WRITER_INDEX ) {
1044 foreach ( $this->trxRecurringCallbacks as $name => $callback ) {
1045 $conn->setTransactionListener( $name, $callback );
1046 }
1047 }
1048
1049 // Make the connection handle live
1050 try {
1051 $conn->initConnection();
1052 ++$this->connectionCounter;
1053 } catch ( DBConnectionError ) {
1054 $this->lastErrorConn = $conn;
1055 // ignore; let the DB handle the logging
1056 }
1057
1058 if ( $conn->isOpen() ) {
1059 $this->logger->debug( __METHOD__ . ": opened new connection for $i/$domain" );
1060 } else {
1061 $this->logger->warning(
1062 __METHOD__ . ": connection error for $i/{db_domain}",
1063 [ 'db_domain' => $domain->getId() ]
1064 );
1065 }
1066
1067 // Log when many connection are made during a single request/script
1068 $count = 0;
1069 foreach ( $this->conns as $poolConnsByServer ) {
1070 foreach ( $poolConnsByServer as $serverConns ) {
1071 $count += count( $serverConns );
1072 }
1073 }
1074 if ( $count >= self::CONN_HELD_WARN_THRESHOLD ) {
1075 $this->logger->warning(
1076 __METHOD__ . ": {connections}+ connections made (primary={primarydb})",
1077 $this->getConnLogContext(
1078 $conn,
1079 [
1080 'connections' => $count,
1081 'primarydb' => $this->serverInfo->getPrimaryServerName(),
1082 'db_domain' => $domain->getId()
1083 ]
1084 )
1085 );
1086 }
1087
1088 $this->assertConnectionDomain( $conn, $domain );
1089
1090 return $conn;
1091 }
1092
1102 private function loadSessionPrimaryPos() {
1103 if ( !$this->chronologyProtectorCalled && $this->chronologyProtector ) {
1104 $this->chronologyProtectorCalled = true;
1105 $pos = $this->chronologyProtector->getSessionPrimaryPos( $this );
1106 $this->logger->debug( __METHOD__ . ': executed chronology callback.' );
1107 if ( $pos ) {
1108 if ( !$this->waitForPos || $pos->hasReached( $this->waitForPos ) ) {
1109 $this->waitForPos = $pos;
1110 }
1111 }
1112 }
1113 }
1114
1120 private function reportConnectionError( $extraLbError = '' ): never {
1121 if ( $this->lastErrorConn instanceof IDatabaseForOwner ) {
1122 $srvName = $this->lastErrorConn->getServerName();
1123 $lastDbError = $this->lastErrorConn->lastError() ?: 'unknown error';
1124
1125 $exception = new DBConnectionError(
1126 $this->lastErrorConn,
1127 $extraLbError
1128 ? "{$extraLbError}; {$lastDbError} ({$srvName})"
1129 : "{$lastDbError} ({$srvName})"
1130 );
1131
1132 if ( $extraLbError ) {
1133 $this->logger->warning(
1134 __METHOD__ . ": $extraLbError; {last_error} ({db_server})",
1135 $this->getConnLogContext(
1136 $this->lastErrorConn,
1137 [
1138 'method' => __METHOD__,
1139 'last_error' => $lastDbError
1140 ]
1141 )
1142 );
1143 }
1144 } else {
1145 $exception = new DBConnectionError(
1146 null,
1147 $extraLbError ?: 'could not connect to the DB server'
1148 );
1149
1150 if ( $extraLbError ) {
1151 $this->logger->error(
1152 __METHOD__ . ": $extraLbError",
1153 [
1154 'method' => __METHOD__,
1155 'last_error' => '(last connection error missing)'
1156 ]
1157 );
1158 }
1159 }
1160
1161 throw $exception;
1162 }
1163
1165 public function getServerCount() {
1166 return $this->serverInfo->getServerCount();
1167 }
1168
1170 public function hasReplicaServers() {
1171 return $this->serverInfo->hasReplicaServers();
1172 }
1173
1175 public function hasStreamingReplicaServers() {
1176 return $this->serverInfo->hasStreamingReplicaServers();
1177 }
1178
1180 public function getServerName( $i ): string {
1181 return $this->serverInfo->getServerName( $i );
1182 }
1183
1185 public function getServerInfo( $i ) {
1186 return $this->serverInfo->getServerInfo( $i );
1187 }
1188
1190 public function getServerType( $i ) {
1191 return $this->serverInfo->getServerType( $i );
1192 }
1193
1195 public function getPrimaryPos() {
1196 $conn = $this->getAnyOpenConnection( ServerInfo::WRITER_INDEX );
1197 if ( $conn ) {
1198 return $conn->getPrimaryPos();
1199 }
1200
1202 $conn = $this->getConnectionInternal( ServerInfo::WRITER_INDEX, [], false, self::CONN_SILENCE_ERRORS );
1203 // @phan-suppress-next-line PhanRedundantCondition
1204 if ( !$conn ) {
1205 $this->reportConnectionError();
1206 }
1207
1208 // ::getConnectionInternal() should return IDatabaseForOwner but changing signature
1209 // is not straightforward (being implemented in Wikibase)
1210 '@phan-var IDatabaseForOwner $conn';
1211 try {
1212 return $conn->getPrimaryPos();
1213 } finally {
1214 $this->closeConnection( $conn );
1215 }
1216 }
1217
1238 public function reconfigure( array $params ) {
1239 $anyServerDepooled = false;
1240
1241 $paramServers = $params['servers'];
1242 $newIndexByServerIndex = $this->serverInfo->reconfigureServers( $paramServers );
1243 foreach ( $newIndexByServerIndex as $i => $ni ) {
1244 if ( $ni === null ) {
1245 // Server no longer exists in the new config
1246 $anyServerDepooled = true;
1247 // Note that if the primary server is depooled and a replica server promoted
1248 // to new primary, then DB_PRIMARY handles will fail with server index errors
1249 unset( $this->loads[$i] );
1250 }
1251 }
1252
1253 if ( $anyServerDepooled ) {
1254 // NOTE: We could close all connection here, but some may be in the middle of
1255 // a transaction. So instead, we leave it to DBConnRef to close the
1256 // connection when it detects that the modcount has changed and no
1257 // transaction is open.
1258 $this->logger->info( 'Reconfiguring dbs!' );
1259 // Unpin DB_REPLICA connection groups from server indexes
1260 $this->readIndexByGroup = [];
1261 // We could close all connection here, but some may be in the middle of a
1262 // transaction. So instead, we leave it to DBConnRef to close the connection
1263 // when it detects that the modcount has changed and no transaction is open.
1264 $this->conns = self::newTrackedConnectionsArray();
1265 // Bump modification counter to invalidate the connections held by DBConnRef
1266 // instances. This will cause the next call to a method on the DBConnRef
1267 // to get a new connection from getConnectionInternal()
1268 $this->modcount++;
1269 }
1270 }
1271
1273 public function disable( $fname = __METHOD__ ) {
1274 $this->closeAll( $fname );
1275 $this->disabled = true;
1276 }
1277
1279 public function closeAll( $fname = __METHOD__ ) {
1281 $scope = ScopedCallback::newScopedIgnoreUserAbort();
1282 foreach ( $this->getOpenConnections() as $conn ) {
1283 $conn->close( $fname );
1284 }
1285
1286 $this->conns = self::newTrackedConnectionsArray();
1287 }
1288
1295 private function closeConnection( IDatabaseForOwner $conn ) {
1296 if ( $conn instanceof DBConnRef ) {
1297 // Avoid calling close() but still leaving the handle in the pool
1298 throw new RuntimeException( 'Cannot close DBConnRef instance; it must be shareable' );
1299 }
1300
1301 $domain = $conn->getDomainID();
1302 $serverIndex = $conn->getLBInfo( self::INFO_SERVER_INDEX );
1303 if ( $serverIndex === null ) {
1304 throw new UnexpectedValueException( "Handle on '$domain' missing server index" );
1305 }
1306
1307 $srvName = $this->serverInfo->getServerName( $serverIndex );
1308
1309 $found = false;
1310 foreach ( $this->conns as $type => $poolConnsByServer ) {
1311 $key = array_search( $conn, $poolConnsByServer[$serverIndex] ?? [], true );
1312 if ( $key !== false ) {
1313 $found = true;
1314 unset( $this->conns[$type][$serverIndex][$key] );
1315 }
1316 }
1317
1318 if ( !$found ) {
1319 $this->logger->warning(
1320 __METHOD__ .
1321 ": orphaned connection to database {$this->stringifyConn( $conn )} at '$srvName'."
1322 );
1323 }
1324
1325 $this->logger->debug(
1326 __METHOD__ .
1327 ": closing connection to database {$this->stringifyConn( $conn )} at '$srvName'."
1328 );
1329
1330 $conn->close( __METHOD__ );
1331 }
1332
1334 public function finalizePrimaryChanges( $fname = __METHOD__ ) {
1335 $this->assertTransactionRoundStage( [ self::ROUND_CURSORY, self::ROUND_FINALIZED ] );
1337 $scope = ScopedCallback::newScopedIgnoreUserAbort();
1338
1339 $this->trxRoundStage = self::ROUND_ERROR; // "failed" until proven otherwise
1340 // Loop until callbacks stop adding callbacks on other connections
1341 $total = 0;
1342 do {
1343 $count = 0; // callbacks execution attempts
1344 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1345 // Run any pre-commit callbacks while leaving the post-commit ones suppressed.
1346 // Any error should cause all (peer) transactions to be rolled back together.
1347 $count += $conn->runOnTransactionPreCommitCallbacks();
1348 }
1349 $total += $count;
1350 } while ( $count > 0 );
1351 // Defer post-commit callbacks until after COMMIT/ROLLBACK happens on all handles
1352 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1353 $conn->setTrxEndCallbackSuppression( true );
1354 }
1355 $this->trxRoundStage = self::ROUND_FINALIZED;
1356
1357 return $total;
1358 }
1359
1361 public function approvePrimaryChanges( int $maxWriteDuration, $fname = __METHOD__ ) {
1362 $this->assertTransactionRoundStage( self::ROUND_FINALIZED );
1364 $scope = ScopedCallback::newScopedIgnoreUserAbort();
1365
1366 $this->trxRoundStage = self::ROUND_ERROR; // "failed" until proven otherwise
1367 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1368 // Any atomic sections should have been closed by now and there definitely should
1369 // not be any open transactions started by begin() from callers outside Database.
1370 if ( $conn->explicitTrxActive() ) {
1371 throw new DBTransactionError(
1372 $conn,
1373 "Explicit transaction still active; a caller might have failed to call " .
1374 "endAtomic() or cancelAtomic()."
1375 );
1376 }
1377 // Assert that the time to replicate the transaction will be reasonable.
1378 // If this fails, then all DB transactions will be rollback back together.
1379 $time = $conn->pendingWriteQueryDuration( $conn::ESTIMATE_DB_APPLY );
1380 if ( $maxWriteDuration > 0 ) {
1381 if ( $time > $maxWriteDuration ) {
1382 $humanTimeSec = round( $time, 3 );
1383 throw new DBTransactionSizeError(
1384 $conn,
1385 "Transaction spent {time}s in writes, exceeding the {$maxWriteDuration}s limit",
1386 // Message parameters for: transaction-duration-limit-exceeded
1387 [ $time, $maxWriteDuration ],
1388 null,
1389 [ 'time' => $humanTimeSec ]
1390 );
1391 } elseif ( $time > 0 ) {
1392 $timeMs = $time * 1000;
1393 $humanTimeMs = round( $timeMs, $timeMs > 1 ? 0 : 3 );
1394 $this->logger->debug(
1395 "Transaction spent {time_ms}ms in writes, under the {$maxWriteDuration}s limit",
1396 [ 'time_ms' => $humanTimeMs ]
1397 );
1398 }
1399 }
1400 // If a connection sits idle for too long it might be dropped, causing transaction
1401 // writes and session locks to be lost. Ping all the server connections before making
1402 // any attempt to commit the transactions belonging to the active transaction round.
1403 if ( $conn->writesOrCallbacksPending() || $conn->sessionLocksPending() ) {
1404 if ( !$conn->ping() ) {
1405 throw new DBTransactionError(
1406 $conn,
1407 "Pre-commit ping failed on server {$conn->getServerName()}"
1408 );
1409 }
1410 }
1411 }
1412 $this->trxRoundStage = self::ROUND_APPROVED;
1413 }
1414
1416 public function beginPrimaryChanges( $fname = __METHOD__ ) {
1417 if ( $this->trxRoundFname !== null ) {
1418 throw new DBTransactionError(
1419 null,
1420 "Transaction round '{$this->trxRoundFname}' already started"
1421 );
1422 }
1423 $this->assertTransactionRoundStage( self::ROUND_CURSORY );
1425 $scope = ScopedCallback::newScopedIgnoreUserAbort();
1426
1427 // Clear any empty transactions (no writes/callbacks) from the implicit round
1428 $this->flushPrimarySnapshots( $fname );
1429
1430 $this->trxRoundFname = $fname;
1431 $this->trxRoundStage = self::ROUND_ERROR; // "failed" until proven otherwise
1432 // Mark applicable handles as participating in this explicit transaction round.
1433 // For each of these handles, any writes and callbacks will be tied to a single
1434 // transaction. The (peer) handles will reject begin()/commit() calls unless they
1435 // are part of an en masse commit or an en masse rollback.
1436 foreach ( $this->getOpenConnections() as $conn ) {
1437 $this->syncConnectionRoundState( $conn );
1438 }
1439 $this->trxRoundStage = self::ROUND_CURSORY;
1440 }
1441
1443 public function commitPrimaryChanges( $fname = __METHOD__ ) {
1444 $this->assertTransactionRoundStage( self::ROUND_APPROVED );
1446 $scope = ScopedCallback::newScopedIgnoreUserAbort();
1447
1448 $failures = [];
1449
1450 $this->trxRoundFname = null;
1451 $this->trxRoundStage = self::ROUND_ERROR; // "failed" until proven otherwise
1452 // Commit any writes and clear any snapshots as well (callbacks require AUTOCOMMIT).
1453 // Note that callbacks should already be suppressed due to finalizePrimaryChanges().
1454 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1455 try {
1456 $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
1457 } catch ( DBError $e ) {
1458 ( $this->errorLogger )( $e );
1459 $failures[] = "{$conn->getServerName()}: {$e->getMessage()}";
1460 }
1461 }
1462 if ( $failures ) {
1463 throw new DBTransactionError(
1464 null,
1465 "Commit failed on server(s) " . implode( "\n", array_unique( $failures ) )
1466 );
1467 }
1468 // Unmark handles as participating in this explicit transaction round
1469 foreach ( $this->getOpenConnections() as $conn ) {
1470 $this->syncConnectionRoundState( $conn );
1471 }
1472 $this->trxRoundStage = self::ROUND_COMMIT_CALLBACKS;
1473 }
1474
1476 public function runPrimaryTransactionIdleCallbacks( $fname = __METHOD__ ) {
1477 if ( $this->trxRoundStage === self::ROUND_COMMIT_CALLBACKS ) {
1478 $type = IDatabase::TRIGGER_COMMIT;
1479 } elseif ( $this->trxRoundStage === self::ROUND_ROLLBACK_CALLBACKS ) {
1480 $type = IDatabase::TRIGGER_ROLLBACK;
1481 } else {
1482 throw new DBTransactionError(
1483 null,
1484 "Transaction should be in the callback stage (not '{$this->trxRoundStage}')"
1485 );
1486 }
1488 $scope = ScopedCallback::newScopedIgnoreUserAbort();
1489
1490 $oldStage = $this->trxRoundStage;
1491 $this->trxRoundStage = self::ROUND_ERROR; // "failed" until proven otherwise
1492
1493 // Now that the COMMIT/ROLLBACK step is over, enable post-commit callback runs
1494 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1495 $conn->setTrxEndCallbackSuppression( false );
1496 }
1497
1498 $errors = [];
1499 $fname = __METHOD__;
1500 // Loop until callbacks stop adding callbacks on other connections
1501 do {
1502 // Run any pending callbacks for each connection...
1503 $count = 0; // callback execution attempts
1504 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1505 if ( $conn->trxLevel() ) {
1506 continue; // retry in the next iteration, after commit() is called
1507 }
1508 $count += $conn->runOnTransactionIdleCallbacks( $type, $errors );
1509 }
1510 // Clear out any active transactions left over from callbacks...
1511 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1512 if ( $conn->writesPending() ) {
1513 // A callback from another handle wrote to this one and DBO_TRX is set
1514 $fnames = implode( ', ', $conn->pendingWriteCallers() );
1515 $this->logger->info(
1516 "$fname: found writes pending ($fnames).",
1517 $this->getConnLogContext(
1518 $conn,
1519 [ 'exception' => new RuntimeException() ]
1520 )
1521 );
1522 $this->statsFactory->getCounter( 'rdbms_callback_writes_found_total' )
1523 ->setLabel( 'db_cluster', $this->getClusterName() )
1524 ->setLabel( 'db_server', $conn->getServerName() )
1525 ->increment();
1526 } elseif ( $conn->trxLevel() ) {
1527 // A callback from another handle read from this one and DBO_TRX is set,
1528 // which can easily happen if there is only one DB (no replicas)
1529 $this->logger->debug( "$fname: found empty transaction." );
1530 }
1531 try {
1532 $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
1533 } catch ( DBError $ex ) {
1534 $errors[] = $ex;
1535 }
1536 }
1537 } while ( $count > 0 );
1538
1539 $this->trxRoundStage = $oldStage;
1540
1541 return $errors[0] ?? null;
1542 }
1543
1545 public function runPrimaryTransactionListenerCallbacks( $fname = __METHOD__ ) {
1546 if ( $this->trxRoundStage === self::ROUND_COMMIT_CALLBACKS ) {
1547 $type = IDatabase::TRIGGER_COMMIT;
1548 } elseif ( $this->trxRoundStage === self::ROUND_ROLLBACK_CALLBACKS ) {
1549 $type = IDatabase::TRIGGER_ROLLBACK;
1550 } else {
1551 throw new DBTransactionError(
1552 null,
1553 "Transaction should be in the callback stage (not '{$this->trxRoundStage}')"
1554 );
1555 }
1557 $scope = ScopedCallback::newScopedIgnoreUserAbort();
1558
1559 $errors = [];
1560 $this->trxRoundStage = self::ROUND_ERROR; // "failed" until proven otherwise
1561 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1562 $conn->runTransactionListenerCallbacks( $type, $errors );
1563 }
1564 $this->trxRoundStage = self::ROUND_CURSORY;
1565
1566 return $errors[0] ?? null;
1567 }
1568
1570 public function rollbackPrimaryChanges( $fname = __METHOD__ ) {
1572 $scope = ScopedCallback::newScopedIgnoreUserAbort();
1573
1574 $this->trxRoundFname = null;
1575 $this->trxRoundStage = self::ROUND_ERROR; // "failed" until proven otherwise
1576 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1577 $conn->rollback( $fname, $conn::FLUSHING_ALL_PEERS );
1578 }
1579 // Unmark handles as participating in this explicit transaction round
1580 foreach ( $this->getOpenConnections() as $conn ) {
1581 $this->syncConnectionRoundState( $conn );
1582 }
1583 $this->trxRoundStage = self::ROUND_ROLLBACK_CALLBACKS;
1584 }
1585
1587 public function flushPrimarySessions( $fname = __METHOD__ ) {
1588 $this->assertTransactionRoundStage( [ self::ROUND_CURSORY ] );
1589 if ( $this->hasPrimaryChanges() ) {
1590 // Any transaction should have been rolled back beforehand
1591 throw new DBTransactionError( null, "Cannot reset session while writes are pending" );
1592 }
1593
1594 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1595 $conn->flushSession( $fname, $conn::FLUSHING_ALL_PEERS );
1596 }
1597 }
1598
1603 private function assertTransactionRoundStage( $stage ) {
1604 $stages = (array)$stage;
1605
1606 if ( !in_array( $this->trxRoundStage, $stages, true ) ) {
1607 $stageList = implode(
1608 '/',
1609 array_map( static fn ( $v ) => "'$v'", $stages )
1610 );
1611 $transactionName = $this->trxRoundFname ?? '<unknown>';
1612 throw new DBTransactionError(
1613 null,
1614 "Transaction '$transactionName' round stage must be '$stageList' (not '{$this->trxRoundStage}')"
1615 );
1616 }
1617 }
1618
1628 private function syncConnectionRoundState( Database $conn ) {
1629 if ( $conn->getLBInfo( self::INFO_CONN_CATEGORY ) !== self::CATEGORY_ROUND ) {
1630 return; // transaction rounds do not apply to these connections
1631 }
1632
1633 if ( $this->trxRoundFname !== null ) {
1634 // Explicit transaction round
1635 $trxRoundLevel = 1;
1636 } else {
1637 // Implicit auto-commit round (cli mode) or implicit transaction round (web mode)
1638 $trxRoundLevel = ( $this->cliMode ? 0 : 1 );
1639 }
1640
1641 // CATEGORY_ROUND DB handles with DBO_DEFAULT are considered "round aware" and the
1642 // load balancer will take over the logic of when DBO_TRX is set for such DB handles.
1643 // DBO_TRX will be set only when a transaction round is active (explicit or implicit).
1644 if ( $conn->getFlag( $conn::DBO_DEFAULT ) ) {
1645 if ( $trxRoundLevel ) {
1646 // Wrap queries in a transaction joined to the active transaction round
1647 $conn->setFlag( $conn::DBO_TRX );
1648 } else {
1649 // Do not wrap queries in a transaction (no active transaction round)
1650 $conn->clearFlag( $conn::DBO_TRX );
1651 }
1652 }
1653
1654 // Note that DBO_TRX is normally only set above or by DatabaseFactory applying
1655 // DBO_DEFAULT. However, integration tests might directly force DBO_TRX in the
1656 // server configuration arrays. Such handles will still be flushed during calls
1657 // to {@link LoadBalancer::commitPrimaryChanges()}.
1658 if ( $conn->getFlag( $conn::DBO_TRX ) ) {
1659 // DB handle is participating in the active transaction round
1660 $conn->setLBInfo( $conn::LB_TRX_ROUND_LEVEL, $trxRoundLevel );
1661 $conn->setLBInfo( $conn::LB_TRX_ROUND_FNAME, $this->trxRoundFname );
1662 } else {
1663 // DB handle is not participating in any transaction rounds
1664 $conn->setLBInfo( $conn::LB_TRX_ROUND_LEVEL, 0 );
1665 $conn->setLBInfo( $conn::LB_TRX_ROUND_FNAME, null );
1666 }
1667 }
1668
1670 public function flushReplicaSnapshots( $fname = __METHOD__ ) {
1671 foreach ( $this->conns as $poolConnsByServer ) {
1672 foreach ( $poolConnsByServer as $serverIndex => $serverConns ) {
1673 if ( $serverIndex === ServerInfo::WRITER_INDEX ) {
1674 continue; // skip primary
1675 }
1676 foreach ( $serverConns as $conn ) {
1677 $conn->flushSnapshot( $fname );
1678 }
1679 }
1680 }
1681 }
1682
1684 public function flushPrimarySnapshots( $fname = __METHOD__ ) {
1685 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1686 $conn->flushSnapshot( $fname );
1687 }
1688 }
1689
1691 public function hasPrimaryConnection() {
1692 return (bool)$this->getAnyOpenConnection( ServerInfo::WRITER_INDEX );
1693 }
1694
1696 public function hasPrimaryChanges() {
1697 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1698 if ( $conn->writesOrCallbacksPending() ) {
1699 return true;
1700 }
1701 }
1702
1703 return false;
1704 }
1705
1707 public function lastPrimaryChangeTimestamp() {
1708 $lastTime = null;
1709 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1710 $lastTime = max( $lastTime, $conn->lastDoneWrites() );
1711 }
1712
1713 return $lastTime;
1714 }
1715
1717 public function hasOrMadeRecentPrimaryChanges( $age = null ) {
1718 $age ??= self::MAX_WAIT_DEFAULT;
1719
1720 return ( $this->hasPrimaryChanges()
1721 || $this->lastPrimaryChangeTimestamp() > microtime( true ) - $age );
1722 }
1723
1726 $fnames = [];
1727 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1728 $fnames = array_merge( $fnames, $conn->pendingWriteCallers() );
1729 }
1730
1731 return $fnames;
1732 }
1733
1735 public function explicitTrxActive() {
1736 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1737 if ( $conn->explicitTrxActive() ) {
1738 return true;
1739 }
1740 }
1741 return false;
1742 }
1743
1744 private function setLaggedReplicaMode(): void {
1745 $this->laggedReplicaMode = true;
1746 $this->logger->warning( __METHOD__ . ": setting lagged replica mode" );
1747 }
1748
1750 public function laggedReplicaUsed() {
1751 return $this->laggedReplicaMode;
1752 }
1753
1755 public function getReadOnlyReason() {
1756 if ( $this->readOnlyReason !== false ) {
1757 return $this->readOnlyReason;
1758 } elseif ( $this->isPrimaryRunningReadOnly() ) {
1759 return 'The primary database server is running in read-only mode.';
1760 }
1761
1762 return false;
1763 }
1764
1770 private function isPrimaryRunningReadOnly( ?IDatabaseForOwner $conn = null ) {
1771 // Context will often be HTTP GET/HEAD; heavily cache the results
1772 return (bool)$this->wanCache->getWithSetCallback(
1773 // Note that table prefixes are not related to server-side read-only mode
1774 $this->wanCache->makeGlobalKey(
1775 'rdbms-server-readonly',
1776 $this->serverInfo->getPrimaryServerName()
1777 ),
1778 self::TTL_CACHE_READONLY,
1779 function ( $oldValue ) use ( $conn ) {
1780 $scope = $this->trxProfiler->silenceForScope();
1781 $conn ??= $this->getServerConnection(
1782 ServerInfo::WRITER_INDEX,
1783 self::DOMAIN_ANY,
1784 self::CONN_SILENCE_ERRORS
1785 );
1786 if ( $conn ) {
1787 try {
1788 $value = (int)$conn->serverIsReadOnly();
1789 } catch ( DBError ) {
1790 $value = is_int( $oldValue ) ? $oldValue : 0;
1791 }
1792 } else {
1793 $value = 0;
1794 }
1795 ScopedCallback::consume( $scope );
1796
1797 return $value;
1798 },
1799 [
1800 'busyValue' => 0,
1801 'pcTTL' => WANObjectCache::TTL_PROC_LONG
1802 ]
1803 );
1804 }
1805
1807 public function pingAll() {
1808 $success = true;
1809 foreach ( $this->getOpenConnections() as $conn ) {
1810 if ( !$conn->ping() ) {
1811 $success = false;
1812 }
1813 }
1814
1815 return $success;
1816 }
1817
1822 private function getOpenConnections() {
1823 foreach ( $this->conns as $poolConnsByServer ) {
1824 foreach ( $poolConnsByServer as $serverConns ) {
1825 foreach ( $serverConns as $conn ) {
1826 yield $conn;
1827 }
1828 }
1829 }
1830 }
1831
1836 private function getOpenPrimaryConnections() {
1837 foreach ( $this->conns as $poolConnsByServer ) {
1839 foreach ( ( $poolConnsByServer[ServerInfo::WRITER_INDEX] ?? [] ) as $conn ) {
1840 yield $conn;
1841 }
1842 }
1843 }
1844
1846 public function getMaxLag() {
1847 $host = '';
1848 $maxLag = -1;
1849 $maxIndex = 0;
1850
1851 if ( $this->serverInfo->hasReplicaServers() ) {
1852 $lagTimes = $this->getLagTimes();
1853 foreach ( $lagTimes as $i => $lag ) {
1854 // Allowing the value to be unset due to stale cache (T361824)
1855 $load = $this->loads[$i] ?? 0;
1856 if ( $load > 0 && $lag > $maxLag ) {
1857 $maxLag = $lag;
1858 $host = $this->serverInfo->getServerInfoStrict( $i, 'host' );
1859 $maxIndex = $i;
1860 }
1861 }
1862 }
1863
1864 return [ $host, $maxLag, $maxIndex ];
1865 }
1866
1868 public function getLagTimes() {
1869 if ( !$this->hasReplicaServers() ) {
1870 return [ ServerInfo::WRITER_INDEX => 0 ]; // no replication = no lag
1871 }
1872 $fname = __METHOD__;
1873 return $this->wanCache->getWithSetCallback(
1874 $this->wanCache->makeGlobalKey( 'rdbms-lags', $this->getClusterName() ),
1875 // Add jitter to avoid stampede
1876 10 + mt_rand( 1, 10 ),
1877 function () use ( $fname ) {
1878 $lags = [];
1879 foreach ( $this->serverInfo->getStreamingReplicaIndexes() as $i ) {
1880 $conn = $this->getServerConnection(
1881 $i,
1882 self::DOMAIN_ANY,
1883 self::CONN_SILENCE_ERRORS | self::CONN_UNTRACKED_GAUGE
1884 );
1885 if ( $conn ) {
1886 $lags[$i] = $conn->getLag();
1887 $conn->close( $fname );
1888 } else {
1889 $lags[$i] = false;
1890 }
1891 }
1892 return $lags;
1893 },
1894 [ 'lockTSE' => 30 ]
1895 );
1896 }
1897
1899 public function waitForPrimaryPos( IDatabase $conn ) {
1900 if ( $conn->getLBInfo( self::INFO_SERVER_INDEX ) === ServerInfo::WRITER_INDEX ) {
1901 return true; // not a replica DB server
1902 }
1903
1904 // Get the current primary DB position, opening a connection only if needed
1905 $flags = self::CONN_SILENCE_ERRORS;
1906 $primaryConn = $this->getAnyOpenConnection( ServerInfo::WRITER_INDEX, $flags );
1907 if ( $primaryConn ) {
1908 $pos = $primaryConn->getPrimaryPos();
1909 } else {
1910 $primaryConn = $this->getServerConnection( ServerInfo::WRITER_INDEX, self::DOMAIN_ANY, $flags );
1911 if ( !$primaryConn ) {
1912 throw new DBReplicationWaitError(
1913 null,
1914 "Could not obtain a primary database connection to get the position"
1915 );
1916 }
1917 $pos = $primaryConn->getPrimaryPos();
1918 $this->closeConnection( $primaryConn );
1919 }
1920
1921 if ( $pos instanceof DBPrimaryPos && $conn instanceof IDatabaseForOwner ) {
1922 $this->logger->debug( __METHOD__ . ': waiting' );
1923 $result = $conn->primaryPosWait( $pos, self::MAX_WAIT_DEFAULT );
1924 $ok = ( $result !== null && $result != -1 );
1925 if ( $ok ) {
1926 $this->logger->debug( __METHOD__ . ': done waiting (success)' );
1927 } else {
1928 $this->logger->debug( __METHOD__ . ': done waiting (failure)' );
1929 }
1930 } else {
1931 $ok = false; // something is misconfigured
1932 $this->logger->error(
1933 __METHOD__ . ': could not get primary pos for {db_server}',
1934 $this->getConnLogContext( $conn, [ 'exception' => new RuntimeException() ] )
1935 );
1936 }
1937
1938 return $ok;
1939 }
1940
1942 public function setTransactionListener( $name, ?callable $callback = null ) {
1943 if ( $callback ) {
1944 $this->trxRecurringCallbacks[$name] = $callback;
1945 } else {
1946 unset( $this->trxRecurringCallbacks[$name] );
1947 }
1948 foreach ( $this->getOpenPrimaryConnections() as $conn ) {
1949 $conn->setTransactionListener( $name, $callback );
1950 }
1951 }
1952
1953 public function setTableAliases( array $aliases ) {
1954 $this->tableAliases = $aliases;
1955 }
1956
1957 public function setDomainAliases( array $aliases ) {
1958 $this->domainAliases = $aliases;
1959 }
1960
1962 public function setLocalDomainPrefix( $prefix ) {
1963 $oldLocalDomain = $this->localDomain;
1964 $this->localDomain = new DatabaseDomain(
1965 $this->localDomain->getDatabase(),
1966 $this->localDomain->getSchema(),
1967 $prefix
1968 );
1969
1970 // Update the prefix for existing connections.
1971 // Existing DBConnRef handles will not be affected.
1972 foreach ( $this->getOpenConnections() as $conn ) {
1973 if ( $oldLocalDomain->equals( $conn->getDomainID() ) ) {
1974 $conn->tablePrefix( $prefix );
1975 }
1976 }
1977 }
1978
1980 public function redefineLocalDomain( $domain ) {
1981 $this->closeAll( __METHOD__ );
1982 $this->localDomain = DatabaseDomain::newFromId( $domain );
1983 }
1984
1986 public function setTempTablesOnlyMode( $value, $domain ) {
1987 $old = $this->tempTablesOnlyMode[$domain] ?? false;
1988 if ( $value ) {
1989 $this->tempTablesOnlyMode[$domain] = true;
1990 } else {
1991 unset( $this->tempTablesOnlyMode[$domain] );
1992 }
1993
1994 return $old;
1995 }
1996
2002 private function stringifyConn( IDatabase $conn ) {
2003 return $conn->getLBInfo( self::INFO_SERVER_INDEX ) . '/' . $conn->getDomainID();
2004 }
2005
2011 private function fieldHasBit( int $flags, int $bit ) {
2012 return ( ( $flags & $bit ) === $bit );
2013 }
2014
2022 protected function getConnLogContext( IDatabase $conn, array $extras = [] ) {
2023 return $extras + [
2024 'db_server' => $conn->getServerName(),
2025 'db_domain' => $conn->getDomainID(),
2026 ];
2027 }
2028
2034 public function setMockTime( &$time ) {
2035 if ( !$this->loadMonitor instanceof LoadMonitor ) {
2036 throw new LogicException( 'Cannot set mock time on ' . $this::class . ' (consider adding ' .
2037 'setMockTime to ILoadMonitor).' );
2038 }
2039 $this->loadMonitor->setMockTime( $time );
2040 }
2041
2042 public function setDefaultGroupName( string $defaultGroup ): void {
2043 $this->defaultGroup = $defaultGroup;
2044 }
2045}
const DB_REPLICA
Definition defines.php:26
const DBO_DEFAULT
Definition defines.php:13
const DB_PRIMARY
Definition defines.php:28
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:69
A collection of static methods to play with arrays.
Abstract class for any ephemeral data store.
Definition BagOStuff.php:73
No-op implementation that stores nothing.
Multi-datacenter aware caching interface.
Provide a given client with protection against visible database lag.
Exception class for attempted DB access.
Helper class used for automatically re-using IDatabase connections and lazily establishing the actual...
Definition DBConnRef.php:39
Database error base class.
Definition DBError.php:22
Exception class for replica DB wait errors.
Class to handle database/schema/prefix specifications for IDatabase.
Constructs Database objects.
rollbackPrimaryChanges( $fname=__METHOD__)
Issue ROLLBACK only on primary, only if queries were done on connection.1.37
getAnyOpenConnection( $i, $flags=0)
sanitizeConnectionFlags( $flags, $domain)
Sanitize connection flags provided by a call to getConnection()
flushReplicaSnapshots( $fname=__METHOD__)
Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshots.
getServerAttributes( $i)
Get basic attributes of the server with the specified index without connecting.array (Database::ATTRI...
getLocalDomainID()
Get the local (and default) database domain ID of connection handles.
hasReplicaServers()
Whether there are any replica servers configured.This scans the list of servers defined in configurat...
getServerType( $i)
Get the RDBMS type of the server with the specified index (e.g."mysql", "sqlite")string One of (mysql...
hasStreamingReplicaServers()
Whether any replica servers use streaming replication from the primary server.This scans the list of ...
setTableAliases(array $aliases)
Make certain table names use their own database, schema, and table prefix when passed into SQL querie...
lastPrimaryChangeTimestamp()
Get the last time that a tracked connection was used to commit a write.Should only be called from the...
closeAll( $fname=__METHOD__)
Close all open connections.
resolveDomainInstance(DatabaseDomain|string|false $domain)
waitForAll(DBPrimaryPos $pos, $timeout=null)
Set the primary wait position and wait for ALL replica DBs to catch up to it.This method is only inte...
getServerConnection( $i, $domain, $flags=0)
Get a DB handle for a specific server index.This is an internal utility method for methods like LoadB...
hasPrimaryChanges()
Whether there are pending changes or callbacks in a transaction by this thread.bool 1....
getMaxLag()
Get the name and lag time of the most-lagged replica server.This is useful for maintenance scripts th...
getConnLogContext(IDatabase $conn, array $extras=[])
Create a log context to pass to PSR-3 logger functions.
redefineLocalDomain( $domain)
Close all connection and redefine the local domain for testing or schema creation....
setDomainAliases(array $aliases)
Convert certain database domains to alternative ones.
hasPrimaryConnection()
bool Whether a primary connection is already open 1.37
laggedReplicaUsed()
Whether a highly "lagged" replica database connection was queried.This method will never cause a new ...
getConnection( $i, $groups=[], string|false $domain=false, $flags=0)
Get a lazy-connecting database handle for a specific or virtual (DB_PRIMARY/DB_REPLICA) server index....
getReadOnlyReason()
This method may trigger a DB connection if not yet done string|false Reason the primary is read-only ...
waitForPrimaryPos(IDatabase $conn)
Wait for a replica DB to reach a specified primary position.If $conn is not a replica server connecti...
hasOrMadeRecentPrimaryChanges( $age=null)
Check if this load balancer object had any recent or still pending writes issued against it by this P...
explicitTrxActive()
Determine whether an explicit transaction is active on any open primary connection....
setTempTablesOnlyMode( $value, $domain)
Indicate whether the tables on this domain are only temporary tables for testing.In "temporary tables...
flushPrimarySessions( $fname=__METHOD__)
Release/destroy session-level named locks, table locks, and temp tables.Only call this function right...
getExistingReaderIndex( $group)
Get the server index chosen for DB_REPLICA connections for the given query group.
beginPrimaryChanges( $fname=__METHOD__)
Flush any primary transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)The DBO_TRX setting wi...
setLocalDomainPrefix( $prefix)
Set a new table prefix for the existing local domain ID for testing.1.33
approvePrimaryChanges(int $maxWriteDuration, $fname=__METHOD__)
Perform all pre-commit checks for things like replication safety.Use this only for multi-database com...
getMaintenanceConnectionRef( $i, $groups=[], $domain=false, $flags=0)
Get a DB handle, suitable for migrations and schema changes, for a server index.The DBConnRef methods...
getConnectionInternal( $i, $groups=[], $domain=false, $flags=0)
Only to be used by DBConnRef IDatabase
runPrimaryTransactionListenerCallbacks( $fname=__METHOD__)
Run all recurring post-COMMIT/ROLLBACK listener callbacks.Exception|null The first exception or null ...
pendingPrimaryChangeCallers()
Get the list of callers that have pending primary changes.string[] List of method names 1....
getServerName( $i)
Get the readable name of the server with the specified index.string Readable server name,...
reallyOpenConnection( $i, DatabaseDomain $domain, array $lbInfo)
Open a new network connection to a server (uncached)
flushPrimarySnapshots( $fname=__METHOD__)
Commit all primary DB transactions so as to flush any REPEATABLE-READ or SSI snapshots....
resolveDomainID(DatabaseDomain|string|false $domain)
string Value of $domain if it is foreign or the local domain otherwise 1.32
runPrimaryTransactionIdleCallbacks( $fname=__METHOD__)
Consume and run all pending post-COMMIT/ROLLBACK callbacks and commit dangling transactions....
setTransactionListener( $name, ?callable $callback=null)
Set a callback via IDatabase::setTransactionListener() on all current and future primary connections ...
reconfigure(array $params)
Apply updated configuration.
commitPrimaryChanges( $fname=__METHOD__)
Issue COMMIT on all open primary connections to flush changes and view snapshots.1....
getLagTimes()
Get an estimate of replication lag (in seconds) for each server.Results are cached for a short time i...
getServerInfo( $i)
Return the server configuration map for the server with the specified index.array|false Server config...
getPrimaryPos()
Get the current primary replication position.DBPrimaryPos|false Returns false if not applicable 1....
getServerCount()
Get the number of servers defined in configuration.int
disable( $fname=__METHOD__)
Close all connections and disable this load balancer.Any attempt to open a new connection will result...
getClusterName()
Get the name of the overall cluster of database servers managing the dataset.
getReaderIndex( $group=false)
Get the specific server index of the reader connection for a given group.This takes into account load...
finalizePrimaryChanges( $fname=__METHOD__)
Run pre-commit callbacks and defer execution of post-commit callbacks.Use this only for multi-databas...
setDefaultGroupName(string $defaultGroup)
Set the default group name for all load balancers.
Basic DB load monitor with no external dependencies.
Container for accessing information about the database servers in a database cluster.
Detect high-contention DB queries via profiling calls.
This is the primary interface for validating metrics definitions, caching defined metrics,...
return[ 'config-schema-inverse'=>['default'=>['ConfigRegistry'=>['main'=> 'MediaWiki\\Config\\GlobalVarConfig::newInstance',], 'Sitename'=> 'MediaWiki', 'Server'=> false, 'CanonicalServer'=> false, 'ServerName'=> false, 'AssumeProxiesUseDefaultProtocolPorts'=> true, 'HttpsPort'=> 443, 'ForceHTTPS'=> false, 'ScriptPath'=> '/wiki', 'UsePathInfo'=> null, 'Script'=> false, 'LoadScript'=> false, 'RestPath'=> false, 'StylePath'=> false, 'LocalStylePath'=> false, 'ExtensionAssetsPath'=> false, 'ExtensionDirectory'=> null, 'StyleDirectory'=> null, 'ArticlePath'=> false, 'UploadPath'=> false, 'ImgAuthPath'=> false, 'ThumbPath'=> false, 'UploadDirectory'=> false, 'FileCacheDirectory'=> false, 'Logo'=> false, 'Logos'=> false, 'Favicon'=> '/favicon.ico', 'AppleTouchIcon'=> false, 'ReferrerPolicy'=> false, 'TmpDirectory'=> false, 'UploadBaseUrl'=> '', 'UploadStashScalerBaseUrl'=> false, 'ActionPaths'=>[], 'MainPageIsDomainRoot'=> false, 'EnableUploads'=> false, 'UploadStashMaxAge'=> 21600, 'EnableAsyncUploads'=> false, 'EnableAsyncUploadsByURL'=> false, 'UploadMaintenance'=> false, 'IllegalFileChars'=> ':\\/\\\\', 'DeletedDirectory'=> false, 'ImgAuthDetails'=> false, 'ImgAuthUrlPathMap'=>[], 'LocalFileRepo'=>['class'=> 'MediaWiki\\FileRepo\\LocalRepo', 'name'=> 'local', 'directory'=> null, 'scriptDirUrl'=> null, 'favicon'=> null, 'url'=> null, 'hashLevels'=> null, 'thumbScriptUrl'=> null, 'transformVia404'=> null, 'deletedDir'=> null, 'deletedHashLevels'=> null, 'updateCompatibleMetadata'=> null, 'reserializeMetadata'=> null,], 'ForeignFileRepos'=>[], 'UseInstantCommons'=> false, 'UseSharedUploads'=> false, 'SharedUploadDirectory'=> null, 'SharedUploadPath'=> null, 'HashedSharedUploadDirectory'=> true, 'RepositoryBaseUrl'=> 'https:'FetchCommonsDescriptions'=> false, 'SharedUploadDBname'=> false, 'SharedUploadDBprefix'=> '', 'CacheSharedUploads'=> true, 'ForeignUploadTargets'=>['local',], 'UploadDialog'=>['fields'=>['description'=> true, 'date'=> false, 'categories'=> false,], 'licensemessages'=>['local'=> 'generic-local', 'foreign'=> 'generic-foreign',], 'comment'=>['local'=> '', 'foreign'=> '',], 'format'=>['filepage'=> ' $DESCRIPTION', 'description'=> ' $TEXT', 'ownwork'=> '', 'license'=> '', 'uncategorized'=> '',],], 'FileBackends'=>[], 'LockManagers'=>[], 'ShowEXIF'=> null, 'UpdateCompatibleMetadata'=> false, 'AllowCopyUploads'=> false, 'CopyUploadsDomains'=>[], 'CopyUploadsFromSpecialUpload'=> false, 'CopyUploadProxy'=> false, 'CopyUploadTimeout'=> false, 'CopyUploadAllowOnWikiDomainConfig'=> false, 'MaxUploadSize'=> 104857600, 'MinUploadChunkSize'=> 1024, 'UploadNavigationUrl'=> false, 'UploadMissingFileUrl'=> false, 'ThumbnailScriptPath'=> false, 'SharedThumbnailScriptPath'=> false, 'HashedUploadDirectory'=> true, 'CSPUploadEntryPoint'=> true, 'FileExtensions'=>['png', 'gif', 'jpg', 'jpeg', 'webp',], 'ProhibitedFileExtensions'=>['html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht', 'php', 'phtml', 'php3', 'php4', 'php5', 'phps', 'phar', 'shtml', 'jhtml', 'pl', 'py', 'cgi', 'exe', 'scr', 'dll', 'msi', 'vbs', 'bat', 'com', 'pif', 'cmd', 'vxd', 'cpl', 'xml',], 'MimeTypeExclusions'=>['text/html', 'application/javascript', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', 'application/x-php', 'text/x-php', 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', 'text/scriptlet', 'application/x-msdownload', 'application/x-msmetafile', 'application/java', 'application/xml', 'text/xml',], 'CheckFileExtensions'=> true, 'StrictFileExtensions'=> true, 'DisableUploadScriptChecks'=> false, 'UploadSizeWarning'=> false, 'TrustedMediaFormats'=>['BITMAP', 'AUDIO', 'VIDEO', 'image/svg+xml', 'application/pdf',], 'MediaHandlers'=>[], 'NativeImageLazyLoading'=> false, 'ParserTestMediaHandlers'=>['image/jpeg'=> 'MockBitmapHandler', 'image/png'=> 'MockBitmapHandler', 'image/gif'=> 'MockBitmapHandler', 'image/tiff'=> 'MockBitmapHandler', 'image/webp'=> 'MockBitmapHandler', 'image/x-ms-bmp'=> 'MockBitmapHandler', 'image/x-bmp'=> 'MockBitmapHandler', 'image/x-xcf'=> 'MockBitmapHandler', 'image/svg+xml'=> 'MockSvgHandler', 'image/vnd.djvu'=> 'MockDjVuHandler',], 'UseImageResize'=> true, 'UseImageMagick'=> false, 'ImageMagickConvertCommand'=> '/usr/bin/convert', 'MaxInterlacingAreas'=>[], 'SharpenParameter'=> '0x0.4', 'SharpenReductionThreshold'=> 0.85, 'ImageMagickTempDir'=> false, 'CustomConvertCommand'=> false, 'JpegTran'=> '/usr/bin/jpegtran', 'JpegPixelFormat'=> 'yuv420', 'JpegQuality'=> 80, 'Exiv2Command'=> '/usr/bin/exiv2', 'Exiftool'=> '/usr/bin/exiftool', 'SVGConverters'=>['ImageMagick'=> ' $path/convert -background "#ffffff00" -thumbnail $widthx$height\\! $input PNG:$output', 'inkscape'=> ' $path/inkscape -w $width -o $output $input', 'batik'=> 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input', 'rsvg'=> ' $path/rsvg-convert -w $width -h $height -o $output $input', 'ImagickExt'=>['SvgHandler::rasterizeImagickExt',],], 'SVGConverter'=> 'ImageMagick', 'SVGConverterPath'=> '', 'SVGMaxSize'=> 5120, 'SVGMetadataCutoff'=> 5242880, 'SVGNativeRendering'=> true, 'SVGNativeRenderingSizeLimit'=> 51200, 'MediaInTargetLanguage'=> true, 'MaxImageArea'=> 12500000, 'MaxAnimatedGifArea'=> 12500000, 'TiffThumbnailType'=>[], 'ThumbnailEpoch'=> '20030516000000', 'AttemptFailureEpoch'=> 1, 'IgnoreImageErrors'=> false, 'GenerateThumbnailOnParse'=> true, 'ShowArchiveThumbnails'=> true, 'EnableAutoRotation'=> null, 'Antivirus'=> null, 'AntivirusSetup'=>['clamav'=>['command'=> 'clamscan --no-summary ', 'codemap'=>[0=> 0, 1=> 1, 52=> -1, ' *'=> false,], 'messagepattern'=> '/.*?:(.*)/sim',],], 'AntivirusRequired'=> true, 'VerifyMimeType'=> true, 'MimeTypeFile'=> 'internal', 'MimeInfoFile'=> 'internal', 'MimeDetectorCommand'=> null, 'TrivialMimeDetection'=> false, 'XMLMimeTypes'=>['http:'svg'=> 'image/svg+xml', 'http:'http:'html'=> 'text/html',], 'ImageLimits'=>[[320, 240,], [640, 480,], [800, 600,], [1024, 768,], [1280, 1024,], [2560, 2048,],], 'ThumbLimits'=>[120, 150, 180, 200, 250, 300,], 'ThumbnailNamespaces'=>[6,], 'ThumbnailSteps'=> null, 'ThumbnailStepsRatio'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'UserEmailConfirmationUseHTML'=> false, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ImageLinksSchemaMigrationStage'=> 769, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory',],], 'text'=> 'MediaWiki\\Content\\TextContentHandler', 'unknown'=> 'MediaWiki\\Content\\FallbackContentHandler',], 'NamespaceContentModels'=>[], 'TextModelsToParse'=>['wikitext', 'javascript', 'css',], 'CompressRevisions'=> false, 'ExternalStores'=>[], 'ExternalServers'=>[], 'DefaultExternalStore'=> false, 'RevisionCacheExpiry'=> 604800, 'PageLanguageUseDB'=> false, 'DiffEngine'=> null, 'ExternalDiffEngine'=> false, 'Wikidiff2Options'=>[], 'RequestTimeLimit'=> null, 'TransactionalTimeLimit'=> 120, 'CriticalSectionTimeLimit'=> 180.0, 'MiserMode'=> false, 'DisableQueryPages'=> false, 'QueryCacheLimit'=> 1000, 'WantedPagesThreshold'=> 1, 'AllowSlowParserFunctions'=> false, 'AllowSchemaUpdates'=> true, 'MaxArticleSize'=> 2048, 'MemoryLimit'=> '50M', 'PoolCounterConf'=> null, 'PoolCountClientConf'=>['servers'=>['127.0.0.1',], 'timeout'=> 0.1,], 'MaxUserDBWriteDuration'=> false, 'MaxJobDBWriteDuration'=> false, 'LinkHolderBatchSize'=> 1000, 'MaximumMovedPages'=> 100, 'ForceDeferredUpdatesPreSend'=> false, 'MultiShardSiteStats'=> false, 'CacheDirectory'=> false, 'MainCacheType'=> 0, 'MessageCacheType'=> -1, 'ParserCacheType'=> -1, 'SessionCacheType'=> -1, 'AnonSessionCacheType'=> false, 'LanguageConverterCacheType'=> -1, 'ObjectCaches'=>[0=>['class'=> 'Wikimedia\\ObjectCache\\EmptyBagOStuff', 'reportDupes'=> false,], 1=>['class'=> 'MediaWiki\\ObjectCache\\SqlBagOStuff', 'loggroup'=> 'SQLBagOStuff',], 'memcached-php'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPhpBagOStuff', 'loggroup'=> 'memcached',], 'memcached-pecl'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPeclBagOStuff', 'loggroup'=> 'memcached',], 'hash'=>['class'=> 'Wikimedia\\ObjectCache\\HashBagOStuff', 'reportDupes'=> false,], 'apc'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,], 'apcu'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,],], 'WANObjectCache'=>[], 'MicroStashType'=> -1, 'MainStash'=> 1, 'ParsoidCacheConfig'=>['StashType'=> null, 'StashDuration'=> 86400, 'WarmParsoidParserCache'=> false,], 'ParsoidSelectiveUpdateSampleRate'=> 0, 'ParserCacheFilterConfig'=>['pcache'=>['default'=>['minCpuTime'=> 0,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'PHPSessionHandling'=> 'warn', 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'MediaWiki\\Language\\LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], 'UseCombinedLoginLink'=> false, 'Edititis'=> false, 'Send404Code'=> true, 'ShowRollbackEditCount'=> 10, 'EnableCanonicalServerLink'=> false, 'InterwikiLogoOverride'=>[], 'ResourceModules'=>[], 'ResourceModuleSkinStyles'=>[], 'ResourceLoaderSources'=>[], 'ResourceBasePath'=> null, 'ResourceLoaderMaxage'=>[], 'ResourceLoaderDebug'=> false, 'ResourceLoaderMaxQueryLength'=> false, 'ResourceLoaderValidateJS'=> true, 'ResourceLoaderEnableJSProfiler'=> false, 'ResourceLoaderStorageEnabled'=> true, 'ResourceLoaderStorageVersion'=> 1, 'ResourceLoaderEnableSourceMapLinks'=> true, 'AllowSiteCSSOnRestrictedPages'=> false, 'VueDevelopmentMode'=> false, 'CodexDevelopmentDir'=> null, 'MetaNamespace'=> false, 'MetaNamespaceTalk'=> false, 'CanonicalNamespaceNames'=>[-2=> 'Media', -1=> 'Special', 0=> '', 1=> 'Talk', 2=> 'User', 3=> 'User_talk', 4=> 'Project', 5=> 'Project_talk', 6=> 'File', 7=> 'File_talk', 8=> 'MediaWiki', 9=> 'MediaWiki_talk', 10=> 'Template', 11=> 'Template_talk', 12=> 'Help', 13=> 'Help_talk', 14=> 'Category', 15=> 'Category_talk',], 'ExtraNamespaces'=>[], 'ExtraGenderNamespaces'=>[], 'NamespaceAliases'=>[], 'LegalTitleChars'=> ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 'CapitalLinks' => true, 'CapitalLinkOverrides' => [ ], 'NamespacesWithSubpages' => [ 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 15 => true, ], 'ContentNamespaces' => [ 0, ], 'ShortPagesNamespaceExclusions' => [ ], 'ExtraSignatureNamespaces' => [ ], 'InvalidRedirectTargets' => [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect', 'Mylog', ], 'DisableHardRedirects' => false, 'FixDoubleRedirects' => false, 'LocalInterwikis' => [ ], 'InterwikiExpiry' => 10800, 'InterwikiCache' => false, 'InterwikiScopes' => 3, 'InterwikiFallbackSite' => 'wiki', 'RedirectSources' => false, 'SiteTypes' => [ 'mediawiki' => 'MediaWiki\\Site\\MediaWikiSite', ], 'MaxTocLevel' => 999, 'MaxPPNodeCount' => 1000000, 'MaxTemplateDepth' => 100, 'MaxPPExpandDepth' => 100, 'UrlProtocols' => [ 'bitcoin:', 'ftp: 'ftps: 'geo:', 'git: 'gopher: 'http: 'https: 'irc: 'ircs: 'magnet:', 'mailto:', 'matrix:', 'mms: 'news:', 'nntp: 'redis: 'sftp: 'sip:', 'sips:', 'sms:', 'ssh: 'svn: 'tel:', 'telnet: 'urn:', 'wikipedia: 'worldwind: 'xmpp:', ' ], 'CleanSignatures' => true, 'AllowExternalImages' => false, 'AllowExternalImagesFrom' => '', 'EnableImageWhitelist' => false, 'TidyConfig' => [ ], 'ParsoidSettings' => [ 'useSelser' => true, ], 'ParsoidExperimentalParserFunctionOutput' => false, 'UseLegacyMediaStyles' => false, 'RawHtml' => false, 'ExternalLinkTarget' => false, 'NoFollowLinks' => true, 'NoFollowNsExceptions' => [ ], 'NoFollowDomainExceptions' => [ 'mediawiki.org', ], 'RegisterInternalExternals' => false, 'ExternalLinksIgnoreDomains' => [ ], 'AllowDisplayTitle' => true, 'RestrictDisplayTitle' => true, 'ExpensiveParserFunctionLimit' => 100, 'PreprocessorCacheThreshold' => 1000, 'EnableScaryTranscluding' => false, 'TranscludeCacheExpiry' => 3600, 'EnableMagicLinks' => [ 'ISBN' => false, 'PMID' => false, 'RFC' => false, ], 'ParserEnableUserLanguage' => false, 'ArticleCountMethod' => 'link', 'ActiveUserDays' => 30, 'LearnerEdits' => 10, 'LearnerMemberSince' => 4, 'ExperiencedUserEdits' => 500, 'ExperiencedUserMemberSince' => 30, 'ManualRevertSearchRadius' => 15, 'RevertedTagMaxDepth' => 15, 'CentralIdLookupProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\CentralId\\LocalIdLookup', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', 'HideUserUtils', ], ], ], 'CentralIdLookupProvider' => 'local', 'UserRegistrationProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\Registration\\LocalUserRegistrationProvider', 'services' => [ 'ConnectionProvider', ], ], ], 'PasswordPolicy' => [ 'policies' => [ 'bureaucrat' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'sysop' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'interface-admin' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'bot' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'default' => [ 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true, ], 'PasswordCannotBeSubstringInUsername' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'PasswordCannotMatchDefaults' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'MaximalPasswordLength' => [ 'value' => 4096, 'suggestChangeOnLogin' => true, ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], ], ], 'checks' => [ 'MinimalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimalPasswordLength', ], 'MinimumPasswordLengthToLogin' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimumPasswordLengthToLogin', ], 'PasswordCannotBeSubstringInUsername' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotBeSubstringInUsername', ], 'PasswordCannotMatchDefaults' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotMatchDefaults', ], 'MaximalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMaximalPasswordLength', ], 'PasswordNotInCommonList' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordNotInCommonList', ], ], ], 'AuthManagerConfig' => null, 'AuthManagerAutoConfig' => [ 'preauth' => [ 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider', 'sort' => 0, ], ], 'primaryauth' => [ 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', 'UserOptionsLookup', ], 'args' => [ [ 'authoritative' => false, ], ], 'sort' => 0, ], 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'args' => [ [ 'authoritative' => true, ], ], 'sort' => 100, ], ], 'secondaryauth' => [ 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider', 'sort' => 100, ], 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'sort' => 200, ], ], ], 'RememberMe' => 'choose', 'ReauthenticateTime' => [ 'default' => 3600, ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'default' => true, ], 'ChangeCredentialsBlacklist' => [ 'MediaWiki\\Auth\\TemporaryPasswordAuthenticationRequest', ], 'RemoveCredentialsBlacklist' => [ 'MediaWiki\\Auth\\PasswordAuthenticationRequest', ], 'InvalidPasswordReset' => true, 'PasswordDefault' => 'pbkdf2', 'PasswordConfig' => [ 'A' => [ 'class' => 'MediaWiki\\Password\\MWOldPassword', ], 'B' => [ 'class' => 'MediaWiki\\Password\\MWSaltedPassword', ], 'pbkdf2-legacyA' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'A', 'pbkdf2', ], ], 'pbkdf2-legacyB' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'B', 'pbkdf2', ], ], 'bcrypt' => [ 'class' => 'MediaWiki\\Password\\BcryptPassword', 'cost' => 9, ], 'pbkdf2' => [ 'class' => 'MediaWiki\\Password\\Pbkdf2PasswordUsingOpenSSL', 'algo' => 'sha512', 'cost' => '30000', 'length' => '64', ], 'argon2' => [ 'class' => 'MediaWiki\\Password\\Argon2Password', 'algo' => 'auto', ], ], 'PasswordResetRoutes' => [ 'username' => true, 'email' => true, ], 'MaxSigChars' => 255, 'SignatureValidation' => 'warning', 'SignatureAllowedLintErrors' => [ 'obsolete-tag', ], 'MaxNameChars' => 255, 'ReservedUsernames' => [ 'MediaWiki default', 'Conversion script', 'Maintenance script', 'Template namespace initialisation script', 'ScriptImporter', 'Delete page script', 'Move page script', 'Command line script', 'Unknown user', 'msg:double-redirect-fixer', 'msg:usermessage-editor', 'msg:proxyblocker', 'msg:sorbs', 'msg:spambot_username', 'msg:autochange-username', ], 'DefaultUserOptions' => [ 'ccmeonemails' => 0, 'date' => 'default', 'diffonly' => 0, 'diff-type' => 'table', 'disablemail' => 0, 'editfont' => 'monospace', 'editondblclick' => 0, 'editrecovery' => 0, 'editsectiononrightclick' => 0, 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, 'enotifwatchlistpages' => 1, 'extendwatchlist' => 1, 'fancysig' => 0, 'forceeditsummary' => 0, 'forcesafemode' => 0, 'gender' => 'unknown', 'hidecategorization' => 1, 'hideminor' => 0, 'hidepatrolled' => 0, 'imagesize' => 2, 'minordefault' => 0, 'newpageshidepatrolled' => 0, 'nickname' => '', 'norollbackdiff' => 0, 'prefershttps' => 1, 'previewonfirst' => 0, 'previewontop' => 1, 'pst-cssjs' => 1, 'rcdays' => 7, 'rcenhancedfilters-disable' => 0, 'rclimit' => 50, 'requireemail' => 0, 'search-match-redirect' => true, 'search-special-page' => 'Search', 'search-thumbnail-extra-namespaces' => true, 'searchlimit' => 20, 'showhiddencats' => 0, 'shownumberswatching' => 1, 'showrollbackconfirmation' => 0, 'skin' => false, 'skin-responsive' => 1, 'thumbsize' => 5, 'underline' => 2, 'useeditwarning' => 1, 'uselivepreview' => 0, 'usenewrc' => 1, 'watchcreations' => 1, 'watchcreations-expiry' => 'infinite', 'watchdefault' => 1, 'watchdefault-expiry' => 'infinite', 'watchdeletion' => 0, 'watchlistdays' => 7, 'watchlisthideanons' => 0, 'watchlisthidebots' => 0, 'watchlisthidecategorization' => 1, 'watchlisthideliu' => 0, 'watchlisthideminor' => 0, 'watchlisthideown' => 0, 'watchlisthidepatrolled' => 0, 'watchlistreloadautomatically' => 0, 'watchlistunwatchlinks' => 0, 'watchmoves' => 0, 'watchrollback' => 0, 'watchuploads' => 1, 'watchrollback-expiry' => 'infinite', 'watchstar-expiry' => 'infinite', 'wlenhancedfilters-disable' => 0, 'wllimit' => 250, ], 'ConditionalUserOptions' => [ ], 'HiddenPrefs' => [ ], 'UserJsPrefLimit' => 100, 'InvalidUsernameCharacters' => '@:>=', 'UserrightsInterwikiDelimiter' => '@', 'SecureLogin' => false, 'AuthenticationTokenVersion' => null, 'SessionProviders' => [ 'MediaWiki\\Session\\CookieSessionProvider' => [ 'class' => 'MediaWiki\\Session\\CookieSessionProvider', 'args' => [ [ 'priority' => 30, ], ], 'services' => [ 'JwtCodec', 'UrlUtils', ], ], 'MediaWiki\\Session\\BotPasswordSessionProvider' => [ 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => [ [ 'priority' => 75, ], ], 'services' => [ 'GrantsInfo', ], ], ], 'AutoCreateTempUser' => [ 'known' => false, 'enabled' => false, 'actions' => [ 'edit', ], 'genPattern' => '~$1', 'matchPattern' => null, 'reservedPattern' => '~$1', 'serialProvider' => [ 'type' => 'local', 'useYear' => true, ], 'serialMapping' => [ 'type' => 'readable-numeric', ], 'expireAfterDays' => 90, 'notifyBeforeExpirationDays' => 10, ], 'AutoblockExemptions' => [ ], 'AutoblockExpiry' => 86400, 'BlockAllowsUTEdit' => true, 'BlockCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 19, ], 'BlockDisablesLogin' => false, 'EnableMultiBlocks' => false, 'WhitelistRead' => false, 'WhitelistReadRegexp' => false, 'EmailConfirmToEdit' => false, 'HideIdentifiableRedirects' => true, 'GroupPermissions' => [ '*' => [ 'createaccount' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'viewmyprivateinfo' => true, 'editmyprivateinfo' => true, 'editmyoptions' => true, ], 'user' => [ 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'movefile' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'minoredit' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, 'editmyuserjsredirect' => true, 'sendemail' => true, 'applychangetags' => true, 'changetags' => true, 'viewmywatchlist' => true, 'editmywatchlist' => true, ], 'autoconfirmed' => [ 'autoconfirmed' => true, 'editsemiprotected' => true, ], 'bot' => [ 'bot' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'nominornewtalk' => true, 'autopatrol' => true, 'suppressredirect' => true, 'apihighlimits' => true, ], 'sysop' => [ 'block' => true, 'createaccount' => true, 'delete' => true, 'bigdelete' => true, 'deletedhistory' => true, 'deletedtext' => true, 'undelete' => true, 'editcontentmodel' => true, 'editinterface' => true, 'editsitejson' => true, 'edituserjson' => true, 'import' => true, 'importupload' => true, 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'patrol' => true, 'autopatrol' => true, 'protect' => true, 'editprotected' => true, 'rollback' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'unwatchedpages' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'blockemail' => true, 'markbotedits' => true, 'apihighlimits' => true, 'browsearchive' => true, 'noratelimit' => true, 'movefile' => true, 'unblockself' => true, 'suppressredirect' => true, 'mergehistory' => true, 'managechangetags' => true, 'deletechangetags' => true, ], 'interface-admin' => [ 'editinterface' => true, 'editsitecss' => true, 'editsitejson' => true, 'editsitejs' => true, 'editusercss' => true, 'edituserjson' => true, 'edituserjs' => true, ], 'bureaucrat' => [ 'userrights' => true, 'noratelimit' => true, 'renameuser' => true, ], 'suppress' => [ 'hideuser' => true, 'suppressrevision' => true, 'viewsuppressed' => true, 'suppressionlog' => true, 'deleterevision' => true, 'deletelogentry' => true, ], ], 'PrivilegedGroups' => [ 'bureaucrat', 'interface-admin', 'suppress', 'sysop', ], 'RevokePermissions' => [ ], 'GroupInheritsPermissions' => [ ], 'ImplicitGroups' => [ '*', 'user', 'autoconfirmed', ], 'GroupsAddToSelf' => [ ], 'GroupsRemoveFromSelf' => [ ], 'RestrictedGroups' => [ ], 'UserRequirementsPrivateConditions' => [ ], 'RestrictionTypes' => [ 'create', 'edit', 'move', 'upload', ], 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop', ], 'CascadingRestrictionLevels' => [ 'sysop', ], 'SemiprotectedRestrictionLevels' => [ 'autoconfirmed', ], 'NamespaceProtection' => [ ], 'NonincludableNamespaces' => [ ], 'AutoConfirmAge' => 0, 'AutoConfirmCount' => 0, 'Autopromote' => [ 'autoconfirmed' => [ '&', [ 1, null, ], [ 2, null, ], ], ], 'AutopromoteOnce' => [ 'onEdit' => [ ], ], 'AutopromoteOnceLogInRC' => true, 'AutopromoteOnceRCExcludedGroups' => [ ], 'AddGroups' => [ ], 'RemoveGroups' => [ ], 'AvailableRights' => [ ], 'ImplicitRights' => [ ], 'DeleteRevisionsLimit' => 0, 'DeleteRevisionsBatchSize' => 1000, 'HideUserContribLimit' => 1000, 'AccountCreationThrottle' => [ [ 'count' => 0, 'seconds' => 86400, ], ], 'TempAccountCreationThrottle' => [ [ 'count' => 1, 'seconds' => 600, ], [ 'count' => 6, 'seconds' => 86400, ], ], 'TempAccountNameAcquisitionThrottle' => [ [ 'count' => 60, 'seconds' => 86400, ], ], 'SpamRegex' => [ ], 'SummarySpamRegex' => [ ], 'EnableDnsBlacklist' => false, 'DnsBlacklistUrls' => [ ], 'ProxyList' => [ ], 'ProxyWhitelist' => [ ], 'SoftBlockRanges' => [ ], 'ApplyIpBlocksToXff' => false, 'RateLimits' => [ 'edit' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], 'user' => [ 90, 60, ], ], 'move' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], 'upload' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'rollback' => [ 'user' => [ 10, 60, ], 'newbie' => [ 5, 120, ], ], 'mailpassword' => [ 'ip' => [ 5, 3600, ], ], 'sendemail' => [ 'ip' => [ 5, 86400, ], 'newbie' => [ 5, 86400, ], 'user' => [ 20, 86400, ], ], 'changeemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'confirmemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'purge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'linkpurge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'renderfile' => [ 'ip' => [ 700, 30, ], 'user' => [ 700, 30, ], ], 'renderfile-nonstandard' => [ 'ip' => [ 70, 30, ], 'user' => [ 70, 30, ], ], 'stashedit' => [ 'ip' => [ 30, 60, ], 'newbie' => [ 30, 60, ], ], 'stashbasehtml' => [ 'ip' => [ 5, 60, ], 'newbie' => [ 5, 60, ], ], 'changetags' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'editcontentmodel' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], ], 'RateLimitsExcludedIPs' => [ ], 'PutIPinRC' => true, 'QueryPageDefaultLimit' => 50, 'ExternalQuerySources' => [ ], 'PasswordAttemptThrottle' => [ [ 'count' => 5, 'seconds' => 300, ], [ 'count' => 150, 'seconds' => 172800, ], ], 'GrantPermissions' => [ 'basic' => [ 'autocreateaccount' => true, 'autoconfirmed' => true, 'autopatrol' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'nominornewtalk' => true, 'patrolmarks' => true, 'read' => true, 'unwatchedpages' => true, ], 'highvolume' => [ 'bot' => true, 'apihighlimits' => true, 'noratelimit' => true, 'markbotedits' => true, ], 'import' => [ 'import' => true, 'importupload' => true, ], 'editpage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], ], 'GrantPermissionGroups' => [ 'basic' => 'hidden', 'editpage' => 'page-interaction', 'createeditmovepage' => 'page-interaction', 'editprotected' => 'page-interaction', 'patrol' => 'page-interaction', 'uploadfile' => 'file-interaction', 'uploadeditmovefile' => 'file-interaction', 'sendemail' => 'email', 'viewmywatchlist' => 'watchlist-interaction', 'editviewmywatchlist' => 'watchlist-interaction', 'editmycssjs' => 'customization', 'editmyoptions' => 'customization', 'editinterface' => 'administration', 'editsiteconfig' => 'administration', 'rollback' => 'administration', 'blockusers' => 'administration', 'delete' => 'administration', 'viewdeleted' => 'administration', 'viewrestrictedlogs' => 'administration', 'protect' => 'administration', 'oversight' => 'administration', 'createaccount' => 'administration', 'mergehistory' => 'administration', 'import' => 'administration', 'highvolume' => 'high-volume', 'privateinfo' => 'private-information', ], 'GrantRiskGroups' => [ 'basic' => 'low', 'editpage' => 'low', 'createeditmovepage' => 'low', 'editprotected' => 'vandalism', 'patrol' => 'low', 'uploadfile' => 'low', 'uploadeditmovefile' => 'low', 'sendemail' => 'security', 'viewmywatchlist' => 'low', 'editviewmywatchlist' => 'low', 'editmycssjs' => 'security', 'editmyoptions' => 'security', 'editinterface' => 'vandalism', 'editsiteconfig' => 'security', 'rollback' => 'low', 'blockusers' => 'vandalism', 'delete' => 'vandalism', 'viewdeleted' => 'vandalism', 'viewrestrictedlogs' => 'security', 'protect' => 'vandalism', 'oversight' => 'security', 'createaccount' => 'low', 'mergehistory' => 'vandalism', 'import' => 'security', 'highvolume' => 'low', 'privateinfo' => 'low', ], 'EnableBotPasswords' => true, 'BotPasswordsCluster' => false, 'BotPasswordsDatabase' => false, 'SecretKey' => false, 'JwtPrivateKey' => false, 'JwtPublicKey' => false, 'AllowUserJs' => false, 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => false, 'CSPFalsePositiveUrls' => [ 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'chrome-extension' => true, ], 'AllowCrossOrigin' => false, 'RestAllowCrossOriginCookieAuth' => false, 'SessionSecret' => false, 'CookieExpiration' => 2592000, 'ExtendedLoginCookieExpiration' => 15552000, 'SessionCookieJwtExpiration' => 14400, 'CookieDomain' => '', 'CookiePath' => '/', 'CookieSecure' => 'detect', 'CookiePrefix' => false, 'CookieHttpOnly' => true, 'CookieSameSite' => null, 'CacheVaryCookies' => [ ], 'SessionName' => false, 'CookieSetOnAutoblock' => true, 'CookieSetOnIpBlock' => true, 'DebugLogFile' => '', 'DebugLogPrefix' => '', 'DebugRedirects' => false, 'DebugRawPage' => false, 'DebugComments' => false, 'DebugDumpSql' => false, 'TrxProfilerLimits' => [ 'GET' => [ 'masterConns' => 0, 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'POST-nonwrite' => [ 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'PostSend-GET' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 10000, 'maxAffected' => 1000, 'masterConns' => 0, 'writes' => 0, ], 'PostSend-POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'JobRunner' => [ 'readQueryTime' => 30, 'writeQueryTime' => 5, 'readQueryRows' => 100000, 'maxAffected' => 500, ], 'Maintenance' => [ 'writeQueryTime' => 5, 'maxAffected' => 1000, ], ], 'DebugLogGroups' => [ ], 'MWLoggerDefaultSpi' => [ 'class' => 'MediaWiki\\Logger\\LegacySpi', ], 'ShowDebug' => false, 'SpecialVersionShowHooks' => false, 'ShowExceptionDetails' => false, 'LogExceptionBacktrace' => true, 'PropagateErrors' => true, 'ShowHostnames' => false, 'OverrideHostname' => false, 'DevelopmentWarnings' => false, 'DeprecationReleaseLimit' => false, 'Profiler' => [ ], 'StatsdServer' => false, 'StatsdMetricPrefix' => 'MediaWiki', 'StatsTarget' => null, 'StatsFormat' => null, 'StatsPrefix' => 'mediawiki', 'OpenTelemetryConfig' => null, 'PageInfoTransclusionLimit' => 50, 'EnableJavaScriptTest' => false, 'CachePrefix' => false, 'DebugToolbar' => false, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCEngines' => [ 'redis' => 'MediaWiki\\RCFeed\\RedisPubSubFeedEngine', 'udp' => 'MediaWiki\\RCFeed\\UDPRCFeedEngine', ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, ], 'UnwatchedPageThreshold' => false, 'RecentChangesFlags' => [ 'newpage' => [ 'letter' => 'newpageletter', 'title' => 'recentchanges-label-newpage', 'legend' => 'recentchanges-legend-newpage', 'grouping' => 'any', ], 'minor' => [ 'letter' => 'minoreditletter', 'title' => 'recentchanges-label-minor', 'legend' => 'recentchanges-legend-minor', 'class' => 'minoredit', 'grouping' => 'all', ], 'bot' => [ 'letter' => 'boteditletter', 'title' => 'recentchanges-label-bot', 'legend' => 'recentchanges-legend-bot', 'class' => 'botedit', 'grouping' => 'all', ], 'unpatrolled' => [ 'letter' => 'unpatrolledletter', 'title' => 'recentchanges-label-unpatrolled', 'legend' => 'recentchanges-legend-unpatrolled', 'grouping' => 'any', ], ], 'WatchlistExpiry' => false, 'EnableWatchlistLabels' => false, 'WatchlistLabelsMaxPerUser' => 100, 'WatchlistPurgeRate' => 0.1, 'WatchlistExpiryMaxDuration' => '1 year', 'EnableChangesListQueryPartitioning' => false, 'RightsPage' => null, 'RightsUrl' => null, 'RightsText' => null, 'RightsIcon' => null, 'UseCopyrightUpload' => false, 'MaxCredits' => 0, 'ShowCreditsIfMax' => true, 'ImportSources' => [ ], 'ImportTargetNamespace' => null, 'ExportAllowHistory' => true, 'ExportMaxHistory' => 0, 'ExportAllowListContributors' => false, 'ExportMaxLinkDepth' => 0, 'ExportFromNamespaces' => false, 'ExportAllowAll' => false, 'ExportPagelistLimit' => 5000, 'XmlDumpSchemaVersion' => '0.11', 'WikiFarmSettingsDirectory' => null, 'WikiFarmSettingsExtension' => 'yaml', 'ExtensionFunctions' => [ ], 'ExtensionMessagesFiles' => [ ], 'MessagesDirs' => [ ], 'TranslationAliasesDirs' => [ ], 'ExtensionEntryPointListFiles' => [ ], 'EnableParserLimitReporting' => true, 'ValidSkinNames' => [ ], 'SpecialPages' => [ ], 'ExtensionCredits' => [ ], 'Hooks' => [ ], 'ServiceWiringFiles' => [ ], 'JobClasses' => [ 'deletePage' => 'MediaWiki\\Page\\DeletePageJob', 'refreshLinks' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'deleteLinks' => 'MediaWiki\\Page\\DeleteLinksJob', 'htmlCacheUpdate' => 'MediaWiki\\JobQueue\\Jobs\\HTMLCacheUpdateJob', 'sendMail' => [ 'class' => 'MediaWiki\\Mail\\EmaillingJob', 'services' => [ 'Emailer', ], ], 'enotifNotify' => [ 'class' => 'MediaWiki\\RecentChanges\\RecentChangeNotifyJob', 'services' => [ 'RecentChangeLookup', ], ], 'fixDoubleRedirect' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\DoubleRedirectJob', 'services' => [ 'RevisionLookup', 'MagicWordFactory', 'WikiPageFactory', ], 'needsPage' => true, ], 'AssembleUploadChunks' => 'MediaWiki\\JobQueue\\Jobs\\AssembleUploadChunksJob', 'PublishStashedFile' => 'MediaWiki\\JobQueue\\Jobs\\PublishStashedFileJob', 'ThumbnailRender' => 'MediaWiki\\JobQueue\\Jobs\\ThumbnailRenderJob', 'UploadFromUrl' => 'MediaWiki\\JobQueue\\Jobs\\UploadFromUrlJob', 'recentChangesUpdate' => 'MediaWiki\\RecentChanges\\RecentChangesUpdateJob', 'refreshLinksPrioritized' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'refreshLinksDynamic' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'activityUpdateJob' => 'MediaWiki\\Watchlist\\ActivityUpdateJob', 'categoryMembershipChange' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryMembershipChangeJob', 'services' => [ 'RecentChangeFactory', ], ], 'CategoryCountUpdateJob' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryCountUpdateJob', 'services' => [ 'ConnectionProvider', 'NamespaceInfo', ], ], 'clearUserWatchlist' => 'MediaWiki\\Watchlist\\ClearUserWatchlistJob', 'watchlistExpiry' => 'MediaWiki\\Watchlist\\WatchlistExpiryJob', 'cdnPurge' => 'MediaWiki\\JobQueue\\Jobs\\CdnPurgeJob', 'userGroupExpiry' => 'MediaWiki\\User\\UserGroupExpiryJob', 'clearWatchlistNotifications' => 'MediaWiki\\Watchlist\\ClearWatchlistNotificationsJob', 'userOptionsUpdate' => 'MediaWiki\\User\\Options\\UserOptionsUpdateJob', 'revertedTagUpdate' => 'MediaWiki\\JobQueue\\Jobs\\RevertedTagUpdateJob', 'null' => 'MediaWiki\\JobQueue\\Jobs\\NullJob', 'userEditCountInit' => 'MediaWiki\\User\\UserEditCountInitJob', 'parsoidCachePrewarm' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\ParsoidCachePrewarmJob', 'services' => [ 'ParserOutputAccess', 'PageStore', 'RevisionLookup', 'ParsoidSiteConfig', ], 'needsPage' => false, ], 'renameUserTable' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], 'renameUserDerived' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserDerivedJob', 'services' => [ 'RenameUserFactory', 'UserFactory', ], ], 'renameUser' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], ], 'JobTypesExcludedFromDefaultQueue' => [ 'AssembleUploadChunks', 'PublishStashedFile', 'UploadFromUrl', ], 'JobBackoffThrottling' => [ ], 'JobTypeConf' => [ 'default' => [ 'class' => 'MediaWiki\\JobQueue\\JobQueueDB', 'order' => 'random', 'claimTTL' => 3600, ], ], 'JobQueueIncludeInMaxLagFactor' => false, 'SpecialPageCacheUpdates' => [ 'Statistics' => [ 'MediaWiki\\Deferred\\SiteStatsUpdate', 'cacheUpdate', ], ], 'PagePropLinkInvalidations' => [ 'hiddencat' => 'categorylinks', ], 'CategoryMagicGallery' => true, 'CategoryPagingLimit' => 200, 'CategoryCollation' => 'uppercase', 'TempCategoryCollations' => [ ], 'SortedCategories' => false, 'TrackingCategories' => [ ], 'LogTypes' => [ '', 'block', 'protect', 'rights', 'delete', 'upload', 'move', 'import', 'interwiki', 'patrol', 'merge', 'suppress', 'tag', 'managetags', 'contentmodel', 'renameuser', ], 'LogRestrictions' => [ 'suppress' => 'suppressionlog', ], 'FilterLogTypes' => [ 'patrol' => true, 'tag' => true, 'newusers' => false, ], 'LogNames' => [ '' => 'all-logs-page', 'block' => 'blocklogpage', 'protect' => 'protectlogpage', 'rights' => 'rightslog', 'delete' => 'dellogpage', 'upload' => 'uploadlogpage', 'move' => 'movelogpage', 'import' => 'importlogpage', 'patrol' => 'patrol-log-page', 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ], 'LogHeaders' => [ '' => 'alllogstext', 'block' => 'blocklogtext', 'delete' => 'dellogpagetext', 'import' => 'importlogpagetext', 'merge' => 'mergelogpagetext', 'move' => 'movelogpagetext', 'patrol' => 'patrol-log-header', 'protect' => 'protectlogtext', 'rights' => 'rightslogtext', 'suppress' => 'suppressionlogtext', 'upload' => 'uploadlogpagetext', ], 'LogActions' => [ ], 'LogActionsHandlers' => [ 'block/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/unblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'contentmodel/change' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'contentmodel/new' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'delete/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir2' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/restore' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'import/interwiki' => 'MediaWiki\\Logging\\ImportLogFormatter', 'import/upload' => 'MediaWiki\\Logging\\ImportLogFormatter', 'interwiki/iw_add' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_delete' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_edit' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'managetags/activate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/create' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/deactivate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/delete' => 'MediaWiki\\Logging\\LogFormatter', 'merge/merge' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'merge/merge-into' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move_redir' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'patrol/patrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'patrol/autopatrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'protect/modify' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/move_prot' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/protect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/unprotect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'renameuser/renameuser' => [ 'class' => 'MediaWiki\\Logging\\RenameuserLogFormatter', 'services' => [ 'TitleParser', ], ], 'rights/autopromote' => 'MediaWiki\\Logging\\RightsLogFormatter', 'rights/rights' => 'MediaWiki\\Logging\\RightsLogFormatter', 'suppress/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'tag/update' => 'MediaWiki\\Logging\\TagLogFormatter', 'upload/overwrite' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/revert' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/upload' => 'MediaWiki\\Logging\\UploadLogFormatter', ], 'ActionFilteredLogs' => [ 'block' => [ 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], 'unblock' => [ 'unblock', ], ], 'contentmodel' => [ 'change' => [ 'change', ], 'new' => [ 'new', ], ], 'delete' => [ 'delete' => [ 'delete', ], 'delete_redir' => [ 'delete_redir', 'delete_redir2', ], 'restore' => [ 'restore', ], 'event' => [ 'event', ], 'revision' => [ 'revision', ], ], 'import' => [ 'interwiki' => [ 'interwiki', ], 'upload' => [ 'upload', ], ], 'managetags' => [ 'create' => [ 'create', ], 'delete' => [ 'delete', ], 'activate' => [ 'activate', ], 'deactivate' => [ 'deactivate', ], ], 'move' => [ 'move' => [ 'move', ], 'move_redir' => [ 'move_redir', ], ], 'newusers' => [ 'create' => [ 'create', 'newusers', ], 'create2' => [ 'create2', ], 'autocreate' => [ 'autocreate', ], 'byemail' => [ 'byemail', ], ], 'protect' => [ 'protect' => [ 'protect', ], 'modify' => [ 'modify', ], 'unprotect' => [ 'unprotect', ], 'move_prot' => [ 'move_prot', ], ], 'rights' => [ 'rights' => [ 'rights', ], 'autopromote' => [ 'autopromote', ], ], 'suppress' => [ 'event' => [ 'event', ], 'revision' => [ 'revision', ], 'delete' => [ 'delete', ], 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], ], 'upload' => [ 'upload' => [ 'upload', ], 'overwrite' => [ 'overwrite', ], 'revert' => [ 'revert', ], ], ], 'NewUserLog' => true, 'PageCreationLog' => true, 'AllowSpecialInclusion' => true, 'DisableQueryPageUpdate' => false, 'CountCategorizedImagesAsUsed' => false, 'MaxRedirectLinksRetrieved' => 500, 'RangeContributionsCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 32, ], 'Actions' => [ ], 'DefaultRobotPolicy' => 'index,follow', 'NamespaceRobotPolicies' => [ ], 'ArticleRobotPolicies' => [ ], 'ExemptFromUserRobotsControl' => null, 'DebugAPI' => false, 'APIModules' => [ ], 'APIFormatModules' => [ ], 'APIMetaModules' => [ ], 'APIPropModules' => [ ], 'APIListModules' => [ ], 'APIMaxDBRows' => 5000, 'APIMaxResultSize' => 8388608, 'APIMaxUncachedDiffs' => 1, 'APIMaxLagThreshold' => 7, 'APICacheHelpTimeout' => 3600, 'APIUselessQueryPages' => [ 'MIMEsearch', 'LinkSearch', ], 'AjaxLicensePreview' => true, 'CrossSiteAJAXdomains' => [ ], 'CrossSiteAJAXdomainExceptions' => [ ], 'AllowedCorsHeaders' => [ 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type', 'Accept-Encoding', 'DNT', 'Origin', 'User-Agent', 'Api-User-Agent', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], 'MaxShellMemory' => 307200, 'MaxShellFileSize' => 102400, 'MaxShellTime' => 180, 'MaxShellWallClockTime' => 180, 'ShellCgroup' => false, 'PhpCli' => '/usr/bin/php', 'ShellRestrictionMethod' => 'autodetect', 'ShellboxUrls' => [ 'default' => null, ], 'ShellboxSecretKey' => null, 'ShellboxShell' => '/bin/sh', 'HTTPTimeout' => 25, 'HTTPConnectTimeout' => 5.0, 'HTTPMaxTimeout' => 0, 'HTTPMaxConnectTimeout' => 0, 'HTTPImportTimeout' => 25, 'AsyncHTTPTimeout' => 25, 'HTTPProxy' => '', 'LocalVirtualHosts' => [ ], 'LocalHTTPProxy' => false, 'AllowExternalReqID' => false, 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => null, 'VirtualRestConfig' => [ 'paths' => [ ], 'modules' => [ ], 'global' => [ 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ], ], 'EventRelayerConfig' => [ 'default' => [ 'class' => 'Wikimedia\\EventRelayer\\EventRelayerNull', ], ], 'Pingback' => false, 'OriginTrials' => [ ], 'ReportToExpiry' => 86400, 'ReportToEndpoints' => [ ], 'FeaturePolicyReportOnly' => [ ], 'SkinsPreferred' => [ 'vector-2022', 'vector', ], 'SpecialContributeSkinsEnabled' => [ ], 'SpecialContributeNewPageTarget' => null, 'EnableEditRecovery' => false, 'EditRecoveryExpiry' => 2592000, 'UseCodexSpecialBlock' => false, 'ShowLogoutConfirmation' => false, 'EnableProtectionIndicators' => true, 'OutputPipelineStages' => [ ], 'FeatureShutdown' => [ ], 'CloneArticleParserOutput' => true, 'UseLeximorph' => false, 'UsePostprocCache' => false, 'UsePostprocCacheLegacy' => false, 'UsePostprocCacheParsoid' => false, 'ParserOptionsLogUnsafeSampleRate' => 0, ], 'type' => [ 'ConfigRegistry' => 'object', 'AssumeProxiesUseDefaultProtocolPorts' => 'boolean', 'ForceHTTPS' => 'boolean', 'ExtensionDirectory' => [ 'string', 'null', ], 'StyleDirectory' => [ 'string', 'null', ], 'UploadDirectory' => [ 'string', 'boolean', 'null', ], 'Logos' => [ 'object', 'boolean', ], 'ReferrerPolicy' => [ 'array', 'string', 'boolean', ], 'ActionPaths' => 'object', 'MainPageIsDomainRoot' => 'boolean', 'ImgAuthUrlPathMap' => 'object', 'LocalFileRepo' => 'object', 'ForeignFileRepos' => 'array', 'UseSharedUploads' => 'boolean', 'SharedUploadDirectory' => [ 'string', 'null', ], 'SharedUploadPath' => [ 'string', 'null', ], 'HashedSharedUploadDirectory' => 'boolean', 'FetchCommonsDescriptions' => 'boolean', 'SharedUploadDBname' => [ 'boolean', 'string', ], 'SharedUploadDBprefix' => 'string', 'CacheSharedUploads' => 'boolean', 'ForeignUploadTargets' => 'array', 'UploadDialog' => 'object', 'FileBackends' => 'object', 'LockManagers' => 'array', 'CopyUploadsDomains' => 'array', 'CopyUploadTimeout' => [ 'boolean', 'integer', ], 'SharedThumbnailScriptPath' => [ 'string', 'boolean', ], 'HashedUploadDirectory' => 'boolean', 'CSPUploadEntryPoint' => 'boolean', 'FileExtensions' => 'array', 'ProhibitedFileExtensions' => 'array', 'MimeTypeExclusions' => 'array', 'TrustedMediaFormats' => 'array', 'MediaHandlers' => 'object', 'NativeImageLazyLoading' => 'boolean', 'ParserTestMediaHandlers' => 'object', 'MaxInterlacingAreas' => 'object', 'SVGConverters' => 'object', 'SVGNativeRendering' => [ 'string', 'boolean', ], 'MaxImageArea' => [ 'string', 'integer', 'boolean', ], 'TiffThumbnailType' => 'array', 'GenerateThumbnailOnParse' => 'boolean', 'EnableAutoRotation' => [ 'boolean', 'null', ], 'Antivirus' => [ 'string', 'null', ], 'AntivirusSetup' => 'object', 'MimeDetectorCommand' => [ 'string', 'null', ], 'XMLMimeTypes' => 'object', 'ImageLimits' => 'array', 'ThumbLimits' => 'array', 'ThumbnailNamespaces' => 'array', 'ThumbnailSteps' => [ 'array', 'null', ], 'ThumbnailStepsRatio' => [ 'number', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'UserEmailConfirmationUseHTML' => 'boolean', 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ImageLinksSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'PHPSessionHandling' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', 'ContentNamespaces' => 'array', 'ShortPagesNamespaceExclusions' => 'array', 'ExtraSignatureNamespaces' => 'array', 'InvalidRedirectTargets' => 'array', 'LocalInterwikis' => 'array', 'InterwikiCache' => [ 'boolean', 'object', ], 'SiteTypes' => 'object', 'UrlProtocols' => 'array', 'TidyConfig' => 'object', 'ParsoidSettings' => 'object', 'ParsoidExperimentalParserFunctionOutput' => 'boolean', 'NoFollowNsExceptions' => 'array', 'NoFollowDomainExceptions' => 'array', 'ExternalLinksIgnoreDomains' => 'array', 'EnableMagicLinks' => 'object', 'ManualRevertSearchRadius' => 'integer', 'RevertedTagMaxDepth' => 'integer', 'CentralIdLookupProviders' => 'object', 'CentralIdLookupProvider' => 'string', 'UserRegistrationProviders' => 'object', 'PasswordPolicy' => 'object', 'AuthManagerConfig' => [ 'object', 'null', ], 'AuthManagerAutoConfig' => 'object', 'RememberMe' => 'string', 'ReauthenticateTime' => 'object', 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => 'object', 'ChangeCredentialsBlacklist' => 'array', 'RemoveCredentialsBlacklist' => 'array', 'PasswordConfig' => 'object', 'PasswordResetRoutes' => 'object', 'SignatureAllowedLintErrors' => 'array', 'ReservedUsernames' => 'array', 'DefaultUserOptions' => 'object', 'ConditionalUserOptions' => 'object', 'HiddenPrefs' => 'array', 'UserJsPrefLimit' => 'integer', 'AuthenticationTokenVersion' => [ 'string', 'null', ], 'SessionProviders' => 'object', 'AutoCreateTempUser' => 'object', 'AutoblockExemptions' => 'array', 'BlockCIDRLimit' => 'object', 'EnableMultiBlocks' => 'boolean', 'GroupPermissions' => 'object', 'PrivilegedGroups' => 'array', 'RevokePermissions' => 'object', 'GroupInheritsPermissions' => 'object', 'ImplicitGroups' => 'array', 'GroupsAddToSelf' => 'object', 'GroupsRemoveFromSelf' => 'object', 'RestrictedGroups' => 'object', 'UserRequirementsPrivateConditions' => 'array', 'RestrictionTypes' => 'array', 'RestrictionLevels' => 'array', 'CascadingRestrictionLevels' => 'array', 'SemiprotectedRestrictionLevels' => 'array', 'NamespaceProtection' => 'object', 'NonincludableNamespaces' => 'object', 'Autopromote' => 'object', 'AutopromoteOnce' => 'object', 'AutopromoteOnceRCExcludedGroups' => 'array', 'AddGroups' => 'object', 'RemoveGroups' => 'object', 'AvailableRights' => 'array', 'ImplicitRights' => 'array', 'AccountCreationThrottle' => [ 'integer', 'array', ], 'TempAccountCreationThrottle' => 'array', 'TempAccountNameAcquisitionThrottle' => 'array', 'SpamRegex' => 'array', 'SummarySpamRegex' => 'array', 'DnsBlacklistUrls' => 'array', 'ProxyList' => [ 'string', 'array', ], 'ProxyWhitelist' => 'array', 'SoftBlockRanges' => 'array', 'RateLimits' => 'object', 'RateLimitsExcludedIPs' => 'array', 'ExternalQuerySources' => 'object', 'PasswordAttemptThrottle' => 'array', 'GrantPermissions' => 'object', 'GrantPermissionGroups' => 'object', 'GrantRiskGroups' => 'object', 'EnableBotPasswords' => 'boolean', 'BotPasswordsCluster' => [ 'string', 'boolean', ], 'BotPasswordsDatabase' => [ 'string', 'boolean', ], 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ 'boolean', 'object', ], 'CSPFalsePositiveUrls' => 'object', 'AllowCrossOrigin' => 'boolean', 'RestAllowCrossOriginCookieAuth' => 'boolean', 'CookieSameSite' => [ 'string', 'null', ], 'CacheVaryCookies' => 'array', 'TrxProfilerLimits' => 'object', 'DebugLogGroups' => 'object', 'MWLoggerDefaultSpi' => 'object', 'Profiler' => 'object', 'StatsTarget' => [ 'string', 'null', ], 'StatsFormat' => [ 'string', 'null', ], 'StatsPrefix' => 'string', 'OpenTelemetryConfig' => [ 'object', 'null', ], 'OpenSearchTemplates' => 'object', 'NamespacesToBeSearchedDefault' => 'object', 'SitemapNamespaces' => [ 'boolean', 'array', ], 'SitemapNamespacesPriorities' => [ 'boolean', 'object', ], 'SitemapApiConfig' => 'object', 'SpecialSearchFormOptions' => 'object', 'SearchMatchRedirectPreference' => 'boolean', 'SearchRunSuggestedQuery' => 'boolean', 'PreviewOnOpenNamespaces' => 'object', 'ReadOnlyWatchedItemStore' => 'boolean', 'GitRepositoryViewers' => 'object', 'InstallerInitialPages' => 'array', 'RCLinkLimits' => 'array', 'RCLinkDays' => 'array', 'RCFeeds' => 'object', 'RCEngines' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchlistLabels' => 'boolean', 'WatchlistLabelsMaxPerUser' => 'integer', 'WatchlistPurgeRate' => 'number', 'WatchlistExpiryMaxDuration' => [ 'string', 'null', ], 'EnableChangesListQueryPartitioning' => 'boolean', 'ImportSources' => 'object', 'ExtensionFunctions' => 'array', 'ExtensionMessagesFiles' => 'object', 'MessagesDirs' => 'object', 'TranslationAliasesDirs' => 'object', 'ExtensionEntryPointListFiles' => 'object', 'ValidSkinNames' => 'object', 'SpecialPages' => 'object', 'ExtensionCredits' => 'object', 'Hooks' => 'object', 'ServiceWiringFiles' => 'array', 'JobClasses' => 'object', 'JobTypesExcludedFromDefaultQueue' => 'array', 'JobBackoffThrottling' => 'object', 'JobTypeConf' => 'object', 'SpecialPageCacheUpdates' => 'object', 'PagePropLinkInvalidations' => 'object', 'TempCategoryCollations' => 'array', 'SortedCategories' => 'boolean', 'TrackingCategories' => 'array', 'LogTypes' => 'array', 'LogRestrictions' => 'object', 'FilterLogTypes' => 'object', 'LogNames' => 'object', 'LogHeaders' => 'object', 'LogActions' => 'object', 'LogActionsHandlers' => 'object', 'ActionFilteredLogs' => 'object', 'RangeContributionsCIDRLimit' => 'object', 'Actions' => 'object', 'NamespaceRobotPolicies' => 'object', 'ArticleRobotPolicies' => 'object', 'ExemptFromUserRobotsControl' => [ 'array', 'null', ], 'APIModules' => 'object', 'APIFormatModules' => 'object', 'APIMetaModules' => 'object', 'APIPropModules' => 'object', 'APIListModules' => 'object', 'APIUselessQueryPages' => 'array', 'CrossSiteAJAXdomains' => 'object', 'CrossSiteAJAXdomainExceptions' => 'object', 'AllowedCorsHeaders' => 'array', 'RestAPIAdditionalRouteFiles' => 'array', 'RestSandboxSpecs' => 'object', 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], 'VirtualRestConfig' => 'object', 'EventRelayerConfig' => 'object', 'Pingback' => 'boolean', 'OriginTrials' => 'array', 'ReportToExpiry' => 'integer', 'ReportToEndpoints' => 'array', 'FeaturePolicyReportOnly' => 'array', 'SkinsPreferred' => 'array', 'SpecialContributeSkinsEnabled' => 'array', 'SpecialContributeNewPageTarget' => [ 'string', 'null', ], 'EnableEditRecovery' => 'boolean', 'EditRecoveryExpiry' => 'integer', 'UseCodexSpecialBlock' => 'boolean', 'ShowLogoutConfirmation' => 'boolean', 'EnableProtectionIndicators' => 'boolean', 'OutputPipelineStages' => 'object', 'FeatureShutdown' => 'array', 'CloneArticleParserOutput' => 'boolean', 'UseLeximorph' => 'boolean', 'UsePostprocCache' => 'boolean', 'UsePostprocCacheLegacy' => 'boolean', 'UsePostprocCacheParsoid' => 'boolean', 'ParserOptionsLogUnsafeSampleRate' => 'integer', ], 'mergeStrategy' => [ 'TiffThumbnailType' => 'replace', 'LBFactoryConf' => 'replace', 'InterwikiCache' => 'replace', 'PasswordPolicy' => 'array_replace_recursive', 'AuthManagerAutoConfig' => 'array_plus_2d', 'GroupPermissions' => 'array_plus_2d', 'RevokePermissions' => 'array_plus_2d', 'AddGroups' => 'array_merge_recursive', 'RemoveGroups' => 'array_merge_recursive', 'RateLimits' => 'array_plus_2d', 'GrantPermissions' => 'array_plus_2d', 'MWLoggerDefaultSpi' => 'replace', 'Profiler' => 'replace', 'Hooks' => 'array_merge_recursive', 'VirtualRestConfig' => 'array_plus_2d', ], 'dynamicDefault' => [ 'UsePathInfo' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUsePathInfo', ], ], 'Script' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultScript', ], ], 'LoadScript' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLoadScript', ], ], 'RestPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultRestPath', ], ], 'StylePath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultStylePath', ], ], 'LocalStylePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalStylePath', ], ], 'ExtensionAssetsPath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultExtensionAssetsPath', ], ], 'ArticlePath' => [ 'use' => [ 'Script', 'UsePathInfo', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultArticlePath', ], ], 'UploadPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUploadPath', ], ], 'FileCacheDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultFileCacheDirectory', ], ], 'Logo' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLogo', ], ], 'DeletedDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDeletedDirectory', ], ], 'ShowEXIF' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultShowEXIF', ], ], 'SharedPrefix' => [ 'use' => [ 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedPrefix', ], ], 'SharedSchema' => [ 'use' => [ 'DBmwschema', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedSchema', ], ], 'DBerrorLogTZ' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDBerrorLogTZ', ], ], 'Localtimezone' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocaltimezone', ], ], 'LocalTZoffset' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalTZoffset', ], ], 'ResourceBasePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultResourceBasePath', ], ], 'MetaNamespace' => [ 'use' => [ 'Sitename', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultMetaNamespace', ], ], 'CookieSecure' => [ 'use' => [ 'ForceHTTPS', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookieSecure', ], ], 'CookiePrefix' => [ 'use' => [ 'SharedDB', 'SharedPrefix', 'SharedTables', 'DBname', 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookiePrefix', ], ], 'ReadOnlyFile' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultReadOnlyFile', ], ], ], ], 'config-schema' => [ 'UploadStashScalerBaseUrl' => [ 'deprecated' => 'since 1.36 Use thumbProxyUrl in $wgLocalFileRepo', ], 'IllegalFileChars' => [ 'deprecated' => 'since 1.41; no longer customizable', ], 'ThumbnailNamespaces' => [ 'items' => [ 'type' => 'integer', ], ], 'LocalDatabases' => [ 'items' => [ 'type' => 'string', ], ], 'ParserCacheFilterConfig' => [ 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of namespace IDs to filter definitions.', 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of filter names to values.', 'properties' => [ 'minCpuTime' => [ 'type' => 'number', ], ], ], ], ], 'PHPSessionHandling' => [ 'deprecated' => 'since 1.45 Integration with PHP session handling will be removed in the future', ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'ChangeCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'RemoveCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'GroupPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GroupInheritsPermissions' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'AvailableRights' => [ 'items' => [ 'type' => 'string', ], ], 'ImplicitRights' => [ 'items' => [ 'type' => 'string', ], ], 'SoftBlockRanges' => [ 'items' => [ 'type' => 'string', ], ], 'ExternalQuerySources' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'enabled' => [ 'type' => 'boolean', 'default' => false, ], 'url' => [ 'type' => 'string', 'format' => 'uri', ], 'timeout' => [ 'type' => 'integer', 'default' => 10, ], ], 'required' => [ 'enabled', 'url', ], 'additionalProperties' => false, ], ], 'GrantPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GrantPermissionGroups' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'SitemapNamespacesPriorities' => [ 'deprecated' => 'since 1.45 and ignored', ], 'SitemapApiConfig' => [ 'additionalProperties' => [ 'enabled' => [ 'type' => 'bool', ], 'sitemapsPerIndex' => [ 'type' => 'int', ], 'pagesPerSitemap' => [ 'type' => 'int', ], 'expiry' => [ 'type' => 'int', ], ], ], 'SoftwareTags' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'JobBackoffThrottling' => [ 'additionalProperties' => [ 'type' => 'number', ], ], 'JobTypeConf' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'class' => [ 'type' => 'string', ], 'order' => [ 'type' => 'string', ], 'claimTTL' => [ 'type' => 'integer', ], ], ], ], 'TrackingCategories' => [ 'deprecated' => 'since 1.25 Extensions should now register tracking categories using the new extension registration system.', ], 'RangeContributionsCIDRLimit' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'RestSandboxSpecs' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'url' => [ 'type' => 'string', 'format' => 'url', ], 'name' => [ 'type' => 'string', ], 'msg' => [ 'type' => 'string', 'description' => 'a message key', ], ], 'required' => [ 'url', ], ], ], 'ShellboxUrls' => [ 'additionalProperties' => [ 'type' => [ 'string', 'boolean', 'null', ], ], ], ], 'obsolete-config' => [ 'MangleFlashPolicy' => 'Since 1.39; no longer has any effect.', 'EnableOpenSearchSuggest' => 'Since 1.35, no longer used', 'AutoloadAttemptLowercase' => 'Since 1.40; no longer has any effect.', ],]
An object representing a primary or replica DB position in a replicated setup.
clearFlag( $flag, $remember=self::REMEMBER_NOTHING)
Clear a flag for this connection.
Internal interface for relational database handles exposed to their owner.
Interface to a relational database.
Definition IDatabase.php:31
getLBInfo( $name=null)
Get properties passed down from the server info array of the load balancer.
trxLevel()
Gets the current transaction level.
Internal interface for load balancer instances exposed to their owner.
const GROUP_GENERIC
The generic query group.
Database load monitoring interface.
close( $fname=__METHOD__)
Close the database connection.
getServerName()
Get the readable name for the server.
getDomainID()
Return the currently selected domain ID.
array $params
The job parameters.