MediaWiki REL1_34
LBFactoryMulti.php
Go to the documentation of this file.
1<?php
24namespace Wikimedia\Rdbms;
25
26use InvalidArgumentException;
27use UnexpectedValueException;
28
37 private $mainLBs = [];
39 private $externalLBs = [];
40
42 private $hostsByName = [];
44 private $sectionsByDB = [];
48 private $groupLoadsByDB = [];
50 private $externalLoads = [];
52 private $serverTemplate = [];
64 private $readOnlyBySection = [];
65
68
70 private $lastDomain;
72 private $lastSection;
73
138 public function __construct( array $conf ) {
139 parent::__construct( $conf );
140
141 $this->hostsByName = $conf['hostsByName'] ?? [];
142 $this->sectionsByDB = $conf['sectionsByDB'];
143 $this->groupLoadsBySection = $conf['groupLoadsBySection'] ?? [];
144 foreach ( ( $conf['sectionLoads'] ?? [] ) as $section => $loadByHost ) {
145 $this->groupLoadsBySection[$section][ILoadBalancer::GROUP_GENERIC] = $loadByHost;
146 }
147 $this->groupLoadsByDB = $conf['groupLoadsByDB'] ?? [];
148 $this->externalLoads = $conf['externalLoads'] ?? [];
149 $this->serverTemplate = $conf['serverTemplate'] ?? [];
150 $this->externalTemplateOverrides = $conf['externalTemplateOverrides'] ?? [];
151 $this->templateOverridesBySection = $conf['templateOverridesBySection'] ?? [];
152 $this->templateOverridesByCluster = $conf['templateOverridesByCluster'] ?? [];
153 $this->masterTemplateOverrides = $conf['masterTemplateOverrides'] ?? [];
154 $this->templateOverridesByServer = $conf['templateOverridesByServer'] ?? [];
155 $this->readOnlyBySection = $conf['readOnlyBySection'] ?? [];
156
157 $this->loadMonitorClass = $conf['loadMonitorClass'] ?? LoadMonitor::class;
158 }
159
160 public function newMainLB( $domain = false, $owner = null ) {
161 $section = $this->getSectionForDomain( $domain );
162 if ( !isset( $this->groupLoadsBySection[$section][ILoadBalancer::GROUP_GENERIC] ) ) {
163 throw new UnexpectedValueException( "Section '$section' has no hosts defined." );
164 }
165
166 $dbGroupLoads = $this->groupLoadsByDB[$this->getDomainDatabase( $domain )] ?? [];
167 unset( $dbGroupLoads[ILoadBalancer::GROUP_GENERIC] ); // cannot override
168
169 return $this->newLoadBalancer(
170 array_merge(
171 $this->serverTemplate,
172 $this->templateOverridesBySection[$section] ?? []
173 ),
174 array_merge( $this->groupLoadsBySection[$section], $dbGroupLoads ),
175 // Use the LB-specific read-only reason if everything isn't already read-only
176 is_string( $this->readOnlyReason )
177 ? $this->readOnlyReason
178 : ( $this->readOnlyBySection[$section] ?? false ),
179 $owner
180 );
181 }
182
183 public function getMainLB( $domain = false ) {
184 $section = $this->getSectionForDomain( $domain );
185
186 if ( !isset( $this->mainLBs[$section] ) ) {
187 $this->mainLBs[$section] = $this->newMainLB( $domain, $this->getOwnershipId() );
188 }
189
190 return $this->mainLBs[$section];
191 }
192
193 public function newExternalLB( $cluster, $owner = null ) {
194 if ( !isset( $this->externalLoads[$cluster] ) ) {
195 throw new InvalidArgumentException( "Unknown cluster '$cluster'" );
196 }
197
198 return $this->newLoadBalancer(
199 array_merge(
200 $this->serverTemplate,
201 $this->externalTemplateOverrides,
202 $this->templateOverridesByCluster[$cluster] ?? []
203 ),
204 [ ILoadBalancer::GROUP_GENERIC => $this->externalLoads[$cluster] ],
205 $this->readOnlyReason,
206 $owner
207 );
208 }
209
210 public function getExternalLB( $cluster ) {
211 if ( !isset( $this->externalLBs[$cluster] ) ) {
212 $this->externalLBs[$cluster] =
213 $this->newExternalLB( $cluster, $this->getOwnershipId() );
214 }
215
216 return $this->externalLBs[$cluster];
217 }
218
219 public function getAllMainLBs() {
220 $lbs = [];
221 foreach ( $this->sectionsByDB as $db => $section ) {
222 if ( !isset( $lbs[$section] ) ) {
223 $lbs[$section] = $this->getMainLB( $db );
224 }
225 }
226
227 return $lbs;
228 }
229
230 public function getAllExternalLBs() {
231 $lbs = [];
232 foreach ( $this->externalLoads as $cluster => $unused ) {
233 $lbs[$cluster] = $this->getExternalLB( $cluster );
234 }
235
236 return $lbs;
237 }
238
239 public function forEachLB( $callback, array $params = [] ) {
240 foreach ( $this->mainLBs as $lb ) {
241 $callback( $lb, ...$params );
242 }
243 foreach ( $this->externalLBs as $lb ) {
244 $callback( $lb, ...$params );
245 }
246 }
247
252 private function getSectionForDomain( $domain = false ) {
253 if ( $this->lastDomain === $domain ) {
254 return $this->lastSection;
255 }
256
257 $database = $this->getDomainDatabase( $domain );
258 $section = $this->sectionsByDB[$database] ?? self::CLUSTER_MAIN_DEFAULT;
259 $this->lastSection = $section;
260 $this->lastDomain = $domain;
261
262 return $section;
263 }
264
274 private function newLoadBalancer( $serverTemplate, $groupLoads, $readOnlyReason, $owner ) {
275 $lb = new LoadBalancer( array_merge(
276 $this->baseLoadBalancerParams( $owner ),
277 [
278 'servers' => $this->makeServerArray( $serverTemplate, $groupLoads ),
279 'loadMonitor' => [ 'class' => $this->loadMonitorClass ],
280 'readOnlyReason' => $readOnlyReason
281 ]
282 ) );
283 $this->initLoadBalancer( $lb );
284
285 return $lb;
286 }
287
295 private function makeServerArray( array $serverTemplate, array $groupLoads ) {
296 // The master server is the first host explicitly listed in the generic load group
297 if ( !$groupLoads[ILoadBalancer::GROUP_GENERIC] ) {
298 throw new UnexpectedValueException( "Empty generic load array; no master defined." );
299 }
300
301 $groupLoadsByHost = $this->reindexGroupLoads( $groupLoads );
302 // Get the ordered map of (host => load); the master server is first
303 $genericLoads = $groupLoads[ILoadBalancer::GROUP_GENERIC];
304 // Implictly append any hosts that only appear in custom load groups
305 $genericLoads += array_fill_keys( array_keys( $groupLoadsByHost ), 0 );
306
307 $servers = [];
308 foreach ( $genericLoads as $host => $load ) {
309 $servers[] = array_merge(
311 $servers ? [] : $this->masterTemplateOverrides,
312 $this->templateOverridesByServer[$host] ?? [],
313 [
314 'host' => $this->hostsByName[$host] ?? $host,
315 'hostName' => $host,
316 'load' => $load,
317 'groupLoads' => $groupLoadsByHost[$host] ?? []
318 ]
319 );
320 }
321
322 return $servers;
323 }
324
330 private function reindexGroupLoads( array $groupLoads ) {
331 $reindexed = [];
332
333 foreach ( $groupLoads as $group => $loadByHost ) {
334 foreach ( $loadByHost as $host => $load ) {
335 $reindexed[$host][$group] = $load;
336 }
337 }
338
339 return $reindexed;
340 }
341
346 private function getDomainDatabase( $domain = false ) {
347 return ( $domain === false )
348 ? $this->localDomain->getDatabase()
349 : DatabaseDomain::newFromId( $domain )->getDatabase();
350 }
351}
A multi-database, multi-master factory for Wikimedia and similar installations.
int[][][] $groupLoadsBySection
Map of (section => group => host => load ratio)
string[] bool[] $readOnlyBySection
A map of section name to read-only message.
array[] $templateOverridesByCluster
Map of (cluster => server config map overrides) for external storage.
getAllMainLBs()
Get cached (tracked) load balancers for all main database clusters.
getAllExternalLBs()
Get cached (tracked) load balancers for all external database clusters.
newMainLB( $domain=false, $owner=null)
Create a new load balancer object.
getExternalLB( $cluster)
Get a cached (tracked) load balancer for external storage.
getMainLB( $domain=false)
Get a cached (tracked) load balancer object.
forEachLB( $callback, array $params=[])
Execute a function for each currently tracked (instantiated) load balancer.
array $externalTemplateOverrides
Server config map overriding "serverTemplate" for external storage.
array $masterTemplateOverrides
Server config override map for all main and external master servers.
makeServerArray(array $serverTemplate, array $groupLoads)
Make a server array as expected by LoadBalancer::__construct()
newExternalLB( $cluster, $owner=null)
Create a new load balancer for external storage.
array $serverTemplate
Server config map ("host", "hostName", "load", and "groupLoads" are ignored)
int[][] $externalLoads
Map of (cluster => host => load ratio)
newLoadBalancer( $serverTemplate, $groupLoads, $readOnlyReason, $owner)
Make a new load balancer object based on template and load array.
string[] $hostsByName
Map of (hostname => IP address)
reindexGroupLoads(array $groupLoads)
Take a group load array indexed by group then server, and reindex it by server then group.
array[] $templateOverridesBySection
Map of (section => server config map overrides)
string $loadMonitorClass
An ILoadMonitor class.
__construct(array $conf)
Template override precedence (highest => lowest):
array[] $templateOverridesByServer
Map of (host => server config map overrides) for main and external servers.
string[] $sectionsByDB
Map of (database name => section name)
int[][][] $groupLoadsByDB
Map of (database => group => host => load ratio)
An interface for generating database load balancers.
Definition LBFactory.php:40
baseLoadBalancerParams( $owner)
Get parameters to ILoadBalancer::__construct()
initLoadBalancer(ILoadBalancer $lb)
string bool $readOnlyReason
Reason all LBs are read-only or false if not.
Definition LBFactory.php:98
Database connection, tracking, load balancing, and transaction manager for a cluster.