MediaWiki  REL1_22
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( array( $this, 'sha1Base36Absolute' ), $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( array( $this, 'sha1Base36Absolute' ), $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 = hash_hmac( 'sha1', "{$this->session}\n{$action}\n{$type}\n{$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 }