Avoid 'message' in log context in AuthManager
[lhc/web/wiklou.git] / includes / filebackend / lockmanager / MySqlLockManager.php
1 <?php
2 /**
3 * MySQL version of DBLockManager that supports shared locks.
4 *
5 * All lock servers must have the innodb table defined in locking/filelocks.sql.
6 * All locks are non-blocking, which avoids deadlocks.
7 *
8 * @ingroup LockManager
9 */
10 class MySqlLockManager extends DBLockManager {
11 /** @var array Mapping of lock types to the type actually used */
12 protected $lockTypeMap = [
13 self::LOCK_SH => self::LOCK_SH,
14 self::LOCK_UW => self::LOCK_SH,
15 self::LOCK_EX => self::LOCK_EX
16 ];
17
18 protected function getLocalLB() {
19 // Use a separate connection so releaseAllLocks() doesn't rollback the main trx
20 return wfGetLBFactory()->newMainLB( $this->domain );
21 }
22
23 protected function initConnection( $lockDb, IDatabase $db ) {
24 # Let this transaction see lock rows from other transactions
25 $db->query( "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;" );
26 # Do everything in a transaction as it all gets rolled back eventually
27 $db->startAtomic( __CLASS__ );
28 }
29
30 /**
31 * Get a connection to a lock DB and acquire locks on $paths.
32 * This does not use GET_LOCK() per http://bugs.mysql.com/bug.php?id=1118.
33 *
34 * @see DBLockManager::getLocksOnServer()
35 * @param string $lockSrv
36 * @param array $paths
37 * @param string $type
38 * @return StatusValue
39 */
40 protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
41 $status = StatusValue::newGood();
42
43 $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
44
45 $keys = []; // list of hash keys for the paths
46 $data = []; // list of rows to insert
47 $checkEXKeys = []; // list of hash keys that this has no EX lock on
48 # Build up values for INSERT clause
49 foreach ( $paths as $path ) {
50 $key = $this->sha1Base36Absolute( $path );
51 $keys[] = $key;
52 $data[] = [ 'fls_key' => $key, 'fls_session' => $this->session ];
53 if ( !isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
54 $checkEXKeys[] = $key;
55 }
56 }
57
58 # Block new writers (both EX and SH locks leave entries here)...
59 $db->insert( 'filelocks_shared', $data, __METHOD__, [ 'IGNORE' ] );
60 # Actually do the locking queries...
61 if ( $type == self::LOCK_SH ) { // reader locks
62 $blocked = false;
63 # Bail if there are any existing writers...
64 if ( count( $checkEXKeys ) ) {
65 $blocked = $db->selectField( 'filelocks_exclusive', '1',
66 [ 'fle_key' => $checkEXKeys ],
67 __METHOD__
68 );
69 }
70 # Other prospective writers that haven't yet updated filelocks_exclusive
71 # will recheck filelocks_shared after doing so and bail due to this entry.
72 } else { // writer locks
73 $encSession = $db->addQuotes( $this->session );
74 # Bail if there are any existing writers...
75 # This may detect readers, but the safe check for them is below.
76 # Note: if two writers come at the same time, both bail :)
77 $blocked = $db->selectField( 'filelocks_shared', '1',
78 [ 'fls_key' => $keys, "fls_session != $encSession" ],
79 __METHOD__
80 );
81 if ( !$blocked ) {
82 # Build up values for INSERT clause
83 $data = [];
84 foreach ( $keys as $key ) {
85 $data[] = [ 'fle_key' => $key ];
86 }
87 # Block new readers/writers...
88 $db->insert( 'filelocks_exclusive', $data, __METHOD__ );
89 # Bail if there are any existing readers...
90 $blocked = $db->selectField( 'filelocks_shared', '1',
91 [ 'fls_key' => $keys, "fls_session != $encSession" ],
92 __METHOD__
93 );
94 }
95 }
96
97 if ( $blocked ) {
98 foreach ( $paths as $path ) {
99 $status->fatal( 'lockmanager-fail-acquirelock', $path );
100 }
101 }
102
103 return $status;
104 }
105
106 /**
107 * @see QuorumLockManager::releaseAllLocks()
108 * @return StatusValue
109 */
110 protected function releaseAllLocks() {
111 $status = StatusValue::newGood();
112
113 foreach ( $this->conns as $lockDb => $db ) {
114 if ( $db->trxLevel() ) { // in transaction
115 try {
116 $db->rollback( __METHOD__ ); // finish transaction and kill any rows
117 } catch ( DBError $e ) {
118 $status->fatal( 'lockmanager-fail-db-release', $lockDb );
119 }
120 }
121 }
122
123 return $status;
124 }
125 }