MediaWiki master
LBFactoryMulti.php
Go to the documentation of this file.
1<?php
6namespace Wikimedia\Rdbms;
7
8use InvalidArgumentException;
9use LogicException;
10use UnexpectedValueException;
11
41 private $mainLBs = [];
43 private $externalLBs = [];
44
46 private $hostsByServerName;
48 private $sectionsByDB;
50 private $loadsBySection;
52 private $externalLoadsByCluster;
54 private $serverTemplate;
56 private $externalTemplateOverrides;
58 private $templateOverridesBySection;
60 private $templateOverridesByCluster;
62 private $masterTemplateOverrides;
64 private $templateOverridesByServer;
66 private $readOnlyBySection;
68 private $loadMonitorConfig;
70 private $nonLocalDomainCache = [];
71
118 public function __construct( array $conf ) {
119 parent::__construct( $conf );
120
121 $this->hostsByServerName = $conf['hostsByName'] ?? [];
122 $this->sectionsByDB = $conf['sectionsByDB'];
123 $this->sectionsByDB += [ self::CLUSTER_MAIN_DEFAULT => self::CLUSTER_MAIN_DEFAULT ];
124 $this->loadsBySection = $conf['sectionLoads'] ?? [];
125 $this->externalLoadsByCluster = $conf['externalLoads'] ?? [];
126 $this->serverTemplate = $conf['serverTemplate'] ?? [];
127 $this->externalTemplateOverrides = $conf['externalTemplateOverrides'] ?? [];
128 $this->templateOverridesBySection = $conf['templateOverridesBySection'] ?? [];
129 $this->templateOverridesByCluster = $conf['templateOverridesByCluster'] ?? [];
130 $this->masterTemplateOverrides = $conf['masterTemplateOverrides'] ?? [];
131 $this->templateOverridesByServer = $conf['templateOverridesByServer'] ?? [];
132 $this->readOnlyBySection = $conf['readOnlyBySection'] ?? [];
133
134 if ( isset( $conf['loadMonitor'] ) ) {
135 $this->loadMonitorConfig = $conf['loadMonitor'];
136 } elseif ( isset( $conf['loadMonitorClass'] ) ) { // b/c
137 $this->loadMonitorConfig = [ 'class' => $conf['loadMonitorClass'] ];
138 } else {
139 $this->loadMonitorConfig = [ 'class' => LoadMonitor::class ];
140 }
141
142 foreach ( $this->externalLoadsByCluster as $cluster => $_ ) {
143 if ( isset( $this->loadsBySection[$cluster] ) ) {
144 throw new LogicException(
145 "External cluster '$cluster' has the same name as a main section/cluster"
146 );
147 }
148 }
149 }
150
152 public function newMainLB( $domain = false ): ILoadBalancerForOwner {
153 $domainInstance = $this->resolveDomainInstance( $domain );
154 $database = $domainInstance->getDatabase();
155 $section = $this->getSectionFromDatabase( $database );
156
157 if ( !isset( $this->loadsBySection[$section] ) ) {
158 throw new UnexpectedValueException( "Section '$section' has no hosts defined." );
159 }
160
161 return $this->newLoadBalancer(
162 $section,
163 array_merge(
164 $this->serverTemplate,
165 $this->templateOverridesBySection[$section] ?? []
166 ),
167 $this->loadsBySection[$section],
168 // Use the LB-specific read-only reason if everything isn't already read-only
169 is_string( $this->readOnlyReason )
170 ? $this->readOnlyReason
171 : ( $this->readOnlyBySection[$section] ?? false )
172 );
173 }
174
179 private function resolveDomainInstance( $domain ) {
180 if ( $domain instanceof DatabaseDomain ) {
181 return $domain; // already a domain instance
182 } elseif ( $domain === false || $domain === $this->localDomain->getId() ) {
183 return $this->localDomain;
184 } elseif ( isset( $this->domainAliases[$domain] ) ) {
185 // This array acts as both the original map and as instance cache.
186 // Instances pass-through DatabaseDomain::newFromId as-is.
187 $this->domainAliases[$domain] =
188 DatabaseDomain::newFromId( $this->domainAliases[$domain] );
189
190 return $this->domainAliases[$domain];
191 }
192
193 $cachedDomain = $this->nonLocalDomainCache[$domain] ?? null;
194 if ( $cachedDomain === null ) {
195 $cachedDomain = DatabaseDomain::newFromId( $domain );
196 $this->nonLocalDomainCache = [ $domain => $cachedDomain ];
197 }
198
199 return $cachedDomain;
200 }
201
203 public function getMainLB( $domain = false ): ILoadBalancer {
204 $domainInstance = $this->resolveDomainInstance( $domain );
205 $section = $this->getSectionFromDatabase( $domainInstance->getDatabase() );
206
207 if ( !isset( $this->mainLBs[$section] ) ) {
208 $this->mainLBs[$section] = $this->newMainLB( $domain );
209 }
210
211 return $this->mainLBs[$section];
212 }
213
215 public function newExternalLB( $cluster ): ILoadBalancerForOwner {
216 if ( !isset( $this->externalLoadsByCluster[$cluster] ) ) {
217 throw new InvalidArgumentException( "Unknown cluster '$cluster'" );
218 }
219 return $this->newLoadBalancer(
220 $cluster,
221 array_merge(
222 $this->serverTemplate,
223 $this->externalTemplateOverrides,
224 $this->templateOverridesByCluster[$cluster] ?? []
225 ),
226 $this->externalLoadsByCluster[$cluster],
227 $this->readOnlyReason
228 );
229 }
230
232 public function getExternalLB( $cluster ): ILoadBalancer {
233 if ( !isset( $this->externalLBs[$cluster] ) ) {
234 $this->externalLBs[$cluster] = $this->newExternalLB(
235 $cluster
236 );
237 }
238
239 return $this->externalLBs[$cluster];
240 }
241
242 public function getAllMainLBs(): array {
243 $lbs = [];
244 foreach ( $this->sectionsByDB as $db => $section ) {
245 if ( !isset( $lbs[$section] ) ) {
246 $lbs[$section] = $this->getMainLB( $db );
247 }
248 }
249
250 return $lbs;
251 }
252
253 public function getAllExternalLBs(): array {
254 $lbs = [];
255 foreach ( $this->externalLoadsByCluster as $cluster => $unused ) {
256 $lbs[$cluster] = $this->getExternalLB( $cluster );
257 }
258
259 return $lbs;
260 }
261
263 protected function getLBsForOwner() {
264 foreach ( $this->mainLBs as $lb ) {
265 yield $lb;
266 }
267 foreach ( $this->externalLBs as $lb ) {
268 yield $lb;
269 }
270 }
271
281 private function newLoadBalancer(
282 string $clusterName,
283 array $serverTemplate,
284 array $loads,
285 $readOnlyReason
286 ) {
287 $lb = new LoadBalancer( array_merge(
288 $this->baseLoadBalancerParams(),
289 [
290 'servers' => $this->makeServerConfigArrays( $serverTemplate, $loads ),
291 'loadMonitor' => $this->loadMonitorConfig,
292 'readOnlyReason' => $readOnlyReason,
293 'clusterName' => $clusterName
294 ]
295 ) );
296 $this->initLoadBalancer( $lb );
297
298 return $lb;
299 }
300
308 private function makeServerConfigArrays( array $serverTemplate, array $loads ) {
309 // Get the ordered map of (server name => load); the primary DB server is first
310 $servers = [];
311 foreach ( $loads as $serverName => $load ) {
312 $servers[] = array_merge(
313 $serverTemplate,
314 $servers ? [] : $this->masterTemplateOverrides,
315 $this->templateOverridesByServer[$serverName] ?? [],
316 [
317 'host' => $this->hostsByServerName[$serverName] ?? $serverName,
318 'serverName' => $serverName,
319 'load' => $load,
320 ]
321 );
322 }
323
324 return $servers;
325 }
326
331 private function getSectionFromDatabase( $database ) {
332 if ( $database !== null && isset( $this->sectionsByDB[$database] ) ) {
333 return $this->sectionsByDB[$database];
334 }
335 return $this->sectionsByDB[self::CLUSTER_MAIN_DEFAULT]
336 ?? self::CLUSTER_MAIN_DEFAULT;
337 }
338
339 public function reconfigure( array $conf ): void {
340 if ( !$conf ) {
341 return;
342 }
343
344 foreach ( $this->mainLBs as $lb ) {
345 // Approximate what LBFactoryMulti::__construct does (T346365)
346 $config = [
347 'servers' => $this->makeServerConfigArrays(
348 $conf['serverTemplate'] ?? [],
349 $conf['sectionLoads'][$lb->getClusterName()]
350 )
351 ];
352 $lb->reconfigure( $config );
353
354 }
355 foreach ( $this->externalLBs as $lb ) {
356 $config = [
357 'servers' => $this->makeServerConfigArrays(
358 $conf['serverTemplate'] ?? [],
359 $conf['externalLoads'][$lb->getClusterName()]
360 )
361 ];
362 $lb->reconfigure( $config );
363 }
364 }
365}
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:69
Class to handle database/schema/prefix specifications for IDatabase.
static newFromId(DatabaseDomain|string $domain)
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.If no tracked instances exists,...
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.The resulting object will be untracked an...
getLBsForOwner()
Get all tracked load balancers with the internal "for owner" interface.Generator|ILoadBalancerForOwne...
reconfigure(array $conf)
Reconfigure using the given config array.
__construct(array $conf)
Template override precedence (highest => lowest):
const CLUSTER_MAIN_DEFAULT
Default main cluster name (do not change this)
Internal interface for load balancer instances exposed to their owner.
This class is a delegate to ILBFactory for a given database cluster.