MediaWiki 1.40.4
LBFactoryMulti.php
Go to the documentation of this file.
1<?php
20namespace Wikimedia\Rdbms;
21
22use InvalidArgumentException;
23use LogicException;
24use UnexpectedValueException;
25
55 private $mainLBs = [];
57 private $externalLBs = [];
58
60 private $hostsByServerName;
62 private $sectionsByDB;
64 private $groupLoadsBySection;
66 private $externalLoadsByCluster;
68 private $serverTemplate;
70 private $externalTemplateOverrides;
72 private $templateOverridesBySection;
74 private $templateOverridesByCluster;
76 private $masterTemplateOverrides;
78 private $templateOverridesByServer;
80 private $readOnlyBySection;
82 private $loadMonitorConfig;
83
133 public function __construct( array $conf ) {
134 parent::__construct( $conf );
135
136 $this->hostsByServerName = $conf['hostsByName'] ?? [];
137 $this->sectionsByDB = $conf['sectionsByDB'];
138 $this->sectionsByDB += [ self::CLUSTER_MAIN_DEFAULT => self::CLUSTER_MAIN_DEFAULT ];
139 $this->groupLoadsBySection = $conf['groupLoadsBySection'] ?? [];
140 foreach ( ( $conf['sectionLoads'] ?? [] ) as $section => $loadsByServerName ) {
141 $this->groupLoadsBySection[$section][ILoadBalancer::GROUP_GENERIC] = $loadsByServerName;
142 }
143 $this->externalLoadsByCluster = $conf['externalLoads'] ?? [];
144 $this->serverTemplate = $conf['serverTemplate'] ?? [];
145 $this->externalTemplateOverrides = $conf['externalTemplateOverrides'] ?? [];
146 $this->templateOverridesBySection = $conf['templateOverridesBySection'] ?? [];
147 $this->templateOverridesByCluster = $conf['templateOverridesByCluster'] ?? [];
148 $this->masterTemplateOverrides = $conf['masterTemplateOverrides'] ?? [];
149 $this->templateOverridesByServer = $conf['templateOverridesByServer'] ?? [];
150 $this->readOnlyBySection = $conf['readOnlyBySection'] ?? [];
151
152 if ( isset( $conf['loadMonitor'] ) ) {
153 $this->loadMonitorConfig = $conf['loadMonitor'];
154 } elseif ( isset( $conf['loadMonitorClass'] ) ) { // b/c
155 $this->loadMonitorConfig = [ 'class' => $conf['loadMonitorClass'] ];
156 } else {
157 $this->loadMonitorConfig = [ 'class' => LoadMonitor::class ];
158 }
159
160 foreach ( array_keys( $this->externalLoadsByCluster ) as $cluster ) {
161 if ( isset( $this->groupLoadsBySection[$cluster] ) ) {
162 throw new LogicException(
163 "External cluster '$cluster' has the same name as a main section/cluster"
164 );
165 }
166 }
167 }
168
169 public function newMainLB( $domain = false ): ILoadBalancerForOwner {
170 $domainInstance = $this->resolveDomainInstance( $domain );
171 $database = $domainInstance->getDatabase();
172 $section = $this->getSectionFromDatabase( $database );
173
174 if ( !isset( $this->groupLoadsBySection[$section][ILoadBalancer::GROUP_GENERIC] ) ) {
175 throw new UnexpectedValueException( "Section '$section' has no hosts defined." );
176 }
177
178 return $this->newLoadBalancer(
179 $section,
180 array_merge(
181 $this->serverTemplate,
182 $this->templateOverridesBySection[$section] ?? []
183 ),
184 $this->groupLoadsBySection[$section],
185 // Use the LB-specific read-only reason if everything isn't already read-only
186 is_string( $this->readOnlyReason )
187 ? $this->readOnlyReason
188 : ( $this->readOnlyBySection[$section] ?? false )
189 );
190 }
191
192 public function getMainLB( $domain = false ): ILoadBalancer {
193 $domainInstance = $this->resolveDomainInstance( $domain );
194 $section = $this->getSectionFromDatabase( $domainInstance->getDatabase() );
195
196 if ( !isset( $this->mainLBs[$section] ) ) {
197 $this->mainLBs[$section] = $this->newMainLB( $domain );
198 }
199
200 return $this->mainLBs[$section];
201 }
202
203 public function newExternalLB( $cluster ): ILoadBalancerForOwner {
204 if ( !isset( $this->externalLoadsByCluster[$cluster] ) ) {
205 throw new InvalidArgumentException( "Unknown cluster '$cluster'" );
206 }
207 return $this->newLoadBalancer(
208 $cluster,
209 array_merge(
210 $this->serverTemplate,
211 $this->externalTemplateOverrides,
212 $this->templateOverridesByCluster[$cluster] ?? []
213 ),
214 [ ILoadBalancer::GROUP_GENERIC => $this->externalLoadsByCluster[$cluster] ],
215 $this->readOnlyReason
216 );
217 }
218
219 public function getExternalLB( $cluster ): ILoadBalancer {
220 if ( !isset( $this->externalLBs[$cluster] ) ) {
221 $this->externalLBs[$cluster] = $this->newExternalLB(
222 $cluster
223 );
224 }
225
226 return $this->externalLBs[$cluster];
227 }
228
229 public function getAllMainLBs(): array {
230 $lbs = [];
231 foreach ( $this->sectionsByDB as $db => $section ) {
232 if ( !isset( $lbs[$section] ) ) {
233 $lbs[$section] = $this->getMainLB( $db );
234 }
235 }
236
237 return $lbs;
238 }
239
240 public function getAllExternalLBs(): array {
241 $lbs = [];
242 foreach ( $this->externalLoadsByCluster as $cluster => $unused ) {
243 $lbs[$cluster] = $this->getExternalLB( $cluster );
244 }
245
246 return $lbs;
247 }
248
249 protected function getLBsForOwner() {
250 foreach ( $this->mainLBs as $lb ) {
251 yield $lb;
252 }
253 foreach ( $this->externalLBs as $lb ) {
254 yield $lb;
255 }
256 }
257
267 private function newLoadBalancer(
268 string $clusterName,
269 array $serverTemplate,
270 array $groupLoads,
271 $readOnlyReason
272 ) {
273 $lb = new LoadBalancer( array_merge(
274 $this->baseLoadBalancerParams(),
275 [
276 'servers' => $this->makeServerConfigArrays( $serverTemplate, $groupLoads ),
277 'loadMonitor' => $this->loadMonitorConfig,
278 'readOnlyReason' => $readOnlyReason,
279 'clusterName' => $clusterName
280 ]
281 ) );
282 $this->initLoadBalancer( $lb );
283
284 return $lb;
285 }
286
294 private function makeServerConfigArrays( array $serverTemplate, array $groupLoads ) {
295 // The primary DB server is the first host explicitly listed in the generic load group
296 if ( !$groupLoads[ILoadBalancer::GROUP_GENERIC] ) {
297 throw new UnexpectedValueException( "Empty generic load array; no primary DB defined." );
298 }
299 $groupLoadsByServerName = $this->reindexGroupLoadsByServerName( $groupLoads );
300 // Get the ordered map of (server name => load); the primary DB server is first
301 $genericLoads = $groupLoads[ILoadBalancer::GROUP_GENERIC];
302 // Implicitly append any hosts that only appear in custom load groups
303 $genericLoads += array_fill_keys( array_keys( $groupLoadsByServerName ), 0 );
304 $servers = [];
305 foreach ( $genericLoads as $serverName => $load ) {
306 $servers[] = array_merge(
307 $serverTemplate,
308 $servers ? [] : $this->masterTemplateOverrides,
309 $this->templateOverridesByServer[$serverName] ?? [],
310 [
311 'host' => $this->hostsByServerName[$serverName] ?? $serverName,
312 'serverName' => $serverName,
313 'load' => $load,
314 'groupLoads' => $groupLoadsByServerName[$serverName] ?? []
315 ]
316 );
317 }
318
319 return $servers;
320 }
321
328 private function reindexGroupLoadsByServerName( array $groupLoads ) {
329 $groupLoadsByServerName = [];
330 foreach ( $groupLoads as $group => $loadByServerName ) {
331 foreach ( $loadByServerName as $serverName => $load ) {
332 $groupLoadsByServerName[$serverName][$group] = $load;
333 }
334 }
335
336 return $groupLoadsByServerName;
337 }
338
343 private function getSectionFromDatabase( $database ) {
344 return $this->sectionsByDB[$database]
345 ?? $this->sectionsByDB[self::CLUSTER_MAIN_DEFAULT]
346 ?? self::CLUSTER_MAIN_DEFAULT;
347 }
348
349 public function reconfigure( array $conf ): void {
350 if ( !$conf ) {
351 return;
352 }
353
354 foreach ( $this->mainLBs as $lb ) {
355 $groupLoads = $conf['groupLoadsBySection'][$lb->getClusterName()];
356 $groupLoads[ILoadBalancer::GROUP_GENERIC] = $conf['sectionLoads'][$lb->getClusterName()] ?? [];
357 $config = [ 'servers' => $this->makeServerConfigArrays( $conf['serverTemplate'] ?? [], $groupLoads ) ];
358 $lb->reconfigure( $config );
359
360 }
361 foreach ( $this->externalLBs as $lb ) {
362 $groupLoads = [ ILoadBalancer::GROUP_GENERIC => $conf['externalLoads'][$lb->getClusterName()] ];
363 $config = [ 'servers' => $this->makeServerConfigArrays( $conf['serverTemplate'] ?? [], $groupLoads ) ];
364 $lb->reconfigure( $config );
365 }
366 }
367}
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Definition WebStart.php:88
LoadBalancer manager for sites with several "main" database clusters.
newMainLB( $domain=false)
Create a new load balancer instance for the main cluster that handles the given domain.
getAllMainLBs()
Get the tracked load balancer instances for all main clusters.
getAllExternalLBs()
Get the tracked load balancer instances for all external clusters.
getExternalLB( $cluster)
Get the tracked load balancer instance for an external cluster.
getMainLB( $domain=false)
Get the tracked load balancer instance for the main cluster that handles the given domain.
newExternalLB( $cluster)
Create a new load balancer instance for an external cluster.
getLBsForOwner()
Get all tracked load balancers with the internal "for owner" interface.
reconfigure(array $conf)
Reconfigure using the given config array.
__construct(array $conf)
Template override precedence (highest => lowest):
Internal interface for LoadBalancer methods used by LBFactory.
This class is a delegate to ILBFactory for a given database cluster.
const GROUP_GENERIC
The generic query group.