Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/infrastructure/util/__tests__/PhabricatorGlobalLockTestCase.php
12242 views
1
<?php
2
3
final class PhabricatorGlobalLockTestCase
4
extends PhabricatorTestCase {
5
6
protected function getPhabricatorTestCaseConfiguration() {
7
return array(
8
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
9
);
10
}
11
12
public function testConnectionPoolWithDefaultConnection() {
13
PhabricatorGlobalLock::clearConnectionPool();
14
15
$this->assertEqual(
16
0,
17
PhabricatorGlobalLock::getConnectionPoolSize(),
18
pht('Clear Connection Pool'));
19
20
$lock_name = $this->newLockName();
21
$lock = PhabricatorGlobalLock::newLock($lock_name);
22
$lock->lock();
23
24
$this->assertEqual(
25
0,
26
PhabricatorGlobalLock::getConnectionPoolSize(),
27
pht('Connection Pool With Lock'));
28
29
$lock->unlock();
30
31
$this->assertEqual(
32
1,
33
PhabricatorGlobalLock::getConnectionPoolSize(),
34
pht('Connection Pool With Lock Released'));
35
36
PhabricatorGlobalLock::clearConnectionPool();
37
}
38
39
public function testConnectionPoolWithSpecificConnection() {
40
$conn = PhabricatorGlobalLock::newConnection();
41
42
PhabricatorGlobalLock::clearConnectionPool();
43
44
$this->assertEqual(
45
0,
46
PhabricatorGlobalLock::getConnectionPoolSize(),
47
pht('Clear Connection Pool'));
48
49
$this->assertEqual(
50
false,
51
$conn->isHoldingAnyLock(),
52
pht('Specific Connection, No Lock'));
53
54
$lock_name = $this->newLockName();
55
$lock = PhabricatorGlobalLock::newLock($lock_name);
56
$lock->setExternalConnection($conn);
57
$lock->lock();
58
59
$this->assertEqual(
60
0,
61
PhabricatorGlobalLock::getConnectionPoolSize(),
62
pht('Connection Pool + Specific, With Lock'));
63
64
$this->assertEqual(
65
true,
66
$conn->isHoldingAnyLock(),
67
pht('Specific Connection, Holding Lock'));
68
69
$lock->unlock();
70
71
// The specific connection provided should NOT be returned to the
72
// connection pool.
73
74
$this->assertEqual(
75
0,
76
PhabricatorGlobalLock::getConnectionPoolSize(),
77
pht('Connection Pool + Specific, With Lock Released'));
78
79
$this->assertEqual(
80
false,
81
$conn->isHoldingAnyLock(),
82
pht('Specific Connection, No Lock'));
83
84
PhabricatorGlobalLock::clearConnectionPool();
85
}
86
87
public function testExternalConnectionMutationScope() {
88
$conn = PhabricatorGlobalLock::newConnection();
89
90
$lock_name = $this->newLockName();
91
$lock = PhabricatorGlobalLock::newLock($lock_name);
92
$lock->lock();
93
94
$caught = null;
95
try {
96
$lock->setExternalConnection($conn);
97
} catch (Exception $ex) {
98
$caught = $ex;
99
} catch (Throwable $ex) {
100
$caught = $ex;
101
}
102
103
$lock->unlock();
104
105
$this->assertTrue(
106
($caught instanceof Exception),
107
pht('Changing connection while locked is forbidden.'));
108
}
109
110
public function testMultipleLocks() {
111
$conn = PhabricatorGlobalLock::newConnection();
112
113
PhabricatorGlobalLock::clearConnectionPool();
114
115
$lock_name_a = $this->newLockName();
116
$lock_name_b = $this->newLockName();
117
118
$lock_a = PhabricatorGlobalLock::newLock($lock_name_a);
119
$lock_a->setExternalConnection($conn);
120
121
$lock_b = PhabricatorGlobalLock::newLock($lock_name_b);
122
$lock_b->setExternalConnection($conn);
123
124
$lock_a->lock();
125
126
$caught = null;
127
try {
128
$lock_b->lock();
129
} catch (Exception $ex) {
130
$caught = $ex;
131
} catch (Throwable $ex) {
132
$caught = $ex;
133
}
134
135
// See T13627. The lock infrastructure must forbid this because it does
136
// not work in versions of MySQL older than 5.7.
137
138
$this->assertTrue(
139
($caught instanceof Exception),
140
pht('Expect multiple locks on the same connection to fail.'));
141
}
142
143
public function testPoolReleaseOnFailure() {
144
$conn = PhabricatorGlobalLock::newConnection();
145
$lock_name = $this->newLockName();
146
147
PhabricatorGlobalLock::clearConnectionPool();
148
149
$this->assertEqual(
150
0,
151
PhabricatorGlobalLock::getConnectionPoolSize(),
152
pht('Clear Connection Pool'));
153
154
$lock = PhabricatorGlobalLock::newLock($lock_name);
155
156
// NOTE: We're cheating here, since there's a global registry of locks
157
// for the process that we have to bypass. In the real world, this lock
158
// would have to be held by some external process. To simplify this
159
// test case, just use a raw "GET_LOCK()" call to hold the lock.
160
161
$raw_conn = PhabricatorGlobalLock::newConnection();
162
$raw_name = $lock->getName();
163
164
$row = queryfx_one(
165
$raw_conn,
166
'SELECT GET_LOCK(%s, %f)',
167
$raw_name,
168
0);
169
$this->assertTrue((bool)head($row), pht('Establish Raw Lock'));
170
171
$this->assertEqual(
172
0,
173
PhabricatorGlobalLock::getConnectionPoolSize(),
174
pht('Connection Pool with Held Lock'));
175
176
// We expect this sequence to establish a new connection, fail to acquire
177
// the lock, then put the connection in the connection pool. After the
178
// first cycle, the connection should be reused.
179
180
for ($ii = 0; $ii < 3; $ii++) {
181
$this->tryHeldLock($lock_name);
182
$this->assertEqual(
183
1,
184
PhabricatorGlobalLock::getConnectionPoolSize(),
185
pht('Connection Pool After Lock Failure'));
186
}
187
188
PhabricatorGlobalLock::clearConnectionPool();
189
190
// Now, do the same thing with an external connection. This connection
191
// should not be put into the pool! See T13627.
192
193
for ($ii = 0; $ii < 3; $ii++) {
194
$this->tryHeldLock($lock_name, $conn);
195
$this->assertEqual(
196
0,
197
PhabricatorGlobalLock::getConnectionPoolSize(),
198
pht('Connection Pool After External Lock Failure'));
199
}
200
}
201
202
private function newLockName() {
203
return 'testlock-'.Filesystem::readRandomCharacters(16);
204
}
205
206
private function tryHeldLock(
207
$lock_name,
208
AphrontDatabaseConnection $conn = null) {
209
210
$lock = PhabricatorGlobalLock::newLock($lock_name);
211
212
if ($conn) {
213
$lock->setExternalConnection($conn);
214
}
215
216
$caught = null;
217
try {
218
$lock->lock(0);
219
} catch (PhutilLockException $ex) {
220
$caught = $ex;
221
}
222
223
$this->assertTrue($caught instanceof PhutilLockException);
224
}
225
226
227
}
228
229