MediaWiki fundraising/REL1_35
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;
67
113 public function __construct( array $conf ) {
114 parent::__construct( $conf );
115
116 $this->hostsByName = $conf['hostsByName'] ?? [];
117 $this->sectionsByDB = $conf['sectionsByDB'];
118 $this->groupLoadsBySection = $conf['groupLoadsBySection'] ?? [];
119 foreach ( ( $conf['sectionLoads'] ?? [] ) as $section => $loadByHost ) {
120 $this->groupLoadsBySection[$section][ILoadBalancer::GROUP_GENERIC] = $loadByHost;
121 }
122 $this->groupLoadsByDB = $conf['groupLoadsByDB'] ?? [];
123 $this->externalLoads = $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
141 public function newMainLB( $domain = false, $owner = null ) {
142 $domainInstance = $this->resolveDomainInstance( $domain );
143 $database = $domainInstance->getDatabase();
144 $section = $this->getSectionFromDatabase( $database );
145 if ( !isset( $this->groupLoadsBySection[$section][ILoadBalancer::GROUP_GENERIC] ) ) {
146 throw new UnexpectedValueException( "Section '$section' has no hosts defined." );
147 }
148 $dbGroupLoads = $this->groupLoadsByDB[$database] ?? [];
149 unset( $dbGroupLoads[ILoadBalancer::GROUP_GENERIC] ); // cannot override
150 return $this->newLoadBalancer(
151 array_merge(
152 $this->serverTemplate,
153 $this->templateOverridesBySection[$section] ?? []
154 ),
155 array_merge( $this->groupLoadsBySection[$section], $dbGroupLoads ),
156 // Use the LB-specific read-only reason if everything isn't already read-only
157 is_string( $this->readOnlyReason )
158 ? $this->readOnlyReason
159 : ( $this->readOnlyBySection[$section] ?? false ),
160 $owner
161 );
162 }
163
164 public function getMainLB( $domain = false ) {
165 $domainInstance = $this->resolveDomainInstance( $domain );
166 $section = $this->getSectionFromDatabase( $domainInstance->getDatabase() );
167
168 if ( !isset( $this->mainLBs[$section] ) ) {
169 $this->mainLBs[$section] = $this->newMainLB( $domain, $this->getOwnershipId() );
170 }
171
172 return $this->mainLBs[$section];
173 }
174
175 public function newExternalLB( $cluster, $owner = null ) {
176 if ( !isset( $this->externalLoads[$cluster] ) ) {
177 throw new InvalidArgumentException( "Unknown cluster '$cluster'" );
178 }
179 return $this->newLoadBalancer(
180 array_merge(
181 $this->serverTemplate,
182 $this->externalTemplateOverrides,
183 $this->templateOverridesByCluster[$cluster] ?? []
184 ),
185 [ ILoadBalancer::GROUP_GENERIC => $this->externalLoads[$cluster] ],
186 $this->readOnlyReason,
187 $owner
188 );
189 }
190
191 public function getExternalLB( $cluster ) {
192 if ( !isset( $this->externalLBs[$cluster] ) ) {
193 $this->externalLBs[$cluster] =
194 $this->newExternalLB( $cluster, $this->getOwnershipId() );
195 }
196
197 return $this->externalLBs[$cluster];
198 }
199
200 public function getAllMainLBs() {
201 $lbs = [];
202 foreach ( $this->sectionsByDB as $db => $section ) {
203 if ( !isset( $lbs[$section] ) ) {
204 $lbs[$section] = $this->getMainLB( $db );
205 }
206 }
207
208 return $lbs;
209 }
210
211 public function getAllExternalLBs() {
212 $lbs = [];
213 foreach ( $this->externalLoads as $cluster => $unused ) {
214 $lbs[$cluster] = $this->getExternalLB( $cluster );
215 }
216
217 return $lbs;
218 }
219
220 public function forEachLB( $callback, array $params = [] ) {
221 foreach ( $this->mainLBs as $lb ) {
222 $callback( $lb, ...$params );
223 }
224 foreach ( $this->externalLBs as $lb ) {
225 $callback( $lb, ...$params );
226 }
227 }
228
238 private function newLoadBalancer( $serverTemplate, $groupLoads, $readOnlyReason, $owner ) {
239 $lb = new LoadBalancer( array_merge(
240 $this->baseLoadBalancerParams( $owner ),
241 [
242 'servers' => $this->makeServerConfigArrays( $serverTemplate, $groupLoads ),
243 'loadMonitor' => $this->loadMonitorConfig,
244 'readOnlyReason' => $readOnlyReason
245 ]
246 ) );
247 $this->initLoadBalancer( $lb );
248
249 return $lb;
250 }
251
259 private function makeServerConfigArrays( array $serverTemplate, array $groupLoads ) {
260 // The master server is the first host explicitly listed in the generic load group
261 if ( !$groupLoads[ILoadBalancer::GROUP_GENERIC] ) {
262 throw new UnexpectedValueException( "Empty generic load array; no master defined." );
263 }
264 $groupLoadsByHost = $this->reindexGroupLoadsByHost( $groupLoads );
265 // Get the ordered map of (host => load); the master server is first
266 $genericLoads = $groupLoads[ILoadBalancer::GROUP_GENERIC];
267 // Implictly append any hosts that only appear in custom load groups
268 $genericLoads += array_fill_keys( array_keys( $groupLoadsByHost ), 0 );
269 $servers = [];
270 foreach ( $genericLoads as $host => $load ) {
271 $servers[] = array_merge(
273 $servers ? [] : $this->masterTemplateOverrides,
274 $this->templateOverridesByServer[$host] ?? [],
275 [
276 'host' => $this->hostsByName[$host] ?? $host,
277 'hostName' => $host,
278 'load' => $load,
279 'groupLoads' => $groupLoadsByHost[$host] ?? []
280 ]
281 );
282 }
283
284 return $servers;
285 }
286
292 private function reindexGroupLoadsByHost( $groupLoads ) {
293 $groupLoadsByHost = [];
294 foreach ( $groupLoads as $group => $loadByHost ) {
295 foreach ( $loadByHost as $host => $load ) {
296 $groupLoadsByHost[$host][$group] = $load;
297 }
298 }
299
300 return $groupLoadsByHost;
301 }
302
307 private function getSectionFromDatabase( $database ) {
308 return $this->sectionsByDB[$database] ?? 'DEFAULT';
309 }
310}
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.
reindexGroupLoadsByHost( $groupLoads)
Take a group load array indexed by group then server, and reindex it by server then group.
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.
LoadBalancer[] $mainLBs
Tracked main load balancer instances.
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.
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.
makeServerConfigArrays(array $serverTemplate, array $groupLoads)
Make a server array as expected by LoadBalancer::__construct()
string[] $hostsByName
Map of (hostname => IP address)
array[] $templateOverridesBySection
Map of (section => server config map overrides)
__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)
LoadBalancer[] $externalLBs
Tracked external load balancer instances.
array $loadMonitorConfig
Configuration for the LoadMonitor to use within LoadBalancer instances.
int[][][] $groupLoadsByDB
Map of (database => group => host => load ratio)
An interface for generating database load balancers.
Definition LBFactory.php:41
baseLoadBalancerParams( $owner)
Get parameters to ILoadBalancer::__construct()
initLoadBalancer(ILoadBalancer $lb)
string bool $readOnlyReason
Reason all LBs are read-only or false if not.
Database connection, tracking, load balancing, and transaction manager for a cluster.