MediaWiki REL1_37
LBFactoryMulti.php
Go to the documentation of this file.
1<?php
24namespace Wikimedia\Rdbms;
25
26use InvalidArgumentException;
27use LogicException;
28use UnexpectedValueException;
29
37 private $mainLBs = [];
39 private $externalLBs = [];
40
67
113 public function __construct( array $conf ) {
114 parent::__construct( $conf );
115
116 $this->hostsByServerName = $conf['hostsByName'] ?? [];
117 $this->sectionsByDB = $conf['sectionsByDB'];
118 $this->groupLoadsBySection = $conf['groupLoadsBySection'] ?? [];
119 foreach ( ( $conf['sectionLoads'] ?? [] ) as $section => $loadsByServerName ) {
120 $this->groupLoadsBySection[$section][ILoadBalancer::GROUP_GENERIC] = $loadsByServerName;
121 }
122 $this->groupLoadsByDB = $conf['groupLoadsByDB'] ?? [];
123 $this->externalLoadsByCluster = $conf['externalLoads'] ?? [];
124 $this->serverTemplate = $conf['serverTemplate'] ?? [];
125 $this->externalTemplateOverrides = $conf['externalTemplateOverrides'] ?? [];
126 $this->templateOverridesBySection = $conf['templateOverridesBySection'] ?? [];
127 $this->templateOverridesByCluster = $conf['templateOverridesByCluster'] ?? [];
128 $this->masterTemplateOverrides = $conf['masterTemplateOverrides'] ?? [];
129 $this->templateOverridesByServer = $conf['templateOverridesByServer'] ?? [];
130 $this->readOnlyBySection = $conf['readOnlyBySection'] ?? [];
131
132 if ( isset( $conf['loadMonitor'] ) ) {
133 $this->loadMonitorConfig = $conf['loadMonitor'];
134 } elseif ( isset( $conf['loadMonitorClass'] ) ) { // b/c
135 $this->loadMonitorConfig = [ 'class' => $conf['loadMonitorClass'] ];
136 } else {
137 $this->loadMonitorConfig = [ 'class' => LoadMonitor::class ];
138 }
139
140 foreach ( array_keys( $this->externalLoadsByCluster ) as $cluster ) {
141 if ( isset( $this->groupLoadsBySection[$cluster] ) ) {
142 throw new LogicException(
143 "External cluster '$cluster' has the same name as a main section/cluster"
144 );
145 }
146 }
147 }
148
149 public function newMainLB( $domain = false, $owner = null ): ILoadBalancer {
150 $domainInstance = $this->resolveDomainInstance( $domain );
151 $database = $domainInstance->getDatabase();
152 $section = $this->getSectionFromDatabase( $database );
153
154 if ( !isset( $this->groupLoadsBySection[$section][ILoadBalancer::GROUP_GENERIC] ) ) {
155 throw new UnexpectedValueException( "Section '$section' has no hosts defined." );
156 }
157
158 $dbGroupLoads = $this->groupLoadsByDB[$database] ?? [];
159 unset( $dbGroupLoads[ILoadBalancer::GROUP_GENERIC] ); // cannot override
160 return $this->newLoadBalancer(
161 $section,
162 array_merge(
163 $this->serverTemplate,
164 $this->templateOverridesBySection[$section] ?? []
165 ),
166 array_merge( $this->groupLoadsBySection[$section], $dbGroupLoads ),
167 // Use the LB-specific read-only reason if everything isn't already read-only
168 is_string( $this->readOnlyReason )
169 ? $this->readOnlyReason
170 : ( $this->readOnlyBySection[$section] ?? false ),
171 $owner
172 );
173 }
174
175 public function getMainLB( $domain = false ): ILoadBalancer {
176 $domainInstance = $this->resolveDomainInstance( $domain );
177 $section = $this->getSectionFromDatabase( $domainInstance->getDatabase() );
178
179 if ( !isset( $this->mainLBs[$section] ) ) {
180 $this->mainLBs[$section] = $this->newMainLB( $domain, $this->getOwnershipId() );
181 }
182
183 return $this->mainLBs[$section];
184 }
185
186 public function newExternalLB( $cluster, $owner = null ): ILoadBalancer {
187 if ( !isset( $this->externalLoadsByCluster[$cluster] ) ) {
188 throw new InvalidArgumentException( "Unknown cluster '$cluster'" );
189 }
190 return $this->newLoadBalancer(
191 $cluster,
192 array_merge(
193 $this->serverTemplate,
194 $this->externalTemplateOverrides,
195 $this->templateOverridesByCluster[$cluster] ?? []
196 ),
197 [ ILoadBalancer::GROUP_GENERIC => $this->externalLoadsByCluster[$cluster] ],
198 $this->readOnlyReason,
199 $owner
200 );
201 }
202
203 public function getExternalLB( $cluster ): ILoadBalancer {
204 if ( !isset( $this->externalLBs[$cluster] ) ) {
205 $this->externalLBs[$cluster] = $this->newExternalLB(
206 $cluster,
207 $this->getOwnershipId()
208 );
209 }
210
211 return $this->externalLBs[$cluster];
212 }
213
214 public function getAllMainLBs(): array {
215 $lbs = [];
216 foreach ( $this->sectionsByDB as $db => $section ) {
217 if ( !isset( $lbs[$section] ) ) {
218 $lbs[$section] = $this->getMainLB( $db );
219 }
220 }
221
222 return $lbs;
223 }
224
225 public function getAllExternalLBs(): array {
226 $lbs = [];
227 foreach ( $this->externalLoadsByCluster as $cluster => $unused ) {
228 $lbs[$cluster] = $this->getExternalLB( $cluster );
229 }
230
231 return $lbs;
232 }
233
234 public function forEachLB( $callback, array $params = [] ) {
235 foreach ( $this->mainLBs as $lb ) {
236 $callback( $lb, ...$params );
237 }
238 foreach ( $this->externalLBs as $lb ) {
239 $callback( $lb, ...$params );
240 }
241 }
242
253 private function newLoadBalancer(
254 string $clusterName,
255 array $serverTemplate,
256 array $groupLoads,
257 $readOnlyReason,
258 $owner
259 ) {
260 $lb = new LoadBalancer( array_merge(
261 $this->baseLoadBalancerParams( $owner ),
262 [
263 'servers' => $this->makeServerConfigArrays( $serverTemplate, $groupLoads ),
264 'loadMonitor' => $this->loadMonitorConfig,
265 'readOnlyReason' => $readOnlyReason,
266 'clusterName' => $clusterName
267 ]
268 ) );
269 $this->initLoadBalancer( $lb );
270
271 return $lb;
272 }
273
281 private function makeServerConfigArrays( array $serverTemplate, array $groupLoads ) {
282 // The primary DB server is the first host explicitly listed in the generic load group
283 if ( !$groupLoads[ILoadBalancer::GROUP_GENERIC] ) {
284 throw new UnexpectedValueException( "Empty generic load array; no primary DB defined." );
285 }
286 $groupLoadsByServerName = $this->reindexGroupLoadsByServerName( $groupLoads );
287 // Get the ordered map of (server name => load); the primary DB server is first
288 $genericLoads = $groupLoads[ILoadBalancer::GROUP_GENERIC];
289 // Implictly append any hosts that only appear in custom load groups
290 $genericLoads += array_fill_keys( array_keys( $groupLoadsByServerName ), 0 );
291 $servers = [];
292 foreach ( $genericLoads as $serverName => $load ) {
293 $servers[] = array_merge(
294 $serverTemplate,
295 $servers ? [] : $this->masterTemplateOverrides,
296 $this->templateOverridesByServer[$serverName] ?? [],
297 [
298 'host' => $this->hostsByServerName[$serverName] ?? $serverName,
299 'serverName' => $serverName,
300 'load' => $load,
301 'groupLoads' => $groupLoadsByServerName[$serverName] ?? []
302 ]
303 );
304 }
305
306 return $servers;
307 }
308
315 private function reindexGroupLoadsByServerName( array $groupLoads ) {
316 $groupLoadsByServerName = [];
317 foreach ( $groupLoads as $group => $loadByServerName ) {
318 foreach ( $loadByServerName as $serverName => $load ) {
319 $groupLoadsByServerName[$serverName][$group] = $load;
320 }
321 }
322
323 return $groupLoadsByServerName;
324 }
325
330 private function getSectionFromDatabase( $database ) {
331 return $this->sectionsByDB[$database] ?? self::CLUSTER_MAIN_DEFAULT;
332 }
333}
if(ini_get('mbstring.func_overload')) if(!defined('MW_ENTRY_POINT'))
Pre-config setup: Before loading LocalSettings.php.
Definition Setup.php:88
A multi-database, multi-primary DB factory for Wikimedia and similar installations.
int[][][] $groupLoadsBySection
Map of (main section => group => server name => load ratio)
string[] bool[] $readOnlyBySection
A map of (main section => read-only message)
array[] $templateOverridesByCluster
Map of (external cluster => server config map overrides)
getAllMainLBs()
Get the tracked load balancer instances for all main clusters.
getAllExternalLBs()
Get the tracked load balancer instances for all external clusters.
newMainLB( $domain=false, $owner=null)
Create a new load balancer instance for a main cluster.
getExternalLB( $cluster)
Get the tracked load balancer instance for an external cluster.
getMainLB( $domain=false)
Get the tracked load balancer instance for a main cluster.
forEachLB( $callback, array $params=[])
Execute a function for each instantiated tracked load balancer instance.
array $externalTemplateOverrides
Server config map overriding "serverTemplate" for all external servers.
array $masterTemplateOverrides
Server config override map for all main/external primary DB servers.
newLoadBalancer(string $clusterName, array $serverTemplate, array $groupLoads, $readOnlyReason, $owner)
Make a new load balancer object based on template and load array.
newExternalLB( $cluster, $owner=null)
Create a new load balancer instance for an external cluster.
string[] $hostsByServerName
Map of (server name => IP address)
array $serverTemplate
Server config map ("host", "serverName", "load", and "groupLoads" ignored)
int[][] $externalLoadsByCluster
Map of (external cluster => server name => load ratio)
makeServerConfigArrays(array $serverTemplate, array $groupLoads)
Make a server array as expected by LoadBalancer::__construct()
array< string, LoadBalancer > $externalLBs
Map of (external cluster => tracked LoadBalancer)
array[] $templateOverridesBySection
Map of (main section => server config map overrides)
__construct(array $conf)
Template override precedence (highest => lowest):
array[] $templateOverridesByServer
Map of (server name => server config map overrides) for all servers.
string[] $sectionsByDB
Map of (database name => main section)
array< string, LoadBalancer > $mainLBs
Map of (main section => tracked LoadBalancer)
reindexGroupLoadsByServerName(array $groupLoads)
Take a group load array indexed by (group,server) and reindex it by (server,group)
array $loadMonitorConfig
Configuration for the LoadMonitor to use within LoadBalancer instances.
int[][][] $groupLoadsByDB
Map of (database => group => server name => load ratio)
An interface for generating database load balancers.
Definition LBFactory.php:42
Database connection, tracking, load balancing, and transaction manager for a cluster.
Database cluster connection, tracking, load balancing, and transaction manager interface.
const GROUP_GENERIC
The generic query group.