Split DBLockManager classes into their own files
authorAaron Schulz <aschulz@wikimedia.org>
Sat, 20 Aug 2016 17:26:01 +0000 (10:26 -0700)
committerAaron Schulz <aschulz@wikimedia.org>
Sat, 20 Aug 2016 17:33:37 +0000 (10:33 -0700)
Change-Id: If903a90a5be2d6ff11504d34eb125e86c1ab1191

autoload.php
includes/filebackend/lockmanager/DBLockManager.php
includes/filebackend/lockmanager/MySqlLockManager.php [new file with mode: 0644]
includes/filebackend/lockmanager/PostgreSqlLockManager.php [new file with mode: 0644]

index e37a011..c718dec 100644 (file)
@@ -950,7 +950,7 @@ $wgAutoloadLocalClasses = [
        'MwSql' => __DIR__ . '/maintenance/sql.php',
        'MySQLField' => __DIR__ . '/includes/db/DatabaseMysqlBase.php',
        'MySQLMasterPos' => __DIR__ . '/includes/db/DatabaseMysqlBase.php',
-       'MySqlLockManager' => __DIR__ . '/includes/filebackend/lockmanager/DBLockManager.php',
+       'MySqlLockManager' => __DIR__ . '/includes/filebackend/lockmanager/MySqlLockManager.php',
        'MysqlInstaller' => __DIR__ . '/includes/installer/MysqlInstaller.php',
        'MysqlUpdater' => __DIR__ . '/includes/installer/MysqlUpdater.php',
        'NaiveForeignTitleFactory' => __DIR__ . '/includes/title/NaiveForeignTitleFactory.php',
@@ -1058,7 +1058,7 @@ $wgAutoloadLocalClasses = [
        'PopulateRecentChangesSource' => __DIR__ . '/maintenance/populateRecentChangesSource.php',
        'PopulateRevisionLength' => __DIR__ . '/maintenance/populateRevisionLength.php',
        'PopulateRevisionSha1' => __DIR__ . '/maintenance/populateRevisionSha1.php',
-       'PostgreSqlLockManager' => __DIR__ . '/includes/filebackend/lockmanager/DBLockManager.php',
+       'PostgreSqlLockManager' => __DIR__ . '/includes/filebackend/lockmanager/PostgreSqlLockManager.php',
        'PostgresBlob' => __DIR__ . '/includes/db/DatabasePostgres.php',
        'PostgresField' => __DIR__ . '/includes/db/DatabasePostgres.php',
        'PostgresInstaller' => __DIR__ . '/includes/installer/PostgresInstaller.php',
index c9aad43..0aefbfa 100644 (file)
@@ -239,205 +239,3 @@ abstract class DBLockManager extends QuorumLockManager {
                }
        }
 }
