Index: trunk/phase3/tests/phpunit/includes/ConcurrencyCheckTest.php |
— | — | @@ -5,9 +5,9 @@ |
6 | 6 | * @var Array of test users |
7 | 7 | */ |
8 | 8 | public static $users; |
9 | | - |
| 9 | + |
10 | 10 | // Prepare test environment |
11 | | - |
| 11 | + |
12 | 12 | public function setUp() { |
13 | 13 | parent::setUp(); |
14 | 14 | |
— | — | @@ -25,22 +25,22 @@ |
26 | 26 | array() |
27 | 27 | ), |
28 | 28 | ); |
29 | | - |
| 29 | + |
30 | 30 | // turn on memcached for this test. |
31 | 31 | // if no memcached is present, this still works fine. |
32 | 32 | global $wgMainCacheType; |
33 | 33 | $this->oldcache = $wgMainCacheType; |
34 | 34 | $wgMainCacheType = CACHE_MEMCACHED; |
35 | 35 | } |
36 | | - |
| 36 | + |
37 | 37 | public function tearDown() { |
38 | 38 | // turn off caching again. |
39 | 39 | global $wgMainCacheType; |
40 | 40 | $wgMainCacheType = $this->oldcache; |
41 | | - |
42 | | - parent::tearDown(); |
| 41 | + |
| 42 | + parent::tearDown(); |
43 | 43 | } |
44 | | - |
| 44 | + |
45 | 45 | // Actual tests from here down |
46 | 46 | |
47 | 47 | public function testCheckoutCheckin() { |
— | — | @@ -51,7 +51,7 @@ |
52 | 52 | // clean up after any previously failed tests |
53 | 53 | $first->checkin($testKey); |
54 | 54 | $second->checkin($testKey); |
55 | | - |
| 55 | + |
56 | 56 | // tests |
57 | 57 | $this->assertTrue( $first->checkout($testKey), "Initial checkout" ); |
58 | 58 | $this->assertTrue( $first->checkout($testKey), "Cache hit" ); |
— | — | @@ -77,7 +77,7 @@ |
78 | 78 | $cc->checkout( 1339 ); |
79 | 79 | $cc->setExpirationTime(); |
80 | 80 | $cc->checkout( 13310 ); |
81 | | - |
| 81 | + |
82 | 82 | // tests |
83 | 83 | $this->assertEquals( 2, $cc->expire(), "Resource expiration" ); |
84 | 84 | $this->assertTrue( $cc->checkin( 13310 ), "Checkin succeeds after expiration" ); |
— | — | @@ -99,4 +99,4 @@ |
100 | 100 | $this->assertEquals( 'invalid', $output[1339]['status'], "Expired checkouts are invalid"); |
101 | 101 | $this->assertEquals( 'invalid', $output[13310]['status'], "Missing checkouts are invalid"); |
102 | 102 | } |
103 | | -} |
\ No newline at end of file |
| 103 | +} |
Index: trunk/phase3/tests/phpunit/includes/api/ApiConcurrencyTest.php |
— | — | @@ -106,7 +106,6 @@ |
107 | 107 | |
108 | 108 | } |
109 | 109 | |
110 | | - |
111 | 110 | /** |
112 | 111 | * @depends testLogin |
113 | 112 | */ |
— | — | @@ -168,4 +167,4 @@ |
169 | 168 | |
170 | 169 | } |
171 | 170 | |
172 | | -} |
\ No newline at end of file |
| 171 | +} |
Index: trunk/phase3/includes/api/ApiConcurrency.php |
— | — | @@ -4,7 +4,6 @@ |
5 | 5 | * API module that handles cooperative locking of web resources |
6 | 6 | */ |
7 | 7 | class ApiConcurrency extends ApiBase { |
8 | | - |
9 | 8 | public function __construct( $main, $action ) { |
10 | 9 | parent::__construct( $main, $action ); |
11 | 10 | } |
— | — | @@ -24,11 +23,10 @@ |
25 | 24 | case 'checkout': |
26 | 25 | case 'checkin': |
27 | 26 | if ( $concurrencyCheck->$params['ccaction']( $params['record'] ) ) { |
28 | | - $res['result'] = 'success'; |
| 27 | + $res['result'] = 'success'; |
| 28 | + } else { |
| 29 | + $res['result'] = 'failure'; |
29 | 30 | } |
30 | | - else { |
31 | | - $res['result'] = 'failure'; |
32 | | - } |
33 | 31 | break; |
34 | 32 | |
35 | 33 | default: |
— | — | @@ -92,7 +90,7 @@ |
93 | 91 | } |
94 | 92 | |
95 | 93 | public function getVersion() { |
96 | | - return __CLASS__ . ': $Id: ApiConcurrency.php $'; |
| 94 | + return __CLASS__ . ': $Id$'; |
97 | 95 | } |
98 | 96 | |
99 | 97 | private function checkPermission( $user ) { |
— | — | @@ -104,4 +102,4 @@ |
105 | 103 | } |
106 | 104 | } |
107 | 105 | |
108 | | -} |
\ No newline at end of file |
| 106 | +} |
Property changes on: trunk/phase3/includes/api/ApiConcurrency.php |
___________________________________________________________________ |
Added: svn:keywords |
109 | 107 | + Id |
Index: trunk/phase3/includes/ConcurrencyCheck.php |
— | — | @@ -18,7 +18,15 @@ |
19 | 19 | * @author Ian Baker <ian@wikimedia.org> |
20 | 20 | */ |
21 | 21 | class ConcurrencyCheck { |
| 22 | + |
| 23 | + protected $expirationTime; |
| 24 | + |
22 | 25 | /** |
| 26 | + * @var User |
| 27 | + */ |
| 28 | + protected $user; |
| 29 | + |
| 30 | + /** |
23 | 31 | * Constructor |
24 | 32 | * |
25 | 33 | * @var $resourceType String The calling application or type of resource, conceptually like a namespace |
— | — | @@ -75,7 +83,7 @@ |
76 | 84 | 'cc_expiration' => time() + $this->expirationTime, |
77 | 85 | ), |
78 | 86 | __METHOD__, |
79 | | - array('IGNORE') |
| 87 | + array( 'IGNORE' ) |
80 | 88 | ); |
81 | 89 | |
82 | 90 | // if the insert succeeded, checkout is done. |
— | — | @@ -143,7 +151,7 @@ |
144 | 152 | $dbw = $this->dbw; |
145 | 153 | $userId = $this->user->getId(); |
146 | 154 | $cacheKey = wfMemcKey( $this->resourceType, $record ); |
147 | | - |
| 155 | + |
148 | 156 | $dbw->delete( |
149 | 157 | 'concurrencycheck', |
150 | 158 | array( |
— | — | @@ -154,14 +162,14 @@ |
155 | 163 | __METHOD__, |
156 | 164 | array() |
157 | 165 | ); |
158 | | - |
| 166 | + |
159 | 167 | // check row count (this is atomic, select would not be) |
160 | 168 | if( $dbw->affectedRows() === 1 ) { |
161 | 169 | $memc->delete( $cacheKey ); |
162 | 170 | return true; |
163 | 171 | } |
164 | | - |
165 | | - return false; |
| 172 | + |
| 173 | + return false; |
166 | 174 | } |
167 | 175 | |
168 | 176 | /** |
— | — | @@ -171,9 +179,9 @@ |
172 | 180 | */ |
173 | 181 | public function expire() { |
174 | 182 | $memc = wfGetMainCache(); |
175 | | - $dbw = $this->dbw; |
| 183 | + $dbw = $this->dbw; |
176 | 184 | $now = time(); |
177 | | - |
| 185 | + |
178 | 186 | // get the rows to remove from cache. |
179 | 187 | $res = $dbw->select( |
180 | 188 | 'concurrencycheck', |
— | — | @@ -184,13 +192,13 @@ |
185 | 193 | __METHOD__, |
186 | 194 | array() |
187 | 195 | ); |
188 | | - |
| 196 | + |
189 | 197 | // build a list of rows to delete. |
190 | 198 | $toExpire = array(); |
191 | 199 | while( $res && $record = $res->fetchRow() ) { |
192 | 200 | $toExpire[] = $record['cc_record']; |
193 | 201 | } |
194 | | - |
| 202 | + |
195 | 203 | // remove the rows from the db |
196 | 204 | $dbw->delete( |
197 | 205 | 'concurrencycheck', |
— | — | @@ -200,7 +208,7 @@ |
201 | 209 | __METHOD__, |
202 | 210 | array() |
203 | 211 | ); |
204 | | - |
| 212 | + |
205 | 213 | // delete all those rows from cache |
206 | 214 | // outside a transaction because deletes don't require atomicity. |
207 | 215 | foreach( $toExpire as $expire ) { |
— | — | @@ -210,7 +218,7 @@ |
211 | 219 | // return the number of rows removed. |
212 | 220 | return $dbw->affectedRows(); |
213 | 221 | } |
214 | | - |
| 222 | + |
215 | 223 | public function status( $keys ) { |
216 | 224 | $memc = wfGetMainCache(); |
217 | 225 | $dbw = $this->dbw; |
— | — | @@ -218,11 +226,11 @@ |
219 | 227 | |
220 | 228 | $checkouts = array(); |
221 | 229 | $toSelect = array(); |
222 | | - |
| 230 | + |
223 | 231 | // validate keys, attempt to retrieve from cache. |
224 | 232 | foreach( $keys as $key ) { |
225 | 233 | $this->validateId( $key ); |
226 | | - |
| 234 | + |
227 | 235 | $cached = $memc->get( wfMemcKey( $this->resourceType, $key ) ); |
228 | 236 | if( $cached && $cached['expiration'] > $now ) { |
229 | 237 | $checkouts[$key] = array( |
— | — | @@ -256,11 +264,11 @@ |
257 | 265 | __METHOD__, |
258 | 266 | array() |
259 | 267 | ); |
260 | | - |
| 268 | + |
261 | 269 | while( $res && $record = $res->fetchRow() ) { |
262 | 270 | $record['status'] = 'valid'; |
263 | 271 | $checkouts[ $record['cc_record'] ] = $record; |
264 | | - |
| 272 | + |
265 | 273 | // safe to store values since this is inside the transaction |
266 | 274 | $memc->set( |
267 | 275 | wfMemcKey( $this->resourceType, $record['cc_record'] ), |
— | — | @@ -272,7 +280,7 @@ |
273 | 281 | // end the transaction. |
274 | 282 | $dbw->rollback(); |
275 | 283 | } |
276 | | - |
| 284 | + |
277 | 285 | // if a key was passed in but has no (unexpired) checkout, include it in the |
278 | 286 | // result set to make things easier and more consistent on the client-side. |
279 | 287 | foreach( $keys as $key ) { |
— | — | @@ -280,19 +288,22 @@ |
281 | 289 | $checkouts[$key]['status'] = 'invalid'; |
282 | 290 | } |
283 | 291 | } |
284 | | - |
| 292 | + |
285 | 293 | return $checkouts; |
286 | 294 | } |
287 | | - |
| 295 | + |
288 | 296 | public function listCheckouts() { |
289 | 297 | // TODO: fill in the function that lets you get the complete set of checkouts for a given application. |
290 | 298 | } |
291 | | - |
292 | | - public function setUser ( $user ) { |
| 299 | + |
| 300 | + /** |
| 301 | + * @param $user user |
| 302 | + */ |
| 303 | + public function setUser( $user ) { |
293 | 304 | $this->user = $user; |
294 | 305 | } |
295 | | - |
296 | | - public function setExpirationTime ( $expirationTime = null ) { |
| 306 | + |
| 307 | + public function setExpirationTime( $expirationTime = null ) { |
297 | 308 | global $wgConcurrencyExpirationDefault, $wgConcurrencyExpirationMax, $wgConcurrencyExpirationMin; |
298 | 309 | |
299 | 310 | // check to make sure the time is digits only, so it can be used in queries |
— | — | @@ -321,10 +332,10 @@ |
322 | 333 | if( ! preg_match('/^\d+$/', $record) ) { |
323 | 334 | throw new ConcurrencyCheckBadRecordIdException( 'Record ID ' . $record . ' must be a positive integer' ); |
324 | 335 | } |
325 | | - |
| 336 | + |
326 | 337 | // TODO: add a hook here for client-side validation. |
327 | 338 | return true; |
328 | 339 | } |
329 | 340 | } |
330 | 341 | |
331 | | -class ConcurrencyCheckBadRecordIdException extends MWException {}; |
| 342 | +class ConcurrencyCheckBadRecordIdException extends MWException {} |