Merge "Special:Newpages feed now shows first revision instead of latest revision"
[lhc/web/wiklou.git] / includes / libs / lockmanager / PostgreSqlLockManager.php
1 <?php
2
3 use Wikimedia\Rdbms\DBError;
4
5 /**
6 * PostgreSQL version of DBLockManager that supports shared locks.
7 * All locks are non-blocking, which avoids deadlocks.
8 *
9 * @ingroup LockManager
10 */
11 class PostgreSqlLockManager extends DBLockManager {
12 /** @var array Mapping of lock types to the type actually used */
13 protected $lockTypeMap = [
14 self::LOCK_SH => self::LOCK_SH,
15 self::LOCK_UW => self::LOCK_SH,
16 self::LOCK_EX => self::LOCK_EX
17 ];
18
19 protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
20 $status = StatusValue::newGood();
21 if ( !count( $paths ) ) {
22 return $status; // nothing to lock
23 }
24
25 $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
26 $bigints = array_unique( array_map(
27 function ( $key ) {
28 return Wikimedia\base_convert( substr( $key, 0, 15 ), 16, 10 );
29 },
30 array_map( [ $this, 'sha1Base16Absolute' ], $paths )
31 ) );
32
33 // Try to acquire all the locks...
34 $fields = [];
35 foreach ( $bigints as $bigint ) {
36 $fields[] = ( $type == self::LOCK_SH )
37 ? "pg_try_advisory_lock_shared({$db->addQuotes( $bigint )}) AS K$bigint"
38 : "pg_try_advisory_lock({$db->addQuotes( $bigint )}) AS K$bigint";
39 }
40 $res = $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
41 $row = $res->fetchRow();
42
43 if ( in_array( 'f', $row ) ) {
44 // Release any acquired locks if some could not be acquired...
45 $fields = [];
46 foreach ( $row as $kbigint => $ok ) {
47 if ( $ok === 't' ) { // locked
48 $bigint = substr( $kbigint, 1 ); // strip off the "K"
49 $fields[] = ( $type == self::LOCK_SH )
50 ? "pg_advisory_unlock_shared({$db->addQuotes( $bigint )})"
51 : "pg_advisory_unlock({$db->addQuotes( $bigint )})";
52 }
53 }
54 if ( count( $fields ) ) {
55 $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
56 }
57 foreach ( $paths as $path ) {
58 $status->fatal( 'lockmanager-fail-acquirelock', $path );
59 }
60 }
61
62 return $status;
63 }
64
65 /**
66 * @see QuorumLockManager::releaseAllLocks()
67 * @return StatusValue
68 */
69 protected function releaseAllLocks() {
70 $status = StatusValue::newGood();
71
72 foreach ( $this->conns as $lockDb => $db ) {
73 try {
74 $db->query( "SELECT pg_advisory_unlock_all()", __METHOD__ );
75 } catch ( DBError $e ) {
76 $status->fatal( 'lockmanager-fail-db-release', $lockDb );
77 }
78 }
79
80 return $status;
81 }
82 }