-
-/**
- * MySQL version of DBLockManager that supports shared locks.
- *
- * All lock servers must have the innodb table defined in locking/filelocks.sql.
- * All locks are non-blocking, which avoids deadlocks.
- *
- * @ingroup LockManager
- */
-class MySqlLockManager extends DBLockManager {
-       /** @var array Mapping of lock types to the type actually used */
-       protected $lockTypeMap = [
-               self::LOCK_SH => self::LOCK_SH,
-               self::LOCK_UW => self::LOCK_SH,
-               self::LOCK_EX => self::LOCK_EX
-       ];
-
-       protected function getLocalLB() {
-               // Use a separate connection so releaseAllLocks() doesn't rollback the main trx
-               return wfGetLBFactory()->newMainLB( $this->domain );
-       }
-
-       protected function initConnection( $lockDb, IDatabase $db ) {
-               # Let this transaction see lock rows from other transactions
-               $db->query( "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;" );
-       }
-
-       /**
-        * Get a connection to a lock DB and acquire locks on $paths.
-        * This does not use GET_LOCK() per http://bugs.mysql.com/bug.php?id=1118.
-        *
-        * @see DBLockManager::getLocksOnServer()
-        * @param string $lockSrv
-        * @param array $paths
-        * @param string $type
-        * @return Status
-        */
-       protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
-               $status = Status::newGood();
-
-               $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
-
-               $keys = []; // list of hash keys for the paths
-               $data = []; // list of rows to insert
-               $checkEXKeys = []; // list of hash keys that this has no EX lock on
-               # Build up values for INSERT clause
-               foreach ( $paths as $path ) {
-                       $key = $this->sha1Base36Absolute( $path );
-                       $keys[] = $key;
-                       $data[] = [ 'fls_key' => $key, 'fls_session' => $this->session ];
-                       if ( !isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
-                               $checkEXKeys[] = $key;
-                       }
-               }
-
-               # Block new writers (both EX and SH locks leave entries here)...
-               $db->insert( 'filelocks_shared', $data, __METHOD__, [ 'IGNORE' ] );
-               # Actually do the locking queries...
-               if ( $type == self::LOCK_SH ) { // reader locks
-                       $blocked = false;
-                       # Bail if there are any existing writers...
-                       if ( count( $checkEXKeys ) ) {
-                               $blocked = $db->selectField( 'filelocks_exclusive', '1',
-                                       [ 'fle_key' => $checkEXKeys ],
-                                       __METHOD__
-                               );
-                       }
-                       # Other prospective writers that haven't yet updated filelocks_exclusive
-                       # will recheck filelocks_shared after doing so and bail due to this entry.
-               } else { // writer locks
-                       $encSession = $db->addQuotes( $this->session );
-                       # Bail if there are any existing writers...
-                       # This may detect readers, but the safe check for them is below.
-                       # Note: if two writers come at the same time, both bail :)
-                       $blocked = $db->selectField( 'filelocks_shared', '1',
-                               [ 'fls_key' => $keys, "fls_session != $encSession" ],
-                               __METHOD__
-                       );
-                       if ( !$blocked ) {
-                               # Build up values for INSERT clause
-                               $data = [];
-                               foreach ( $keys as $key ) {
-                                       $data[] = [ 'fle_key' => $key ];
-                               }
-                               # Block new readers/writers...
-                               $db->insert( 'filelocks_exclusive', $data, __METHOD__ );
-                               # Bail if there are any existing readers...
-                               $blocked = $db->selectField( 'filelocks_shared', '1',
-                                       [ 'fls_key' => $keys, "fls_session != $encSession" ],
-                                       __METHOD__
-                               );
-                       }
-               }
-
-               if ( $blocked ) {
-                       foreach ( $paths as $path ) {
-                               $status->fatal( 'lockmanager-fail-acquirelock', $path );
-                       }
-               }
-
-               return $status;
-       }
-
-       /**
-        * @see QuorumLockManager::releaseAllLocks()
-        * @return Status
-        */
-       protected function releaseAllLocks() {
-               $status = Status::newGood();
-
-               foreach ( $this->conns as $lockDb => $db ) {
-                       if ( $db->trxLevel() ) { // in transaction
-                               try {
-                                       $db->rollback( __METHOD__ ); // finish transaction and kill any rows
-                               } catch ( DBError $e ) {
-                                       $status->fatal( 'lockmanager-fail-db-release', $lockDb );
-                               }
-                       }
-               }
-
-               return $status;
-       }
-}
-
-/**
- * PostgreSQL version of DBLockManager that supports shared locks.
- * All locks are non-blocking, which avoids deadlocks.
- *
- * @ingroup LockManager
- */
-class PostgreSqlLockManager extends DBLockManager {
-       /** @var array Mapping of lock types to the type actually used */
-       protected $lockTypeMap = [
-               self::LOCK_SH => self::LOCK_SH,
-               self::LOCK_UW => self::LOCK_SH,
-               self::LOCK_EX => self::LOCK_EX
-       ];
-
-       protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
-               $status = Status::newGood();
-               if ( !count( $paths ) ) {
-                       return $status; // nothing to lock
-               }
-
-               $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
-               $bigints = array_unique( array_map(
-                       function ( $key ) {
-                               return Wikimedia\base_convert( substr( $key, 0, 15 ), 16, 10 );
-                       },
-                       array_map( [ $this, 'sha1Base16Absolute' ], $paths )
-               ) );
-
-               // Try to acquire all the locks...
-               $fields = [];
-               foreach ( $bigints as $bigint ) {
-                       $fields[] = ( $type == self::LOCK_SH )
-                               ? "pg_try_advisory_lock_shared({$db->addQuotes( $bigint )}) AS K$bigint"
-                               : "pg_try_advisory_lock({$db->addQuotes( $bigint )}) AS K$bigint";
-               }
-               $res = $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
-               $row = $res->fetchRow();
-
-               if ( in_array( 'f', $row ) ) {
-                       // Release any acquired locks if some could not be acquired...
-                       $fields = [];
-                       foreach ( $row as $kbigint => $ok ) {
-                               if ( $ok === 't' ) { // locked
-                                       $bigint = substr( $kbigint, 1 ); // strip off the "K"
-                                       $fields[] = ( $type == self::LOCK_SH )
-                                               ? "pg_advisory_unlock_shared({$db->addQuotes( $bigint )})"
-                                               : "pg_advisory_unlock({$db->addQuotes( $bigint )})";
-                               }
-                       }
-                       if ( count( $fields ) ) {
-                               $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
-                       }
-                       foreach ( $paths as $path ) {
-                               $status->fatal( 'lockmanager-fail-acquirelock', $path );
-                       }
-               }
-
-               return $status;
-       }
-
-       /**
-        * @see QuorumLockManager::releaseAllLocks()
-        * @return Status
-        */
-       protected function releaseAllLocks() {
-               $status = Status::newGood();
-
-               foreach ( $this->conns as $lockDb => $db ) {
-                       try {
-                               $db->query( "SELECT pg_advisory_unlock_all()", __METHOD__ );
-                       } catch ( DBError $e ) {
-                               $status->fatal( 'lockmanager-fail-db-release', $lockDb );
-                       }
-               }
-
-               return $status;
-       }
-}
diff --git a/includes/filebackend/lockmanager/MySqlLockManager.php b/includes/filebackend/lockmanager/MySqlLockManager.php
new file mode 100644 (file)
index 0000000..c51df16
--- /dev/null
@@ -0,0 +1,123 @@
+<?php
+/**
+ * MySQL version of DBLockManager that supports shared locks.
+ *
+ * All lock servers must have the innodb table defined in locking/filelocks.sql.
+ * All locks are non-blocking, which avoids deadlocks.
+ *
+ * @ingroup LockManager
+ */
+class MySqlLockManager extends DBLockManager {
+       /** @var array Mapping of lock types to the type actually used */
+       protected $lockTypeMap = [
+               self::LOCK_SH => self::LOCK_SH,
+               self::LOCK_UW => self::LOCK_SH,
+               self::LOCK_EX => self::LOCK_EX
+       ];
+
+       protected function getLocalLB() {
+               // Use a separate connection so releaseAllLocks() doesn't rollback the main trx
+               return wfGetLBFactory()->newMainLB( $this->domain );
+       }
+
+       protected function initConnection( $lockDb, IDatabase $db ) {
+               # Let this transaction see lock rows from other transactions
+               $db->query( "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;" );
+       }
+
+       /**
+        * Get a connection to a lock DB and acquire locks on $paths.
+        * This does not use GET_LOCK() per http://bugs.mysql.com/bug.php?id=1118.
+        *
+        * @see DBLockManager::getLocksOnServer()
+        * @param string $lockSrv
+        * @param array $paths
+        * @param string $type
+        * @return Status
+        */
+       protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
+               $status = Status::newGood();
+
+               $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
+
+               $keys = []; // list of hash keys for the paths
+               $data = []; // list of rows to insert
+               $checkEXKeys = []; // list of hash keys that this has no EX lock on
+               # Build up values for INSERT clause
+               foreach ( $paths as $path ) {
+                       $key = $this->sha1Base36Absolute( $path );
+                       $keys[] = $key;
+                       $data[] = [ 'fls_key' => $key, 'fls_session' => $this->session ];
+                       if ( !isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
+                               $checkEXKeys[] = $key;
+                       }
+               }
+
+               # Block new writers (both EX and SH locks leave entries here)...
+               $db->insert( 'filelocks_shared', $data, __METHOD__, [ 'IGNORE' ] );
+               # Actually do the locking queries...
+               if ( $type == self::LOCK_SH ) { // reader locks
+                       $blocked = false;
+                       # Bail if there are any existing writers...
+                       if ( count( $checkEXKeys ) ) {
+                               $blocked = $db->selectField( 'filelocks_exclusive', '1',
+                                       [ 'fle_key' => $checkEXKeys ],
+                                       __METHOD__
+                               );
+                       }
+                       # Other prospective writers that haven't yet updated filelocks_exclusive
+                       # will recheck filelocks_shared after doing so and bail due to this entry.
+               } else { // writer locks
+                       $encSession = $db->addQuotes( $this->session );
+                       # Bail if there are any existing writers...
+                       # This may detect readers, but the safe check for them is below.
+                       # Note: if two writers come at the same time, both bail :)
+                       $blocked = $db->selectField( 'filelocks_shared', '1',
+                               [ 'fls_key' => $keys, "fls_session != $encSession" ],
+                               __METHOD__
+                       );
+                       if ( !$blocked ) {
+                               # Build up values for INSERT clause
+                               $data = [];
+                               foreach ( $keys as $key ) {
+                                       $data[] = [ 'fle_key' => $key ];
+                               }
+                               # Block new readers/writers...
+                               $db->insert( 'filelocks_exclusive', $data, __METHOD__ );
+                               # Bail if there are any existing readers...
+                               $blocked = $db->selectField( 'filelocks_shared', '1',
+                                       [ 'fls_key' => $keys, "fls_session != $encSession" ],
+                                       __METHOD__
+                               );
+                       }
+               }
+
+               if ( $blocked ) {
+                       foreach ( $paths as $path ) {
+                               $status->fatal( 'lockmanager-fail-acquirelock', $path );
+                       }
+               }
+
+               return $status;
+       }
+
+       /**
+        * @see QuorumLockManager::releaseAllLocks()
+        * @return Status
+        */
+       protected function releaseAllLocks() {
+               $status = Status::newGood();
+
+               foreach ( $this->conns as $lockDb => $db ) {
+                       if ( $db->trxLevel() ) { // in transaction
+                               try {
+                                       $db->rollback( __METHOD__ ); // finish transaction and kill any rows
+                               } catch ( DBError $e ) {
+                                       $status->fatal( 'lockmanager-fail-db-release', $lockDb );
+                               }
+                       }
+               }
+
+               return $status;
+       }
+}
diff --git a/includes/filebackend/lockmanager/PostgreSqlLockManager.php b/includes/filebackend/lockmanager/PostgreSqlLockManager.php
new file mode 100644 (file)
index 0000000..d55b5ae
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+/**
+ * PostgreSQL version of DBLockManager that supports shared locks.
+ * All locks are non-blocking, which avoids deadlocks.
+ *
+ * @ingroup LockManager
+ */
+class PostgreSqlLockManager extends DBLockManager {
+       /** @var array Mapping of lock types to the type actually used */
+       protected $lockTypeMap = [
+               self::LOCK_SH => self::LOCK_SH,
+               self::LOCK_UW => self::LOCK_SH,
+               self::LOCK_EX => self::LOCK_EX
+       ];
+
+       protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
+               $status = Status::newGood();
+               if ( !count( $paths ) ) {
+                       return $status; // nothing to lock
+               }
+
+               $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
+               $bigints = array_unique( array_map(
+                       function ( $key ) {
+                               return Wikimedia\base_convert( substr( $key, 0, 15 ), 16, 10 );
+                       },
+                       array_map( [ $this, 'sha1Base16Absolute' ], $paths )
+               ) );
+
+               // Try to acquire all the locks...
+               $fields = [];
+               foreach ( $bigints as $bigint ) {
+                       $fields[] = ( $type == self::LOCK_SH )
+                               ? "pg_try_advisory_lock_shared({$db->addQuotes( $bigint )}) AS K$bigint"
+                               : "pg_try_advisory_lock({$db->addQuotes( $bigint )}) AS K$bigint";
+               }
+               $res = $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
+               $row = $res->fetchRow();
+
+               if ( in_array( 'f', $row ) ) {
+                       // Release any acquired locks if some could not be acquired...
+                       $fields = [];
+                       foreach ( $row as $kbigint => $ok ) {
+                               if ( $ok === 't' ) { // locked
+                                       $bigint = substr( $kbigint, 1 ); // strip off the "K"
+                                       $fields[] = ( $type == self::LOCK_SH )
+                                               ? "pg_advisory_unlock_shared({$db->addQuotes( $bigint )})"
+                                               : "pg_advisory_unlock({$db->addQuotes( $bigint )})";
+                               }
+                       }
+                       if ( count( $fields ) ) {
+                               $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
+                       }
+                       foreach ( $paths as $path ) {
+                               $status->fatal( 'lockmanager-fail-acquirelock', $path );
+                       }
+               }
+
+               return $status;
+       }
+
+       /**
+        * @see QuorumLockManager::releaseAllLocks()
+        * @return Status
+        */
+       protected function releaseAllLocks() {
+               $status = Status::newGood();
+
+               foreach ( $this->conns as $lockDb => $db ) {
+                       try {
+                               $db->query( "SELECT pg_advisory_unlock_all()", __METHOD__ );
+                       } catch ( DBError $e ) {
+                               $status->fatal( 'lockmanager-fail-db-release', $lockDb );
+                       }
+               }
+
+               return $status;
+       }
+}