r52889 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r52888‎ | r52889 | r52890 >
Date:08:15, 8 July 2009
Author:tstarling
Status:deferred
Tags:
Comment:
Client-side code for Domas's pool counter daemon (in python).
Modified paths:
  • /trunk/extensions/PoolCounter (added) (history)
  • /trunk/extensions/PoolCounter/PoolCounterClient.i18n.php (added) (history)
  • /trunk/extensions/PoolCounter/PoolCounterClient.php (added) (history)
  • /trunk/extensions/PoolCounter/PoolCounterClient_body.php (added) (history)

Diff [purge]

Index: trunk/extensions/PoolCounter/PoolCounterClient_body.php
@@ -0,0 +1,175 @@
 2+<?php
 3+
 4+class PoolCounter_ConnectionManager {
 5+ var $hostNames;
 6+ var $conns = array();
 7+ var $refCounts = array();
 8+
 9+ function __construct( $conf ) {
 10+ $this->hostNames = $conf['servers'];
 11+ $this->timeout = isset( $conf['timeout'] ) ? $conf['timeout'] : 0.1;
 12+ if ( !count( $this->hostNames ) ) {
 13+ throw new MWException( __METHOD__.': no servers configured' );
 14+ }
 15+ }
 16+
 17+ function get( $key ) {
 18+ $hashes = array();
 19+ foreach ( $this->hostNames as $hostName ) {
 20+ $hashes[$hostName] = md5( $hostName . $key );
 21+ }
 22+ asort( $hashes );
 23+ $errno = $errstr = '';
 24+ foreach ( $hashes as $hostName => $hash ) {
 25+ if ( isset( $this->conns[$hostName] ) ) {
 26+ $this->refCounts[$hostName]++;
 27+ return Status::newGood( $this->conns[$hostName] );
 28+ }
 29+ $parts = explode( ':', $hostName, 2 );
 30+ if ( count( $parts ) < 2 ) {
 31+ $parts[] = 7531;
 32+ }
 33+ $conn = fsockopen( $parts[0], $parts[1], $errno, $errstr, $this->timeout );
 34+ if ( $conn ) {
 35+ break;
 36+ }
 37+ }
 38+ if ( !$conn ) {
 39+ return Status::newFatal( 'poolcounter-connection-error', $errstr );
 40+ }
 41+ wfDebug( "Connected to pool counter server: $hostName\n" );
 42+ $this->conns[$hostName] = $conn;
 43+ $this->refCounts[$hostName] = 1;
 44+ return Status::newGood( $conn );
 45+ }
 46+
 47+ function close( $conn ) {
 48+ foreach ( $this->conns as $hostName => $otherConn ) {
 49+ if ( $conn === $otherConn ) {
 50+ if ( $this->refCounts[$hostName] ) {
 51+ $this->refCounts[$hostName]--;
 52+ }
 53+ if ( !$this->refCounts[$hostName] ) {
 54+ fclose( $conn );
 55+ unset( $this->conns[$hostName] );
 56+ }
 57+ }
 58+ }
 59+ }
 60+}
 61+
 62+class PoolCounter_Client extends PoolCounter {
 63+ var $maxThreads, $waitTimeout, $type, $key, $conn;
 64+ var $isAcquired = false;
 65+
 66+ static $manager;
 67+
 68+ function __construct( $conf, $type, $key ) {
 69+ $this->waitTimeout = isset( $conf['waitTimeout'] ) ? $conf['waitTimeout'] : 15;
 70+ $this->maxThreads = isset( $conf['maxThreads'] ) ? $conf['maxThreads'] : 5;
 71+ $this->type = $type;
 72+ $this->key = $key;
 73+ if ( !self::$manager ) {
 74+ global $wgPoolCountClientConf;
 75+ self::$manager = new PoolCounter_ConnectionManager( $wgPoolCountClientConf );
 76+ }
 77+ }
 78+
 79+ function getConn() {
 80+ if ( !isset( $this->conn ) ) {
 81+ $status = self::$manager->get( $this->key );
 82+ if ( !$status->isOK() ) {
 83+ return $status;
 84+ }
 85+ $this->conn = $status->value;
 86+ }
 87+ return Status::newGood( $this->conn );
 88+ }
 89+
 90+ function sendCommand( /*, ...*/ ) {
 91+ $args = func_get_args();
 92+ $args = str_replace( ' ', '%20', $args );
 93+ $cmd = implode( ' ', $args );
 94+ $status = $this->getConn();
 95+ if ( !$status->isOK() ) {
 96+ return $status;
 97+ }
 98+ $conn = $status->value;
 99+ wfDebug( "Sending pool counter command: $cmd\n" );
 100+ if ( fwrite( $conn, "$cmd\r\n" ) === false ) {
 101+ return Status::newFatal( 'poolcounter-write-error' );
 102+ }
 103+ $response = fgets( $conn );
 104+ if ( $response === false ) {
 105+ return Status::newFatal( 'poolcounter-read-error' );
 106+ }
 107+ $response = rtrim( $response, "\r\n" );
 108+ wfDebug( "Got pool counter response: $response\n" );
 109+ $parts = explode( ' ', $response, 2 );
 110+ $responseType = $parts[0];
 111+ switch ( $responseType ) {
 112+ case 'ERROR':
 113+ $parts = explode( ' ', $parts[1], 2 );
 114+ $errorMsg = isset( $parts[1] ) ? $parts[1] : '(no message given)';
 115+ return Status::newFatal( 'poolcounter-remote-error', $errorMsg );
 116+ case 'ACK':
 117+ case 'RELEASED':
 118+ case 'COUNT':
 119+ $parts = explode( ' ', $parts[1] );
 120+ $key = array_shift( $parts );
 121+ $attribs = $this->colonsToAssoc( $parts );
 122+ $attribs['responseType'] = $responseType;
 123+ return Status::newGood( $attribs );
 124+ }
 125+ }
 126+
 127+ function colonsToAssoc( $items ) {
 128+ $assoc = array();
 129+ foreach ( $items as $item ) {
 130+ $parts = explode( ':', $item, 2 );
 131+ if ( count( $parts ) !== 2 ) {
 132+ continue;
 133+ }
 134+ $assoc[$parts[0]] = $parts[1];
 135+ }
 136+ return $assoc;
 137+ }
 138+
 139+ function acquire() {
 140+ $status = $this->sendCommand( 'acquire', $this->key, "max:{$this->maxThreads}" );
 141+ if ( !$status->isOK() ) {
 142+ return $status;
 143+ }
 144+ $response = $status->value;
 145+ $count = isset( $response['count'] ) ? $response['count'] : 0;
 146+ $this->isAcquired = true;
 147+ if ( $count > $this->maxThreads ) {
 148+ $response['overload'] = true;
 149+ $this->release();
 150+ }
 151+ return Status::newGood( $response );
 152+ }
 153+
 154+ function release() {
 155+ if ( $this->isAcquired ) {
 156+ $status = $this->sendCommand( 'release', $this->key );
 157+ $this->isAcquired = false;
 158+ } else {
 159+ $status = Status::newGood();
 160+ }
 161+ if ( $this->conn ) {
 162+ self::$manager->close( $this->conn );
 163+ $this->conn = null;
 164+ }
 165+ return $status;
 166+ }
 167+
 168+ function wait() {
 169+ $status = $this->sendCommand( 'wait', $this->key, "timeout:{$this->waitTimeout}" );
 170+ if ( !$status->isOK() ) {
 171+ return $status;
 172+ }
 173+ $this->isAcquired = true;
 174+ return $status;
 175+ }
 176+}
