MediaWiki master
PoolCounterConnectionManager.php
Go to the documentation of this file.
1<?php
8
9use InvalidArgumentException;
11use Wikimedia\IPUtils;
12
21 public $hostNames;
23 public $conns = [];
25 public $refCounts = [];
27 public $timeout;
30
35 public $host;
36
41 public $port;
42
46 public function __construct( $conf ) {
47 if ( !count( $conf['servers'] ) ) {
48 throw new InvalidArgumentException( __METHOD__ . ': no servers configured' );
49 }
50 $this->hostNames = $conf['servers'];
51 $this->timeout = $conf['timeout'] ?? 0.1;
52 $this->connect_timeout = $conf['connect_timeout'] ?? 0;
53 }
54
59 public function get( $key ) {
60 $hashes = [];
61 foreach ( $this->hostNames as $hostName ) {
62 $hashes[$hostName] = md5( $hostName . $key );
63 }
64 asort( $hashes );
65 $errno = 0;
66 $errstr = '';
67 $hostName = '';
68 $conn = null;
69 foreach ( $hashes as $hostName => $hash ) {
70 if ( isset( $this->conns[$hostName] ) ) {
71 $this->refCounts[$hostName]++;
72 return Status::newGood(
73 [ 'conn' => $this->conns[$hostName], 'hostName' => $hostName ] );
74 }
75 $parts = IPUtils::splitHostAndPort( $hostName );
76 if ( $parts === false ) {
77 $errstr = '\'servers\' config incorrectly configured.';
78 return Status::newFatal( 'poolcounter-connection-error', $errstr, $hostName );
79 }
80 // IPV6 addresses need to be in brackets otherwise it fails.
81 $this->host = IPUtils::isValidIPv6( $parts[0] ) ? '[' . $parts[0] . ']' : $parts[0];
82 $this->port = $parts[1] ?: 7531;
83 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
84 $conn = @$this->open( $this->host, $this->port, $errno, $errstr );
85 if ( $conn ) {
86 break;
87 }
88 }
89 if ( !$conn ) {
90 return Status::newFatal( 'poolcounter-connection-error', $errstr, $hostName );
91 }
92 // TODO: Inject PSR Logger from ServiceWiring
93 wfDebug( "Connected to pool counter server: $hostName\n" );
94 $this->conns[$hostName] = $conn;
95 $this->refCounts[$hostName] = 1;
96 return Status::newGood( [ 'conn' => $conn, 'hostName' => $hostName ] );
97 }
98
107 private function open( $host, $port, &$errno, &$errstr ) {
108 // If connect_timeout is set, we try to open the socket twice.
109 // You usually want to set the connection timeout to a very
110 // small value so that in case of failure of a server the
111 // connection to poolcounter is not a SPOF.
112 if ( $this->connect_timeout > 0 ) {
113 $tries = 2;
115 } else {
116 $tries = 1;
118 }
119
120 $fp = null;
121 while ( true ) {
122 $fp = fsockopen( $host, $port, $errno, $errstr, $timeout );
123 if ( $fp !== false || --$tries < 1 ) {
124 break;
125 }
126 usleep( 1000 );
127 }
128
129 return $fp;
130 }
131
135 public function close( $conn ) {
136 foreach ( $this->conns as $hostName => $otherConn ) {
137 if ( $conn === $otherConn ) {
138 if ( $this->refCounts[$hostName] ) {
139 $this->refCounts[$hostName]--;
140 }
141 if ( !$this->refCounts[$hostName] ) {
142 fclose( $conn );
143 unset( $this->conns[$hostName] );
144 }
145 }
146 }
147 }
148}
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Helper for \MediaWiki\PoolCounter\PoolCounterClient.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44