MediaWiki master
PoolCounterConnectionManager.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\PoolCounter;
22
23use InvalidArgumentException;
25use Wikimedia\IPUtils;
26
35 public $hostNames;
37 public $conns = [];
39 public $refCounts = [];
41 public $timeout;
44
48 public $host;
49
53 public $port;
54
58 public function __construct( $conf ) {
59 if ( !count( $conf['servers'] ) ) {
60 throw new InvalidArgumentException( __METHOD__ . ': no servers configured' );
61 }
62 $this->hostNames = $conf['servers'];
63 $this->timeout = $conf['timeout'] ?? 0.1;
64 $this->connect_timeout = $conf['connect_timeout'] ?? 0;
65 }
66
71 public function get( $key ) {
72 $hashes = [];
73 foreach ( $this->hostNames as $hostName ) {
74 $hashes[$hostName] = md5( $hostName . $key );
75 }
76 asort( $hashes );
77 $errno = 0;
78 $errstr = '';
79 $hostName = '';
80 $conn = null;
81 foreach ( $hashes as $hostName => $hash ) {
82 if ( isset( $this->conns[$hostName] ) ) {
83 $this->refCounts[$hostName]++;
84 return Status::newGood(
85 [ 'conn' => $this->conns[$hostName], 'hostName' => $hostName ] );
86 }
87 $parts = IPUtils::splitHostAndPort( $hostName );
88 if ( $parts === false ) {
89 $errstr = '\'servers\' config incorrectly configured.';
90 return Status::newFatal( 'poolcounter-connection-error', $errstr, $hostName );
91 }
92 // IPV6 addresses need to be in brackets otherwise it fails.
93 $this->host = IPUtils::isValidIPv6( $parts[0] ) ? '[' . $parts[0] . ']' : $parts[0];
94 $this->port = $parts[1] ?: 7531;
95 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
96 $conn = @$this->open( $this->host, $this->port, $errno, $errstr );
97 if ( $conn ) {
98 break;
99 }
100 }
101 if ( !$conn ) {
102 return Status::newFatal( 'poolcounter-connection-error', $errstr, $hostName );
103 }
104 // TODO: Inject PSR Logger from ServiceWiring
105 wfDebug( "Connected to pool counter server: $hostName\n" );
106 $this->conns[$hostName] = $conn;
107 $this->refCounts[$hostName] = 1;
108 return Status::newGood( [ 'conn' => $conn, 'hostName' => $hostName ] );
109 }
110
119 private function open( $host, $port, &$errno, &$errstr ) {
120 // If connect_timeout is set, we try to open the socket twice.
121 // You usually want to set the connection timeout to a very
122 // small value so that in case of failure of a server the
123 // connection to poolcounter is not a SPOF.
124 if ( $this->connect_timeout > 0 ) {
125 $tries = 2;
127 } else {
128 $tries = 1;
130 }
131
132 $fp = null;
133 while ( true ) {
134 $fp = fsockopen( $host, $port, $errno, $errstr, $timeout );
135 if ( $fp !== false || --$tries < 1 ) {
136 break;
137 }
138 usleep( 1000 );
139 }
140
141 return $fp;
142 }
143
147 public function close( $conn ) {
148 foreach ( $this->conns as $hostName => $otherConn ) {
149 if ( $conn === $otherConn ) {
150 if ( $this->refCounts[$hostName] ) {
151 $this->refCounts[$hostName]--;
152 }
153 if ( !$this->refCounts[$hostName] ) {
154 fclose( $conn );
155 unset( $this->conns[$hostName] );
156 }
157 }
158 }
159 }
160}
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:54