Property changes on: trunk/extensions/PoolCounter/PoolCounterClient_body.php
___________________________________________________________________
Name: svn:eol-style
1177 + native
Index: trunk/extensions/PoolCounter/PoolCounterClient.i18n.php
@@ -0,0 +1,9 @@
 2+<?php
 3+
 4+$messages = array();
 5+$messages['en'] = array(
 6+ 'poolcounter-connection-error' => 'Error connecting to pool counter server: $1',
 7+ 'poolcounter-read-error' => 'Error reading from pool counter server',
 8+ 'poolcounter-write-error' => 'Error writing to pool counter server',
 9+ 'poolcounter-remote-error' => 'Pool counter server error: $1',
 10+);
Property changes on: trunk/extensions/PoolCounter/PoolCounterClient.i18n.php
___________________________________________________________________
Name: svn:eol-style
111 + native
Index: trunk/extensions/PoolCounter/PoolCounterClient.php
@@ -0,0 +1,35 @@
 2+<?php
 3+
 4+/**
 5+ * MediaWiki client for the pool counter daemon poolcounter.py.
 6+ */
 7+
 8+/**
 9+ * Configuration array for the connection manager.
 10+ * Use $wgPoolCounterConf to configure the pools.
 11+ */
 12+$wgPoolCountClientConf = array(
 13+ /**
 14+ * Array of hostnames, or hostname:port. The default port is 7531.
 15+ */
 16+ 'servers' => array( '127.0.0.1' ),
 17+
 18+ /**
 19+ * Connect timeout
 20+ */
 21+ 'timeout' => 0.1,
 22+);
 23+
 24+/**
 25+ * Sample pool configuration:
 26+ * $wgPoolCounterConf = array( 'Article::view' => array(
 27+ * 'class' => 'PoolCounter_Client',
 28+ * 'waitTimeout' => 15, // wait timeout in seconds
 29+ * 'maxThreads' => 5, // maximum number of threads in each pool
 30+ * ) );
 31+ */
 32+
 33+$wgAutoloadClasses['PoolCounter_ConnectionManager']
 34+ = $wgAutoloadClasses['PoolCounter_Client']
 35+ = dirname(__FILE__).'/PoolCounterClient_body.php';
 36+$wgExtensionMessagesFiles['PoolCounterClient'] = dirname(__FILE__).'/PoolCounterClient.i18n.php';
Property changes on: trunk/extensions/PoolCounter/PoolCounterClient.php
___________________________________________________________________
Name: svn:eol-style
137 + native

Status & tagging log