Merge "mediawiki.page.ready: Avoid duplicate DOM query on logout click"
[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 * @phan-file-suppress PhanUndeclaredConstant Phan doesn't read constants in LockManager
11 * when accessed via self::
12 */
13 class PostgreSqlLockManager extends DBLockManager {
14 /** @var array Mapping of lock types to the type actually used */
15 protected $lockTypeMap = [
16 self::LOCK_SH => self::LOCK_SH,
17 self::LOCK_UW => self::LOCK_SH,
18 self::LOCK_EX => self::LOCK_EX
19 ];
20
21 protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
22 $status = StatusValue::newGood();
23 if ( $paths === [] ) {
24 return $status; // nothing to lock
25 }
26
27 $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
28 $bigints = array_unique( array_map(
29 function ( $key ) {
30 return Wikimedia\base_convert( substr( $key, 0, 15 ), 16, 10 );
31 },
32 array_map( [ $this, 'sha1Base16Absolute' ], $paths )
33 ) );
34
35 // Try to acquire all the locks...
36 $fields = [];
37 foreach ( $bigints as $bigint ) {
38 $fields[] = ( $type == self::LOCK_SH )
39 ? "pg_try_advisory_lock_shared({$db->addQuotes( $bigint )}) AS K$bigint"
40 : "pg_try_advisory_lock({$db->addQuotes( $bigint )}) AS K$bigint";
41 }
42 $res = $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
43 $row = $res->fetchRow();
44
45 if ( in_array( 'f', $row ) ) {
46 // Release any acquired locks if some could not be acquired...
47 $fields = [];
48 foreach ( $row as $kbigint => $ok ) {
49 if ( $ok === 't' ) { // locked
50 $bigint = substr( $kbigint, 1 ); // strip off the "K"
51 $fields[] = ( $type == self::LOCK_SH )
52 ? "pg_advisory_unlock_shared({$db->addQuotes( $bigint )})"
53 : "pg_advisory_unlock({$db->addQuotes( $bigint )})";
54 }
55 }
56 if ( count( $fields ) ) {
57 $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
58 }
59 foreach ( $paths as $path ) {
60 $status->fatal( 'lockmanager-fail-acquirelock', $path );
61 }
62 }
63
64 return $status;
65 }
66
67 /**
68 * @see QuorumLockManager::releaseAllLocks()
69 * @return StatusValue
70 */
71 protected function releaseAllLocks() {
72 $status = StatusValue::newGood();
73
74 foreach ( $this->conns as $lockDb => $db ) {
75 try {
76 $db->query( "SELECT pg_advisory_unlock_all()", __METHOD__ );
77 } catch ( DBError $e ) {
78 $status->fatal( 'lockmanager-fail-db-release', $lockDb );
79 }
80 }
81
82 return $status;
83 }
84 }