MediaWiki  master
PoolCounterConnectionManager.php
Go to the documentation of this file.
1 <?php
21 namespace MediaWiki\PoolCounter;
22 
24 use MWException;
25 
34  public $hostNames;
36  public $conns = [];
38  public $refCounts = [];
40  public $timeout;
43 
48  public function __construct( $conf ) {
49  $this->hostNames = $conf['servers'];
50  $this->timeout = $conf['timeout'] ?? 0.1;
51  $this->connect_timeout = $conf['connect_timeout'] ?? 0;
52  if ( !count( $this->hostNames ) ) {
53  throw new MWException( __METHOD__ . ': no servers configured' );
54  }
55  }
56 
61  public function get( $key ) {
62  $hashes = [];
63  foreach ( $this->hostNames as $hostName ) {
64  $hashes[$hostName] = md5( $hostName . $key );
65  }
66  asort( $hashes );
67  $errno = 0;
68  $errstr = '';
69  $hostName = '';
70  $conn = null;
71  foreach ( $hashes as $hostName => $hash ) {
72  if ( isset( $this->conns[$hostName] ) ) {
73  $this->refCounts[$hostName]++;
74  return Status::newGood(
75  [ 'conn' => $this->conns[$hostName], 'hostName' => $hostName ] );
76  }
77  $parts = explode( ':', $hostName, 2 );
78  if ( count( $parts ) < 2 ) {
79  $parts[] = 7531;
80  }
81  // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
82  $conn = @$this->open( $parts[0], $parts[1], $errno, $errstr );
83  if ( $conn ) {
84  break;
85  }
86  }
87  if ( !$conn ) {
88  return Status::newFatal( 'poolcounter-connection-error', $errstr, $hostName );
89  }
90  // TODO: Inject PSR Logger from ServiceWiring
91  wfDebug( "Connected to pool counter server: $hostName\n" );
92  $this->conns[$hostName] = $conn;
93  $this->refCounts[$hostName] = 1;
94  return Status::newGood( [ 'conn' => $conn, 'hostName' => $hostName ] );
95  }
96 
105  private function open( $host, $port, &$errno, &$errstr ) {
106  // If connect_timeout is set, we try to open the socket twice.
107  // You usually want to set the connection timeout to a very
108  // small value so that in case of failure of a server the
109  // connection to poolcounter is not a SPOF.
110  if ( $this->connect_timeout > 0 ) {
111  $tries = 2;
113  } else {
114  $tries = 1;
116  }
117 
118  $fp = null;
119  while ( true ) {
120  $fp = fsockopen( $host, $port, $errno, $errstr, $timeout );
121  if ( $fp !== false || --$tries < 1 ) {
122  break;
123  }
124  usleep( 1000 );
125  }
126 
127  return $fp;
128  }
129 
133  public function close( $conn ) {
134  foreach ( $this->conns as $hostName => $otherConn ) {
135  if ( $conn === $otherConn ) {
136  if ( $this->refCounts[$hostName] ) {
137  $this->refCounts[$hostName]--;
138  }
139  if ( !$this->refCounts[$hostName] ) {
140  fclose( $conn );
141  unset( $this->conns[$hostName] );
142  }
143  }
144  }
145  }
146 }
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
MediaWiki exception.
Definition: MWException.php:33
Helper for \MediaWiki\PoolCounter\PoolCounterClient.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:58
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:73
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:85