MediaWiki REL1_39
LBFactoryMulti.php
Go to the documentation of this file.
1<?php
20namespace Wikimedia\Rdbms;
21
22use InvalidArgumentException;
23use LogicException;
24use UnexpectedValueException;
25
36 private $mainLBs = [];
38 private $externalLBs = [];
39
41 private $hostsByServerName;
43 private $sectionsByDB;
45 private $groupLoadsBySection;
47 private $externalLoadsByCluster;
49 private $serverTemplate;
51 private $externalTemplateOverrides;
53 private $templateOverridesBySection;
55 private $templateOverridesByCluster;
57 private $masterTemplateOverrides;
59 private $templateOverridesByServer;
61 private $readOnlyBySection;
63 private $loadMonitorConfig;
64
112 public function __construct( array $conf ) {
113 parent::__construct( $conf );
114
115 $this->hostsByServerName = $conf['hostsByName'] ?? [];
116 $this->sectionsByDB = $conf['sectionsByDB'];
117 $this->groupLoadsBySection = $conf['groupLoadsBySection'] ?? [];
118 foreach ( ( $conf['sectionLoads'] ?? [] ) as $section => $loadsByServerName ) {
119 $this->groupLoadsBySection[$section][ILoadBalancer::GROUP_GENERIC] = $loadsByServerName;
120 }
121 $this->externalLoadsByCluster = $conf['externalLoads'] ?? [];
122 $this->serverTemplate = $conf['serverTemplate'] ?? [];
123 $this->externalTemplateOverrides = $conf['externalTemplateOverrides'] ?? [];
124 $this->templateOverridesBySection = $conf['templateOverridesBySection'] ?? [];
125 $this->templateOverridesByCluster = $conf['templateOverridesByCluster'] ?? [];
126 $this->masterTemplateOverrides = $conf['masterTemplateOverrides'] ?? [];
127 $this->templateOverridesByServer = $conf['templateOverridesByServer'] ?? [];
128 $this->readOnlyBySection = $conf['readOnlyBySection'] ?? [];
129
130 if ( isset( $conf['loadMonitor'] ) ) {
131 $this->loadMonitorConfig = $conf['loadMonitor'];
132 } elseif ( isset( $conf['loadMonitorClass'] ) ) { // b/c
133 $this->loadMonitorConfig = [ 'class' => $conf['loadMonitorClass'] ];
134 } else {
135 $this->loadMonitorConfig = [ 'class' => LoadMonitor::class ];
136 }
137
138 foreach ( array_keys( $this->externalLoadsByCluster ) as $cluster ) {
139 if ( isset( $this->groupLoadsBySection[$cluster] ) ) {
140 throw new LogicException(
141 "External cluster '$cluster' has the same name as a main section/cluster"
142 );
143 }
144 }
145 }
146
147 public function newMainLB( $domain = false ): ILoadBalancerForOwner {
148 $domainInstance = $this->resolveDomainInstance( $domain );
149 $database = $domainInstance->getDatabase();
150 $section = $this->getSectionFromDatabase( $database );
151
152 if ( !isset( $this->groupLoadsBySection[$section][ILoadBalancer::GROUP_GENERIC] ) ) {
153 throw new UnexpectedValueException( "Section '$section' has no hosts defined." );
154 }
155
156 return $this->newLoadBalancer(
157 $section,
158 array_merge(
159 $this->serverTemplate,
160 $this->templateOverridesBySection[$section] ?? []
161 ),
162 $this->groupLoadsBySection[$section],
163 // Use the LB-specific read-only reason if everything isn't already read-only
164 is_string( $this->readOnlyReason )
165 ? $this->readOnlyReason
166 : ( $this->readOnlyBySection[$section] ?? false )
167 );
168 }
169
170 public function getMainLB( $domain = false ): ILoadBalancer {
171 $domainInstance = $this->resolveDomainInstance( $domain );
172 $section = $this->getSectionFromDatabase( $domainInstance->getDatabase() );
173
174 if ( !isset( $this->mainLBs[$section] ) ) {
175 $this->mainLBs[$section] = $this->newMainLB( $domain );
176 }
177
178 return $this->mainLBs[$section];
179 }
180
181 public function newExternalLB( $cluster ): ILoadBalancerForOwner {
182 if ( !isset( $this->externalLoadsByCluster[$cluster] ) ) {
183 throw new InvalidArgumentException( "Unknown cluster '$cluster'" );
184 }
185 return $this->newLoadBalancer(
186 $cluster,
187 array_merge(
188 $this->serverTemplate,
189 $this->externalTemplateOverrides,
190 $this->templateOverridesByCluster[$cluster] ?? []
191 ),
192 [ ILoadBalancer::GROUP_GENERIC => $this->externalLoadsByCluster[$cluster] ],
193 $this->readOnlyReason
194 );
195 }
196
197 public function getExternalLB( $cluster ): ILoadBalancer {
198 if ( !isset( $this->externalLBs[$cluster] ) ) {
199 $this->externalLBs[$cluster] = $this->newExternalLB(
200 $cluster
201 );
202 }
203
204 return $this->externalLBs[$cluster];
205 }
206
207 public function getAllMainLBs(): array {
208 $lbs = [];
209 foreach ( $this->sectionsByDB as $db => $section ) {
210 if ( !isset( $lbs[$section] ) ) {
211 $lbs[$section] = $this->getMainLB( $db );
212 }
213 }
214
215 return $lbs;
216 }
217
218 public function getAllExternalLBs(): array {
219 $lbs = [];
220 foreach ( $this->externalLoadsByCluster as $cluster => $unused ) {
221 $lbs[$cluster] = $this->getExternalLB( $cluster );
222 }
223
224 return $lbs;
225 }
226
227 public function forEachLB( $callback, array $params = [] ) {
228 wfDeprecated( __METHOD__, '1.39' );
229 foreach ( $this->mainLBs as $lb ) {
230 $callback( $lb, ...$params );
231 }
232 foreach ( $this->externalLBs as $lb ) {
233 $callback( $lb, ...$params );
234 }
235 }
236
237 protected function getLBsForOwner() {
238 foreach ( $this->mainLBs as $lb ) {
239 yield $lb;
240 }
241 foreach ( $this->externalLBs as $lb ) {
242 yield $lb;
243 }
244 }
245
255 private function newLoadBalancer(
256 string $clusterName,
257 array $serverTemplate,
258 array $groupLoads,
259 $readOnlyReason
260 ) {
261 $lb = new LoadBalancer( array_merge(
262 $this->baseLoadBalancerParams(),
263 [
264 'servers' => $this->makeServerConfigArrays( $serverTemplate, $groupLoads ),
265 'loadMonitor' => $this->loadMonitorConfig,
266 'readOnlyReason' => $readOnlyReason,
267 'clusterName' => $clusterName
268 ]
269 ) );
270 $this->initLoadBalancer( $lb );
271
272 return $lb;
273 }
274
282 private function makeServerConfigArrays( array $serverTemplate, array $groupLoads ) {
283 // The primary DB server is the first host explicitly listed in the generic load group
284 if ( !$groupLoads[ILoadBalancer::GROUP_GENERIC] ) {
285 throw new UnexpectedValueException( "Empty generic load array; no primary DB defined." );
286 }
287 $groupLoadsByServerName = $this->reindexGroupLoadsByServerName( $groupLoads );
288 // Get the ordered map of (server name => load); the primary DB server is first
289 $genericLoads = $groupLoads[ILoadBalancer::GROUP_GENERIC];
290 // Implicitly append any hosts that only appear in custom load groups
291 $genericLoads += array_fill_keys( array_keys( $groupLoadsByServerName ), 0 );
292 $servers = [];
293 foreach ( $genericLoads as $serverName => $load ) {
294 $servers[] = array_merge(
295 $serverTemplate,
296 $servers ? [] : $this->masterTemplateOverrides,
297 $this->templateOverridesByServer[$serverName] ?? [],
298 [
299 'host' => $this->hostsByServerName[$serverName] ?? $serverName,
300 'serverName' => $serverName,
301 'load' => $load,
302 'groupLoads' => $groupLoadsByServerName[$serverName] ?? []
303 ]
304 );
305 }
306
307 return $servers;
308 }
309
316 private function reindexGroupLoadsByServerName( array $groupLoads ) {
317 $groupLoadsByServerName = [];
318 foreach ( $groupLoads as $group => $loadByServerName ) {
319 foreach ( $loadByServerName as $serverName => $load ) {
320 $groupLoadsByServerName[$serverName][$group] = $load;
321 }
322 }
323
324 return $groupLoadsByServerName;
325 }
326
331 private function getSectionFromDatabase( $database ) {
332 return $this->sectionsByDB[$database] ?? self::CLUSTER_MAIN_DEFAULT;
333 }
334}
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Definition WebStart.php:82
Advanced manager for multiple database sections, e.g.
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.
forEachLB( $callback, array $params=[])
Execute a function for each instantiated tracked load balancer instance.
newExternalLB( $cluster)
Create a new load balancer instance for an external cluster.
getLBsForOwner()
Get all tracked load balancers with the internal "for owner" interface.
__construct(array $conf)
Template override precedence (highest => lowest):
Internal interface for LoadBalancer methods used by LBFactory.
Create and track the database connections and transactions for a given database cluster.
const GROUP_GENERIC
The generic query group.