MediaWiki  REL1_20
LSLockManager.php
Go to the documentation of this file.
00001 <?php
00039 class LSLockManager extends QuorumLockManager {
00041         protected $lockTypeMap = array(
00042                 self::LOCK_SH => self::LOCK_SH,
00043                 self::LOCK_UW => self::LOCK_SH,
00044                 self::LOCK_EX => self::LOCK_EX
00045         );
00046 
00048         protected $lockServers; // (server name => server config array)
00049 
00051         protected $conns = array();
00052 
00053         protected $connTimeout; // float number of seconds
00054         protected $session = ''; // random SHA-1 string
00055 
00071         public function __construct( array $config ) {
00072                 parent::__construct( $config );
00073 
00074                 $this->lockServers = $config['lockServers'];
00075                 // Sanitize srvsByBucket config to prevent PHP errors
00076                 $this->srvsByBucket = array_filter( $config['srvsByBucket'], 'is_array' );
00077                 $this->srvsByBucket = array_values( $this->srvsByBucket ); // consecutive
00078 
00079                 if ( isset( $config['connTimeout'] ) ) {
00080                         $this->connTimeout = $config['connTimeout'];
00081                 } else {
00082                         $this->connTimeout = 3; // use some sane amount
00083                 }
00084 
00085                 $this->session = wfRandomString( 32 ); // 128 bits
00086         }
00087 
00092         protected function getLocksOnServer( $lockSrv, array $paths, $type ) {
00093                 $status = Status::newGood();
00094 
00095                 // Send out the command and get the response...
00096                 $type = ( $type == self::LOCK_SH ) ? 'SH' : 'EX';
00097                 $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
00098                 $response = $this->sendCommand( $lockSrv, 'ACQUIRE', $type, $keys );
00099 
00100                 if ( $response !== 'ACQUIRED' ) {
00101                         foreach ( $paths as $path ) {
00102                                 $status->fatal( 'lockmanager-fail-acquirelock', $path );
00103                         }
00104                 }
00105 
00106                 return $status;
00107         }
00108 
00113         protected function freeLocksOnServer( $lockSrv, array $paths, $type ) {
00114                 $status = Status::newGood();
00115 
00116                 // Send out the command and get the response...
00117                 $type = ( $type == self::LOCK_SH ) ? 'SH' : 'EX';
00118                 $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
00119                 $response = $this->sendCommand( $lockSrv, 'RELEASE', $type, $keys );
00120 
00121                 if ( $response !== 'RELEASED' ) {
00122                         foreach ( $paths as $path ) {
00123                                 $status->fatal( 'lockmanager-fail-releaselock', $path );
00124                         }
00125                 }
00126 
00127                 return $status;
00128         }
00129 
00134         protected function releaseAllLocks() {
00135                 $status = Status::newGood();
00136 
00137                 foreach ( $this->conns as $lockSrv => $conn ) {
00138                         $response = $this->sendCommand( $lockSrv, 'RELEASE_ALL', '', array() );
00139                         if ( $response !== 'RELEASED_ALL' ) {
00140                                 $status->fatal( 'lockmanager-fail-svr-release', $lockSrv );
00141                         }
00142                 }
00143 
00144                 return $status;
00145         }
00146 
00151         protected function isServerUp( $lockSrv ) {
00152                 return (bool)$this->getConnection( $lockSrv );
00153         }
00154 
00164         protected function sendCommand( $lockSrv, $action, $type, $values ) {
00165                 $conn = $this->getConnection( $lockSrv );
00166                 if ( !$conn ) {
00167                         return false; // no connection
00168                 }
00169                 $authKey = $this->lockServers[$lockSrv]['authKey'];
00170                 // Build of the command as a flat string...
00171                 $values = implode( '|', $values );
00172                 $key = sha1( $this->session . $action . $type . $values . $authKey );
00173                 // Send out the command...
00174                 if ( fwrite( $conn, "{$this->session}:$key:$action:$type:$values\n" ) === false ) {
00175                         return false;
00176                 }
00177                 // Get the response...
00178                 $response = fgets( $conn );
00179                 if ( $response === false ) {
00180                         return false;
00181                 }
00182                 return trim( $response );
00183         }
00184 
00191         protected function getConnection( $lockSrv ) {
00192                 if ( !isset( $this->conns[$lockSrv] ) ) {
00193                         $cfg = $this->lockServers[$lockSrv];
00194                         wfSuppressWarnings();
00195                         $errno = $errstr = '';
00196                         $conn = fsockopen( $cfg['host'], $cfg['port'], $errno, $errstr, $this->connTimeout );
00197                         wfRestoreWarnings();
00198                         if ( $conn === false ) {
00199                                 return null;
00200                         }
00201                         $sec = floor( $this->connTimeout );
00202                         $usec = floor( ( $this->connTimeout - floor( $this->connTimeout ) ) * 1e6 );
00203                         stream_set_timeout( $conn, $sec, $usec );
00204                         $this->conns[$lockSrv] = $conn;
00205                 }
00206                 return $this->conns[$lockSrv];
00207         }
00208 
00212         function __destruct() {
00213                 $this->releaseAllLocks();
00214                 foreach ( $this->conns as $conn ) {
00215                         fclose( $conn );
00216                 }
00217         }
00218 }