MediaWiki  master
LBFactoryMulti.php
Go to the documentation of this file.
1 <?php
24 namespace Wikimedia\Rdbms;
25 
26 use InvalidArgumentException;
27 use LogicException;
28 use UnexpectedValueException;
29 
35 class LBFactoryMulti extends LBFactory {
37  private $mainLBs = [];
39  private $externalLBs = [];
40 
42  private $hostsByName;
44  private $sectionsByDB;
48  private $groupLoadsByDB;
52  private $serverTemplate;
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->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 ) {
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 ) {
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 ) {
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 ) {
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() {
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() {
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,
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 master 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 master defined." );
285  }
286  $groupLoadsByHost = $this->reindexGroupLoadsByHost( $groupLoads );
287  // Get the ordered map of (host => load); the master 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( $groupLoadsByHost ), 0 );
291  $servers = [];
292  foreach ( $genericLoads as $host => $load ) {
293  $servers[] = array_merge(
295  $servers ? [] : $this->masterTemplateOverrides,
296  $this->templateOverridesByServer[$host] ?? [],
297  [
298  'host' => $this->hostsByName[$host] ?? $host,
299  'hostName' => $host,
300  'load' => $load,
301  'groupLoads' => $groupLoadsByHost[$host] ?? []
302  ]
303  );
304  }
305 
306  return $servers;
307  }
308 
314  private function reindexGroupLoadsByHost( $groupLoads ) {
315  $groupLoadsByHost = [];
316  foreach ( $groupLoads as $group => $loadByHost ) {
317  foreach ( $loadByHost as $host => $load ) {
318  $groupLoadsByHost[$host][$group] = $load;
319  }
320  }
321 
322  return $groupLoadsByHost;
323  }
324 
329  private function getSectionFromDatabase( $database ) {
330  return $this->sectionsByDB[$database] ?? self::CLUSTER_MAIN_DEFAULT;
331  }
332 }
Wikimedia\Rdbms\LBFactoryMulti\$masterTemplateOverrides
array $masterTemplateOverrides
Server config override map for all main and external master servers.
Definition: LBFactoryMulti.php:60
Wikimedia\Rdbms\ILoadBalancer\GROUP_GENERIC
const GROUP_GENERIC
The generic query group.
Definition: ILoadBalancer.php:90
Wikimedia\Rdbms\LBFactoryMulti\$externalLoadsByCluster
int[][] $externalLoadsByCluster
Map of (cluster => host => load ratio)
Definition: LBFactoryMulti.php:50
Wikimedia\Rdbms\LBFactoryMulti\$groupLoadsByDB
int[][][] $groupLoadsByDB
Map of (database => group => host => load ratio)
Definition: LBFactoryMulti.php:48
Wikimedia\Rdbms\LBFactoryMulti\$loadMonitorConfig
array $loadMonitorConfig
Configuration for the LoadMonitor to use within LoadBalancer instances.
Definition: LBFactoryMulti.php:66
Wikimedia\Rdbms\LBFactory\initLoadBalancer
initLoadBalancer(ILoadBalancer $lb)
Definition: LBFactory.php:663
Wikimedia\Rdbms\LBFactoryMulti\$hostsByName
string[] $hostsByName
Map of (hostname => IP address)
Definition: LBFactoryMulti.php:42
Wikimedia\Rdbms\LBFactoryMulti\reindexGroupLoadsByHost
reindexGroupLoadsByHost( $groupLoads)
Take a group load array indexed by group then server, and reindex it by server then group.
Definition: LBFactoryMulti.php:314
Wikimedia\Rdbms\LBFactory\getOwnershipId
getOwnershipId()
Definition: LBFactory.php:796
Wikimedia\Rdbms
Definition: ChronologyProtector.php:24
Wikimedia\Rdbms\LBFactory\resolveDomainInstance
resolveDomainInstance( $domain)
Definition: LBFactory.php:194
Wikimedia\Rdbms\LBFactoryMulti\$templateOverridesBySection
array[] $templateOverridesBySection
Map of (section => server config map overrides)
Definition: LBFactoryMulti.php:56
Wikimedia\Rdbms\LBFactoryMulti\makeServerConfigArrays
makeServerConfigArrays(array $serverTemplate, array $groupLoads)
Make a server array as expected by LoadBalancer::__construct()
Definition: LBFactoryMulti.php:281
Wikimedia\Rdbms\LBFactoryMulti\$externalTemplateOverrides
array $externalTemplateOverrides
Server config map overriding "serverTemplate" for external storage.
Definition: LBFactoryMulti.php:54
Wikimedia\Rdbms\LBFactoryMulti\getSectionFromDatabase
getSectionFromDatabase( $database)
Definition: LBFactoryMulti.php:329
Wikimedia\Rdbms\LBFactoryMulti\__construct
__construct(array $conf)
Template override precedence (highest => lowest):
Definition: LBFactoryMulti.php:113
Wikimedia\Rdbms\LBFactoryMulti\$templateOverridesByCluster
array[] $templateOverridesByCluster
Map of (cluster => server config map overrides) for external storage.
Definition: LBFactoryMulti.php:58
Wikimedia\Rdbms\LBFactory\$readOnlyReason
string bool $readOnlyReason
Reason all LBs are read-only or false if not.
Definition: LBFactory.php:106
Wikimedia\Rdbms\LBFactoryMulti\forEachLB
forEachLB( $callback, array $params=[])
Execute a function for each currently tracked (instantiated) load balancer.
Definition: LBFactoryMulti.php:234
Wikimedia\Rdbms\LBFactoryMulti\getMainLB
getMainLB( $domain=false)
Get a cached (tracked) load balancer object.
Definition: LBFactoryMulti.php:175
Wikimedia\Rdbms\LBFactoryMulti\newLoadBalancer
newLoadBalancer(string $clusterName, array $serverTemplate, array $groupLoads, $readOnlyReason, $owner)
Make a new load balancer object based on template and load array.
Definition: LBFactoryMulti.php:253
Wikimedia\Rdbms\LBFactoryMulti\$mainLBs
LoadBalancer[] $mainLBs
Map of (section => tracked LoadBalancer)
Definition: LBFactoryMulti.php:37
Wikimedia\Rdbms\LBFactoryMulti\$sectionsByDB
string[] $sectionsByDB
Map of (database name => section name)
Definition: LBFactoryMulti.php:44
Wikimedia\Rdbms\LBFactory\baseLoadBalancerParams
baseLoadBalancerParams( $owner)
Get parameters to ILoadBalancer::__construct()
Definition: LBFactory.php:624
Wikimedia\Rdbms\LBFactoryMulti\getAllExternalLBs
getAllExternalLBs()
Get cached (tracked) load balancers for all external database clusters.
Definition: LBFactoryMulti.php:225
Wikimedia\Rdbms\LoadBalancer
Database connection, tracking, load balancing, and transaction manager for a cluster.
Definition: LoadBalancer.php:42
Wikimedia\Rdbms\LBFactoryMulti\$externalLBs
LoadBalancer[] $externalLBs
Map of (cluster => tracked LoadBalancer)
Definition: LBFactoryMulti.php:39
Wikimedia\Rdbms\LBFactoryMulti\$serverTemplate
array $serverTemplate
Server config map ("host", "hostName", "load", and "groupLoads" are ignored)
Definition: LBFactoryMulti.php:52
Wikimedia\Rdbms\LBFactoryMulti
A multi-database, multi-master factory for Wikimedia and similar installations.
Definition: LBFactoryMulti.php:35
Wikimedia\Rdbms\LBFactoryMulti\$templateOverridesByServer
array[] $templateOverridesByServer
Map of (host => server config map overrides) for main and external servers.
Definition: LBFactoryMulti.php:62
Wikimedia\Rdbms\LBFactoryMulti\getAllMainLBs
getAllMainLBs()
Get cached (tracked) load balancers for all main database clusters.
Definition: LBFactoryMulti.php:214
Wikimedia\Rdbms\LBFactoryMulti\newMainLB
newMainLB( $domain=false, $owner=null)
Create a new load balancer object.
Definition: LBFactoryMulti.php:149
Wikimedia\Rdbms\LBFactory
An interface for generating database load balancers.
Definition: LBFactory.php:41
Wikimedia\Rdbms\LBFactoryMulti\$readOnlyBySection
string[] bool[] $readOnlyBySection
A map of section name to read-only message.
Definition: LBFactoryMulti.php:64
Wikimedia\Rdbms\LBFactoryMulti\$groupLoadsBySection
int[][][] $groupLoadsBySection
Map of (section => group => host => load ratio)
Definition: LBFactoryMulti.php:46
Wikimedia\Rdbms\LBFactoryMulti\getExternalLB
getExternalLB( $cluster)
Get a cached (tracked) load balancer for external storage.
Definition: LBFactoryMulti.php:203
Wikimedia\Rdbms\LBFactoryMulti\newExternalLB
newExternalLB( $cluster, $owner=null)
Create a new load balancer for external storage.
Definition: LBFactoryMulti.php:186