Index: branches/concurrency/tests/phpunit/includes/ConcurrencyCheckTest.php |
— | — | @@ -25,12 +25,20 @@ |
26 | 26 | array() |
27 | 27 | ), |
28 | 28 | ); |
| 29 | + |
| 30 | + // turn on memcached for this test. |
| 31 | + // if no memcached is present, this still works fine. |
| 32 | + global $wgMainCacheType; |
| 33 | + $this->oldcache = $wgMainCacheType; |
| 34 | + $wgMainCacheType = CACHE_MEMCACHED; |
29 | 35 | } |
30 | 36 | |
31 | 37 | public function tearDown() { |
32 | | - parent::tearDown(); |
| 38 | + // turn off caching again. |
| 39 | + global $wgMainCacheType; |
| 40 | + $wgMainCacheType = $this->oldcache; |
33 | 41 | |
34 | | - // perhaps clean up all the cruft left in the db |
| 42 | + parent::tearDown(); |
35 | 43 | } |
36 | 44 | |
37 | 45 | // Actual tests from here down |
— | — | @@ -46,6 +54,7 @@ |
47 | 55 | |
48 | 56 | // tests |
49 | 57 | $this->assertTrue( $first->checkout($testKey), "Initial checkout" ); |
| 58 | + $this->assertTrue( $first->checkout($testKey), "Cache hit" ); |
50 | 59 | $this->assertFalse( $second->checkout($testKey), "Checkout of locked resource fails as different user" ); |
51 | 60 | $this->assertTrue( $first->checkout($testKey), "Checkout of locked resource succeeds as original user" ); |
52 | 61 | $this->assertFalse( $second->checkin($testKey), "Checkin of locked resource fails as different user" ); |
Index: branches/concurrency/includes/ConcurrencyCheck.php |
— | — | @@ -18,9 +18,13 @@ |
19 | 19 | * @author Ian Baker <ian@wikimedia.org> |
20 | 20 | */ |
21 | 21 | class ConcurrencyCheck { |
22 | | - |
23 | | - |
24 | | - // TODO: docblock |
| 22 | + /** |
| 23 | + * Constructor |
| 24 | + * |
| 25 | + * @var $resourceType String The calling application or type of resource, conceptually like a namespace |
| 26 | + * @var $user User object, the current user |
| 27 | + * @var $expirationTime Integer (optional) How long should a checkout last, in seconds |
| 28 | + */ |
25 | 29 | public function __construct( $resourceType, $user, $expirationTime = null ) { |
26 | 30 | |
27 | 31 | // All database calls are to the master, since the whole point of this class is maintaining |
— | — | @@ -48,16 +52,17 @@ |
49 | 53 | $cacheKey = wfMemcKey( $this->resourceType, $record ); |
50 | 54 | |
51 | 55 | // when operating with a single memcached cluster, it's reasonable to check the cache here. |
52 | | - // TODO: in a muti-datacenter environment with discrete memcache instances, this has to be turned off. make a config var? |
53 | | - $cached = $memc->get( $cacheKey ); |
54 | | - |
55 | | - if( $cached ) { |
56 | | - if( ! $override && $cached['userId'] != $userId && $cached['expiration'] > time() ) { |
57 | | - // this is already checked out. |
58 | | - return false; |
| 56 | + global $wgConcurrencyTrustMemc; |
| 57 | + if( $wgConcurrencyTrustMemc ) { |
| 58 | + $cached = $memc->get( $cacheKey ); |
| 59 | + if( $cached ) { |
| 60 | + if( ! $override && $cached['userId'] != $userId && $cached['expiration'] > time() ) { |
| 61 | + // this is already checked out. |
| 62 | + return false; |
| 63 | + } |
59 | 64 | } |
60 | 65 | } |
61 | | - |
| 66 | + |
62 | 67 | // attempt an insert, check success (this is atomic) |
63 | 68 | $insertError = null; |
64 | 69 | $res = $dbw->insert( |
— | — | @@ -334,13 +339,20 @@ |
335 | 340 | } |
336 | 341 | |
337 | 342 | public function setExpirationTime ( $expirationTime = null ) { |
| 343 | + global $wgConcurrencyExpirationDefault, $wgConcurrencyExpirationMax, $wgConcurrencyExpirationMin; |
| 344 | + |
338 | 345 | // check to make sure the time is digits only, so it can be used in queries |
339 | 346 | // negative number are allowed, though mostly only used for testing |
340 | | - // TODO: define maximum and minimum times in configuration, to prevent DoS |
341 | 347 | if( $expirationTime && preg_match('/^[\d-]+$/', $expirationTime) ) { |
342 | | - $this->expirationTime = $expirationTime; // the amount of time before a checkout expires. |
| 348 | + if( $expirationTime > $wgConcurrencyExpirationMax ) { |
| 349 | + $this->expirationTime = $wgConcurrencyExpirationMax; // if the number is too high, limit it to the max value. |
| 350 | + } elseif ( $expirationTime < $wgConcurrencyExpirationMin ) { |
| 351 | + $this->expirationTime = $wgConcurrencyExpirationMin; // low limit, default -1 min |
| 352 | + } else { |
| 353 | + $this->expirationTime = $expirationTime; // the amount of time before a checkout expires. |
| 354 | + } |
343 | 355 | } else { |
344 | | - $this->expirationTime = 60 * 15; // 15 mins. TODO: make a configurable default for this. |
| 356 | + $this->expirationTime = $wgConcurrencyExpirationDefault; // global default is 15 mins. |
345 | 357 | } |
346 | 358 | } |
347 | 359 | |
Index: branches/concurrency/includes/DefaultSettings.php |
— | — | @@ -5732,6 +5732,15 @@ |
5733 | 5733 | $wgDBtestpassword = ''; |
5734 | 5734 | |
5735 | 5735 | /** |
| 5736 | + * ConcurrencyCheck keeps track of which web resources are in use, for producing higher-quality UI |
| 5737 | + */ |
| 5738 | +$wgConcurrencyExpirationDefault = 60 * 15; // Default checkout duration. 15 minutes. |
| 5739 | +$wgConcurrencyExpirationMax = 60 * 30; // Maximum possible checkout duration. 30 minutes. |
| 5740 | +$wgConcurrencyExpirationMin = 60 * -1; // Minimum possible checkout duration. Negative is possible (but barely) for testing. |
| 5741 | +$wgConcurrencyTrustMemc = true; // If running in an environment with multiple discrete caches, set to false. |
| 5742 | + |
| 5743 | + |
| 5744 | +/** |
5736 | 5745 | * For really cool vim folding this needs to be at the end: |
5737 | 5746 | * vim: foldmarker=@{,@} foldmethod=marker |
5738 | 5747 | * @} |