3 use Wikimedia\TestingAccessWrapper;
21 use MediaWikiCoversValidator;
22 use PHPUnit4And6Compat;
34 'pool' =>
'testcache-hash',
38 $wanCache = TestingAccessWrapper::newFromObject( $this->
cache );
40 $this->internalCache = $wanCache->cache;
56 $this->
cache->get( $key, $curTTL, [], $asOf );
57 $this->assertNull( $curTTL,
"Current TTL is null" );
58 $this->assertNull( $asOf,
"Current as-of-time is infinite" );
60 $t = microtime(
true );
63 $this->assertEquals(
$value, $this->
cache->get( $key, $curTTL, [], $asOf ) );
64 if ( is_infinite( $ttl ) || $ttl == 0 ) {
65 $this->assertTrue( is_infinite( $curTTL ),
"Current TTL is infinite" );
67 $this->assertGreaterThan( 0, $curTTL,
"Current TTL > 0" );
68 $this->assertLessThanOrEqual( $ttl, $curTTL,
"Current TTL < nominal TTL" );
70 $this->assertGreaterThanOrEqual(
$t - 1, $asOf,
"As-of-time in range of set() time" );
71 $this->assertLessThanOrEqual(
$t + 1, $asOf,
"As-of-time in range of set() time" );
97 $this->assertFalse(
$value,
"Non-existing key has false value" );
98 $this->assertNull( $curTTL,
"Non-existing key has null current TTL" );
106 for ( $i = 0; $i < 3; ++$i ) {
110 $this->assertEquals( $this->
cache->get( $key ),
$value );
120 $this->
cache->set( $key,
$value, 3, [
'since' => microtime(
true ) - 30 ] );
122 $this->assertFalse( $this->
cache->get( $key ),
"Stale set() value ignored" );
127 $callback =
function ()
use ( &$hit ) {
132 $groups = [
'thiscache:1',
'thatcache:1',
'somecache:1' ];
134 foreach (
$keys as $i => $key ) {
135 $this->
cache->getWithSetCallback(
136 $key, 100, $callback, [
'pcTTL' => 5,
'pcGroup' => $groups[$i] ] );
138 $this->assertEquals( 3, $hit );
140 foreach (
$keys as $i => $key ) {
141 $this->
cache->getWithSetCallback(
142 $key, 100, $callback, [
'pcTTL' => 5,
'pcGroup' => $groups[$i] ] );
144 $this->assertEquals( 3, $hit,
"Values cached" );
146 foreach (
$keys as $i => $key ) {
147 $this->
cache->getWithSetCallback(
148 "$key-2", 100, $callback, [
'pcTTL' => 5,
'pcGroup' => $groups[$i] ] );
150 $this->assertEquals( 6, $hit );
152 foreach (
$keys as $i => $key ) {
153 $this->
cache->getWithSetCallback(
154 "$key-2", 100, $callback, [
'pcTTL' => 5,
'pcGroup' => $groups[$i] ] );
156 $this->assertEquals( 6, $hit,
"New values cached" );
158 foreach (
$keys as $i => $key ) {
159 $this->
cache->delete( $key );
160 $this->
cache->getWithSetCallback(
161 $key, 100, $callback, [
'pcTTL' => 5,
'pcGroup' => $groups[$i] ] );
163 $this->assertEquals( 9, $hit,
"Values evicted" );
165 $key = reset(
$keys );
167 $this->
cache->getWithSetCallback( $key, 100, $callback, [
'pcTTL' => 5 ] );
168 $this->assertEquals( 10, $hit,
"Value calculated" );
169 $this->
cache->getWithSetCallback( $key, 100, $callback, [
'pcTTL' => 5 ] );
170 $this->assertEquals( 10, $hit,
"Value cached" );
171 $outerCallback =
function ()
use ( &$callback, $key ) {
172 $v = $this->
cache->getWithSetCallback( $key, 100, $callback, [
'pcTTL' => 5 ] );
177 $this->
cache->getWithSetCallback(
"$key-miss-outer", 100, $outerCallback );
178 $this->assertEquals( 11, $hit,
"Nested callback value process cache skipped" );
199 $func =
function ( $old, &$ttl, &$opts, $asOf )
200 use ( &$wasSet, &$priorValue, &$priorAsOf,
$value )
211 $this->assertEquals(
$value, $v,
"Value returned" );
212 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
213 $this->assertFalse( $priorValue,
"No prior value" );
214 $this->assertNull( $priorAsOf,
"No prior value" );
218 $this->assertLessThanOrEqual( 20, $curTTL,
'Current TTL between 19-20 (overriden)' );
219 $this->assertGreaterThanOrEqual( 19, $curTTL,
'Current TTL between 19-20 (overriden)' );
223 $key, 30, $func, [
'lowTTL' => 0,
'lockTSE' => 5 ] + $extOpts );
224 $this->assertEquals(
$value, $v,
"Value returned" );
225 $this->assertEquals( 0, $wasSet,
"Value not regenerated" );
227 $mockWallClock = microtime(
true );
228 $priorTime = $mockWallClock;
235 $key, 30, $func, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
237 $this->assertEquals(
$value, $v,
"Value returned" );
238 $this->assertEquals( 1, $wasSet,
"Value regenerated due to check keys" );
239 $this->assertEquals(
$value, $priorValue,
"Has prior value" );
240 $this->assertInternalType(
'float', $priorAsOf,
"Has prior value" );
242 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check keys generated on miss' );
244 $this->assertGreaterThanOrEqual( $priorTime, $t2,
'Check keys generated on miss' );
246 $mockWallClock += 0.01;
247 $priorTime = $mockWallClock;
250 $key, 30, $func, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
252 $this->assertEquals(
$value, $v,
"Value returned" );
253 $this->assertEquals( 1, $wasSet,
"Value regenerated due to still-recent check keys" );
255 $this->assertLessThanOrEqual( $priorTime, $t1,
'Check keys did not change again' );
257 $this->assertLessThanOrEqual( $priorTime, $t2,
'Check keys did not change again' );
260 $v =
$cache->
get( $key, $curTTL, [ $cKey1, $cKey2 ] );
262 $this->assertEquals(
$value, $v[$cache::VFLD_DATA],
"Value returned" );
264 $this->assertEquals(
$value, $v,
"Value returned" );
266 $this->assertLessThanOrEqual( 0, $curTTL,
"Value has current TTL < 0 due to check keys" );
271 $this->assertEquals(
$value, $v,
"Value returned" );
274 $this->assertEquals(
$value, $v,
"Value still returned after deleted" );
275 $this->assertEquals( 1, $wasSet,
"Value process cached while deleted" );
277 $oldValReceived = -1;
278 $oldAsOfReceived = -1;
279 $checkFunc =
function ( $oldVal, &$ttl,
array $setOpts, $oldAsOf )
280 use ( &$oldValReceived, &$oldAsOfReceived, &$wasSet ) {
282 $oldValReceived = $oldVal;
283 $oldAsOfReceived = $oldAsOf;
285 return 'xxx' . $wasSet;
288 $mockWallClock = microtime(
true );
289 $priorTime = $mockWallClock;
294 $key, 30, $checkFunc, [
'staleTTL' => 50 ] + $extOpts );
295 $this->assertEquals(
'xxx1', $v,
"Value returned" );
296 $this->assertEquals(
false, $oldValReceived,
"Callback got no stale value" );
297 $this->assertEquals(
null, $oldAsOfReceived,
"Callback got no stale value" );
299 $mockWallClock += 40;
301 $key, 30, $checkFunc, [
'staleTTL' => 50 ] + $extOpts );
302 $this->assertEquals(
'xxx2', $v,
"Value still returned after expired" );
303 $this->assertEquals( 2, $wasSet,
"Value recalculated while expired" );
304 $this->assertEquals(
'xxx1', $oldValReceived,
"Callback got stale value" );
305 $this->assertNotEquals(
null, $oldAsOfReceived,
"Callback got stale value" );
307 $mockWallClock += 260;
309 $key, 30, $checkFunc, [
'staleTTL' => 50 ] + $extOpts );
310 $this->assertEquals(
'xxx3', $v,
"Value still returned after expired" );
311 $this->assertEquals( 3, $wasSet,
"Value recalculated while expired" );
312 $this->assertEquals(
false, $oldValReceived,
"Callback got no stale value" );
313 $this->assertEquals(
null, $oldAsOfReceived,
"Callback got no stale value" );
315 $mockWallClock = ( $priorTime - $cache::HOLDOFF_TTL - 1 );
320 $mockWallClock = $priorTime;
323 $cache::TTL_INDEFINITE,
325 [
'graceTTL' => $cache::TTL_WEEK,
'checkKeys' => [ $checkKey ] ] + $extOpts
327 $this->assertEquals(
'xxx1', $v,
"Value returned" );
328 $this->assertEquals( 1, $wasSet,
"Value computed" );
329 $this->assertEquals(
false, $oldValReceived,
"Callback got no stale value" );
330 $this->assertEquals(
null, $oldAsOfReceived,
"Callback got no stale value" );
332 $mockWallClock += $cache::TTL_HOUR;
335 $cache::TTL_INDEFINITE,
337 [
'graceTTL' => $cache::TTL_WEEK,
'checkKeys' => [ $checkKey ] ] + $extOpts
339 $this->assertEquals(
'xxx1', $v,
"Cached value returned" );
340 $this->assertEquals( 1, $wasSet,
"Cached value returned" );
343 $mockWallClock += 0.01;
347 $cache::TTL_INDEFINITE,
349 [
'graceTTL' => $cache::TTL_WEEK,
'checkKeys' => [ $checkKey ] ] + $extOpts
351 $this->assertEquals(
'xxx1', $v,
"Value still returned after expired (in grace)" );
352 $this->assertEquals( 1, $wasSet,
"Value still returned after expired (in grace)" );
355 $mockWallClock += $cache::TTL_WEEK;
358 $cache::TTL_INDEFINITE,
360 [
'graceTTL' => $cache::TTL_WEEK,
'checkKeys' => [ $checkKey ] ] + $extOpts
362 $this->assertEquals(
'xxx2', $v,
"Value was recomputed (past grace)" );
363 $this->assertEquals( 2, $wasSet,
"Value was recomputed (past grace)" );
364 $this->assertEquals(
'xxx1', $oldValReceived,
"Callback got post-grace stale value" );
365 $this->assertNotEquals(
null, $oldAsOfReceived,
"Callback got post-grace stale value" );
371 [ [
'version' => 1 ],
true ]
378 $func =
function ( $old, &$ttl, &$opts, $asOf )
use ( &$wasSet, &
$value )
391 $opts = [
'lowTTL' => 30 ];
393 $this->assertEquals(
$value, $v,
"Value returned" );
394 $this->assertEquals( 1, $wasSet,
"Value calculated" );
396 $this->assertEquals( 2, $wasSet,
"Value re-calculated" );
400 $opts = [
'lowTTL' => 1 ];
402 $this->assertEquals(
$value, $v,
"Value returned" );
403 $this->assertEquals( 1, $wasSet,
"Value calculated" );
405 $this->assertEquals( 1, $wasSet,
"Value cached" );
408 $asyncHandler =
function ( $callback )
use ( &$asycList ) {
409 $asycList[] = $callback;
414 'asyncHandler' => $asyncHandler
417 $mockWallClock = microtime(
true );
418 $priorTime = $mockWallClock;
423 $opts = [
'lowTTL' => 100 ];
425 $this->assertEquals(
$value, $v,
"Value returned" );
426 $this->assertEquals( 1, $wasSet,
"Value calculated" );
428 $this->assertEquals( 1, $wasSet,
"Cached value used" );
429 $this->assertEquals( $v,
$value,
"Value cached" );
431 $mockWallClock += 250;
433 $this->assertEquals(
$value, $v,
"Value returned" );
434 $this->assertEquals( 1, $wasSet,
"Stale value used" );
435 $this->assertEquals( 1,
count( $asycList ),
"Refresh deferred." );
439 $this->assertEquals( 2, $wasSet,
"Value calculated at later time" );
440 $this->assertEquals( 0,
count( $asycList ),
"No deferred refreshes added." );
442 $this->assertEquals(
$value, $v,
"New value stored" );
449 $mockWallClock = $priorTime;
454 $opts = [
'hotTTR' => 900 ];
456 $this->assertEquals(
$value, $v,
"Value returned" );
457 $this->assertEquals( 1, $wasSet,
"Value calculated" );
459 $mockWallClock += 30;
462 $this->assertEquals( 1, $wasSet,
"Value cached" );
464 $mockWallClock = $priorTime;
467 $opts = [
'hotTTR' => 10 ];
469 $this->assertEquals(
$value, $v,
"Value returned" );
470 $this->assertEquals( 1, $wasSet,
"Value calculated" );
472 $mockWallClock += 30;
475 $this->assertEquals(
$value, $v,
"Value returned" );
476 $this->assertEquals( 2, $wasSet,
"Value re-calculated" );
485 $this->
cache->getWithSetCallback(
'key', 30,
'invalid callback' );
508 $genFunc =
function ( $id, $old, &$ttl, &$opts, $asOf )
use (
509 &$wasSet, &$priorValue, &$priorAsOf
519 $keyedIds =
new ArrayIterator( [ $keyA => 3353 ] );
522 $keyedIds, 30, $genFunc, [
'lockTSE' => 5 ] + $extOpts );
523 $this->assertEquals(
$value, $v[$keyA],
"Value returned" );
524 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
525 $this->assertFalse( $priorValue,
"No prior value" );
526 $this->assertNull( $priorAsOf,
"No prior value" );
530 $this->assertLessThanOrEqual( 20, $curTTL,
'Current TTL between 19-20 (overriden)' );
531 $this->assertGreaterThanOrEqual( 19, $curTTL,
'Current TTL between 19-20 (overriden)' );
535 $keyedIds =
new ArrayIterator( [ $keyB =>
'efef' ] );
537 $keyedIds, 30, $genFunc, [
'lowTTL' => 0,
'lockTSE' => 5, ] + $extOpts );
538 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
539 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
542 $keyedIds, 30, $genFunc, [
'lowTTL' => 0,
'lockTSE' => 5, ] + $extOpts );
543 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
544 $this->assertEquals( 1, $wasSet,
"Value not regenerated" );
547 $mockWallClock = microtime(
true );
548 $priorTime = $mockWallClock;
554 $keyedIds =
new ArrayIterator( [ $keyB =>
'efef' ] );
556 $keyedIds, 30, $genFunc, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
558 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
559 $this->assertEquals( 1, $wasSet,
"Value regenerated due to check keys" );
560 $this->assertEquals(
$value, $priorValue,
"Has prior value" );
561 $this->assertInternalType(
'float', $priorAsOf,
"Has prior value" );
563 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check keys generated on miss' );
565 $this->assertGreaterThanOrEqual( $priorTime, $t2,
'Check keys generated on miss' );
567 $mockWallClock += 0.01;
568 $priorTime = $mockWallClock;
571 $keyedIds =
new ArrayIterator( [ $keyC => 43636 ] );
573 $keyedIds, 30, $genFunc, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
575 $this->assertEquals(
$value, $v[$keyC],
"Value returned" );
576 $this->assertEquals( 1, $wasSet,
"Value regenerated due to still-recent check keys" );
578 $this->assertLessThanOrEqual( $priorTime, $t1,
'Check keys did not change again' );
580 $this->assertLessThanOrEqual( $priorTime, $t2,
'Check keys did not change again' );
583 $v =
$cache->
get( $keyC, $curTTL, [ $cKey1, $cKey2 ] );
585 $this->assertEquals(
$value, $v[$cache::VFLD_DATA],
"Value returned" );
587 $this->assertEquals(
$value, $v,
"Value returned" );
589 $this->assertLessThanOrEqual( 0, $curTTL,
"Value has current TTL < 0 due to check keys" );
593 $keyedIds =
new ArrayIterator( [ $key => 242424 ] );
595 $keyedIds, 30, $genFunc, [
'pcTTL' => 5 ] + $extOpts );
596 $this->assertEquals(
"@{$keyedIds[$key]}$", $v[$key],
"Value returned" );
598 $keyedIds =
new ArrayIterator( [ $key => 242424 ] );
600 $keyedIds, 30, $genFunc, [
'pcTTL' => 5 ] + $extOpts );
601 $this->assertEquals(
"@{$keyedIds[$key]}$", $v[$key],
"Value still returned after deleted" );
602 $this->assertEquals( 1, $wasSet,
"Value process cached while deleted" );
605 $ids = [ 1, 2, 3, 4, 5, 6 ];
607 return $wanCache->makeKey(
'test', $id );
610 $genFunc =
function ( $id, $oldValue, &$ttl,
array &$setops )
use ( &$calls ) {
618 [
"val-1",
"val-2",
"val-3",
"val-4",
"val-5",
"val-6" ],
619 array_values( $values ),
620 "Correct values in correct order"
623 array_map( $keyFunc, $ids, array_fill( 0,
count( $ids ), $this->
cache ) ),
624 array_keys( $values ),
625 "Correct keys in correct order"
627 $this->assertEquals(
count( $ids ), $calls );
630 $this->assertEquals(
count( $ids ), $calls,
"Values cached" );
634 ->setMethods( [
'getMulti' ] )->getMock();
635 $localBag->expects( $this->exactly( 1 ) )->method(
'getMulti' )->willReturn( [
639 $wanCache =
new WANObjectCache( [
'cache' => $localBag,
'pool' =>
'testcache-hash' ] );
642 $keyedIds =
new ArrayIterator( [
'k1' =>
'id1',
'k2' =>
'id2' ] );
644 [
'k1' =>
'val-id1',
'k2' =>
'val-id2' ],
645 $wanCache->getMultiWithSetCallback( $keyedIds, 10, $genFunc, [
'pcTTL' => 5 ] )
649 [
'k1' =>
'val-id1',
'k2' =>
'val-id2' ],
650 $wanCache->getMultiWithSetCallback( $keyedIds, 10, $genFunc, [
'pcTTL' => 5 ] )
657 [ [
'version' => 1 ],
true ]
679 &$wasSet, &$priorValue, &$priorAsOf
682 foreach ( $ids
as $id ) {
684 $newValues[$id] =
"@$id$";
692 $keyedIds =
new ArrayIterator( [ $keyA => 3353 ] );
695 $keyedIds, 30, $genFunc, $extOpts );
696 $this->assertEquals(
$value, $v[$keyA],
"Value returned" );
697 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
701 $this->assertLessThanOrEqual( 20, $curTTL,
'Current TTL between 19-20 (overriden)' );
702 $this->assertGreaterThanOrEqual( 19, $curTTL,
'Current TTL between 19-20 (overriden)' );
706 $keyedIds =
new ArrayIterator( [ $keyB =>
'efef' ] );
708 $keyedIds, 30, $genFunc, [
'lowTTL' => 0 ] + $extOpts );
709 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
710 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
713 $keyedIds, 30, $genFunc, [
'lowTTL' => 0 ] + $extOpts );
714 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
715 $this->assertEquals( 1, $wasSet,
"Value not regenerated" );
718 $mockWallClock = microtime(
true );
719 $priorTime = $mockWallClock;
725 $keyedIds =
new ArrayIterator( [ $keyB =>
'efef' ] );
727 $keyedIds, 30, $genFunc, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
729 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
730 $this->assertEquals( 1, $wasSet,
"Value regenerated due to check keys" );
732 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check keys generated on miss' );
734 $this->assertGreaterThanOrEqual( $priorTime, $t2,
'Check keys generated on miss' );
736 $mockWallClock += 0.01;
737 $priorTime = $mockWallClock;
740 $keyedIds =
new ArrayIterator( [ $keyC => 43636 ] );
742 $keyedIds, 30, $genFunc, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
744 $this->assertEquals(
$value, $v[$keyC],
"Value returned" );
745 $this->assertEquals( 1, $wasSet,
"Value regenerated due to still-recent check keys" );
747 $this->assertLessThanOrEqual( $priorTime, $t1,
'Check keys did not change again' );
749 $this->assertLessThanOrEqual( $priorTime, $t2,
'Check keys did not change again' );
752 $v =
$cache->
get( $keyC, $curTTL, [ $cKey1, $cKey2 ] );
754 $this->assertEquals(
$value, $v[$cache::VFLD_DATA],
"Value returned" );
756 $this->assertEquals(
$value, $v,
"Value returned" );
758 $this->assertLessThanOrEqual( 0, $curTTL,
"Value has current TTL < 0 due to check keys" );
762 $keyedIds =
new ArrayIterator( [ $key => 242424 ] );
764 $keyedIds, 30, $genFunc, [
'pcTTL' => 5 ] + $extOpts );
765 $this->assertEquals(
"@{$keyedIds[$key]}$", $v[$key],
"Value returned" );
767 $keyedIds =
new ArrayIterator( [ $key => 242424 ] );
769 $keyedIds, 30, $genFunc, [
'pcTTL' => 5 ] + $extOpts );
770 $this->assertEquals(
"@{$keyedIds[$key]}$", $v[$key],
"Value still returned after deleted" );
771 $this->assertEquals( 1, $wasSet,
"Value process cached while deleted" );
774 $ids = [ 1, 2, 3, 4, 5, 6 ];
776 return $wanCache->makeKey(
'test', $id );
781 foreach ( $ids
as $id ) {
783 $newValues[$id] =
"val-{$id}";
791 [
"val-1",
"val-2",
"val-3",
"val-4",
"val-5",
"val-6" ],
792 array_values( $values ),
793 "Correct values in correct order"
796 array_map( $keyFunc, $ids, array_fill( 0,
count( $ids ), $this->
cache ) ),
797 array_keys( $values ),
798 "Correct keys in correct order"
800 $this->assertEquals(
count( $ids ), $calls );
803 $this->assertEquals(
count( $ids ), $calls,
"Values cached" );
809 [ [
'version' => 1 ],
true ]
830 $this->assertEquals( 1, $calls,
'Value was populated' );
833 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
837 [
'lockTSE' => 5,
'checkKeys' => $checkKeys ] );
838 $this->assertEquals(
$value,
$ret,
'Old value used' );
839 $this->assertEquals( 1, $calls,
'Callback was not used' );
843 [
'lockTSE' => 5,
'checkKeys' => $checkKeys ] );
844 $this->assertEquals(
$value,
$ret,
'Callback was used; interim saved' );
845 $this->assertEquals( 2, $calls,
'Callback was used; interim saved' );
848 [
'lockTSE' => 5,
'checkKeys' => $checkKeys ] );
849 $this->assertEquals(
$value,
$ret,
'Callback was not used; used interim (mutex failed)' );
850 $this->assertEquals( 2, $calls,
'Callback was not used; used interim (mutex failed)' );
864 $func =
function ( $oldValue, &$ttl, &$setOpts )
use ( &$calls,
$value,
$cache, $key ) {
866 $setOpts[
'since'] = microtime(
true ) - 10;
876 $this->assertEquals(
$value,
$cache->
get( $key, $curTTL ),
'Value was populated' );
877 $this->assertLessThan( 0, $curTTL,
'Value has negative curTTL' );
878 $this->assertEquals( 1, $calls,
'Value was generated' );
881 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
884 $this->assertEquals( 1, $calls,
'Callback was not used' );
907 $this->assertEquals( 1, $calls,
'Value was populated' );
910 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
914 [
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
915 $this->assertEquals(
$value,
$ret,
'Callback used' );
916 $this->assertEquals( 2, $calls,
'Callback used' );
919 [
'lockTSE' => 30,
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
920 $this->assertEquals(
$value,
$ret,
'Old value used' );
921 $this->assertEquals( 2, $calls,
'Callback was not used' );
925 [
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
926 $this->assertEquals( $busyValue,
$ret,
'Callback was not used; used busy value' );
927 $this->assertEquals( 2, $calls,
'Callback was not used; used busy value' );
929 $this->internalCache->delete( $cache::MUTEX_KEY_PREFIX . $key );
931 [
'lockTSE' => 30,
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
932 $this->assertEquals(
$value,
$ret,
'Callback was used; saved interim' );
933 $this->assertEquals( 3, $calls,
'Callback was used; saved interim' );
935 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
937 [
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
938 $this->assertEquals(
$value,
$ret,
'Callback was not used; used interim' );
939 $this->assertEquals( 3, $calls,
'Callback was not used; used interim' );
948 $value1 = [
'this' =>
'is',
'a' =>
'test' ];
949 $value2 = [
'this' =>
'is',
'another' =>
'test' ];
960 [ $key1 => $value1, $key2 => $value2 ],
962 'Result array populated'
965 $this->assertEquals( 2,
count( $curTTLs ),
"Two current TTLs in array" );
966 $this->assertGreaterThan( 0, $curTTLs[$key1],
"Key 1 has current TTL > 0" );
967 $this->assertGreaterThan( 0, $curTTLs[$key2],
"Key 2 has current TTL > 0" );
972 $mockWallClock = microtime(
true );
973 $priorTime = $mockWallClock;
980 [ $key1 => $value1, $key2 => $value2 ],
981 $cache->
getMulti( [ $key1, $key2, $key3 ], $curTTLs, [ $cKey1, $cKey2 ] ),
982 "Result array populated even with new check keys"
985 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check key 1 generated on miss' );
987 $this->assertGreaterThanOrEqual( $priorTime, $t2,
'Check key 2 generated on miss' );
988 $this->assertEquals( 2,
count( $curTTLs ),
"Current TTLs array set" );
989 $this->assertLessThanOrEqual( 0, $curTTLs[$key1],
'Key 1 has current TTL <= 0' );
990 $this->assertLessThanOrEqual( 0, $curTTLs[$key2],
'Key 2 has current TTL <= 0' );
996 [ $key1 => $value1, $key2 => $value2 ],
997 $cache->
getMulti( [ $key1, $key2, $key3 ], $curTTLs, [ $cKey1, $cKey2 ] ),
998 "Result array still populated even with new check keys"
1000 $this->assertEquals( 2,
count( $curTTLs ),
"Current TTLs still array set" );
1001 $this->assertLessThan( 0, $curTTLs[$key1],
'Key 1 has negative current TTL' );
1002 $this->assertLessThan( 0, $curTTLs[$key2],
'Key 2 has negative current TTL' );
1019 $mockWallClock = microtime(
true );
1024 foreach ( [ $checkAll, $check1, $check2 ]
as $checkKey ) {
1028 $mockWallClock += 0.100;
1040 $this->assertEquals(
1041 [
'key1' => $value1,
'key2' => $value2 ],
1045 $this->assertGreaterThanOrEqual( 9.5, $curTTLs[
'key1'],
'Initial ttls' );
1046 $this->assertLessThanOrEqual( 10.5, $curTTLs[
'key1'],
'Initial ttls' );
1047 $this->assertGreaterThanOrEqual( 9.5, $curTTLs[
'key2'],
'Initial ttls' );
1048 $this->assertLessThanOrEqual( 10.5, $curTTLs[
'key2'],
'Initial ttls' );
1050 $mockWallClock += 0.100;
1060 $this->assertEquals(
1061 [
'key1' => $value1,
'key2' => $value2 ],
1063 'key1 expired by check1, but value still provided'
1065 $this->assertLessThan( 0, $curTTLs[
'key1'],
'key1 TTL expired' );
1066 $this->assertGreaterThan( 0, $curTTLs[
'key2'],
'key2 still valid' );
1077 $this->assertEquals(
1078 [
'key1' => $value1,
'key2' => $value2 ],
1080 'All keys expired by checkAll, but value still provided'
1082 $this->assertLessThan( 0, $curTTLs[
'key1'],
'key1 expired by checkAll' );
1083 $this->assertLessThan( 0, $curTTLs[
'key2'],
'key2 expired by checkAll' );
1093 for ( $i = 0; $i < 500; ++$i ) {
1097 $cache->
get( $key, $curTTL, [ $checkKey ] );
1100 $v =
$cache->
get( $key, $curTTL, [ $checkKey ] );
1102 $this->assertEquals(
'val', $v );
1103 $this->assertLessThan( 0, $curTTL,
"Step $i: CTL < 0 (miss/set/hit)" );
1106 for ( $i = 0; $i < 500; ++$i ) {
1112 $v =
$cache->
get( $key, $curTTL, [ $checkKey ] );
1114 $this->assertEquals(
'val', $v );
1115 $this->assertLessThan( 0, $curTTL,
"Step $i: CTL < 0 (set/hit)" );
1130 $v = $this->
cache->get( $key, $curTTL );
1131 $this->assertEquals(
$value, $v,
"Key was created with value" );
1132 $this->assertGreaterThan( 0, $curTTL,
"Existing key has current TTL > 0" );
1134 $this->
cache->delete( $key );
1137 $v = $this->
cache->get( $key, $curTTL );
1138 $this->assertFalse( $v,
"Deleted key has false value" );
1139 $this->assertLessThan( 0, $curTTL,
"Deleted key has current TTL < 0" );
1142 $v = $this->
cache->get( $key, $curTTL );
1143 $this->assertFalse( $v,
"Deleted key is tombstoned and has false value" );
1144 $this->assertLessThan( 0, $curTTL,
"Deleted key is tombstoned and has current TTL < 0" );
1150 $v = $this->
cache->get( $key, $curTTL );
1151 $this->assertFalse( $v,
"Deleted key has false value" );
1152 $this->assertNull( $curTTL,
"Deleted key has null current TTL" );
1155 $v = $this->
cache->get( $key, $curTTL );
1156 $this->assertEquals(
$value, $v,
"Key was created with value" );
1157 $this->assertGreaterThan( 0, $curTTL,
"Existing key has current TTL > 0" );
1175 $funcV1 =
function ()
use ( &$wasSet, $valueV1 ) {
1181 $priorValue =
false;
1183 $funcV2 =
function ( $oldValue, &$ttl, $setOpts, $oldAsOf )
1184 use ( &$wasSet, $valueV2, &$priorValue, &$priorAsOf ) {
1185 $priorValue = $oldValue;
1186 $priorAsOf = $oldAsOf;
1195 $this->assertEquals( $valueV1, $v,
"Value returned" );
1196 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
1198 $this->assertEquals( 1, $wasSet,
"Value not regenerated" );
1199 $this->assertEquals( $valueV1, $v,
"Value not regenerated" );
1203 $verOpts = [
'version' => $extOpts[
'version'] + 1 ];
1206 $verOpts = [
'version' => 1 ];
1212 $this->assertEquals( $valueV2, $v,
"Value returned" );
1213 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
1214 $this->assertEquals(
false, $priorValue,
"Old value not given due to old format" );
1215 $this->assertEquals(
null, $priorAsOf,
"Old value not given due to old format" );
1219 $this->assertEquals( $valueV2, $v,
"Value not regenerated (secondary key)" );
1220 $this->assertEquals( 0, $wasSet,
"Value not regenerated (secondary key)" );
1228 $this->assertEquals( $valueV2, $v,
"Value returned" );
1229 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
1232 $this->assertEquals( $valueV2, $v,
"Value not regenerated (main key)" );
1233 $this->assertEquals( 1, $wasSet,
"Value not regenerated (main key)" );
1239 [ [
'version' => 1 ],
true ]
1252 $func =
function ()
use ( &$wasCalled,
$value ) {
1263 $this->assertEquals( 1, $wasCalled,
'Value cached' );
1266 $this->assertEquals( 2, $wasCalled,
'Value regenerated (got mutex)' );
1268 $this->assertEquals( 3, $wasCalled,
'Value regenerated (got mutex)' );
1270 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
1272 $this->assertEquals( 3, $wasCalled,
'Value interim cached (failed mutex)' );
1273 $this->internalCache->delete( $cache::MUTEX_KEY_PREFIX . $key );
1281 $this->assertEquals( 1, $wasCalled,
'Value cached' );
1284 $this->assertEquals( 2, $wasCalled,
'Value regenerated (got mutex)' );
1286 $this->assertEquals( 3, $wasCalled,
'Value still regenerated (got mutex)' );
1288 $this->assertEquals( 4, $wasCalled,
'Value still regenerated (got mutex)' );
1290 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
1292 $this->assertEquals( 5, $wasCalled,
'Value still regenerated (failed mutex)' );
1307 $mockWallClock = microtime(
true );
1308 $priorTime = $mockWallClock;
1311 $mockWallClock += 0.100;
1313 $this->assertGreaterThanOrEqual( $priorTime, $t0,
'Check key auto-created' );
1315 $priorTime = $mockWallClock;
1316 $mockWallClock += 0.100;
1319 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check key created' );
1322 $this->assertEquals( $t1, $t2,
'Check key time did not change' );
1324 $mockWallClock += 0.100;
1327 $this->assertGreaterThan( $t2, $t3,
'Check key time increased' );
1330 $this->assertEquals( $t3, $t4,
'Check key time did not change' );
1332 $mockWallClock += 0.100;
1335 $this->assertGreaterThan( $t4, $t5,
'Check key time increased' );
1338 $this->assertEquals( $t5, $t6,
'Check key time did not change' );
1351 $this->internalCache->set(
1355 $this->internalCache->set(
1359 $this->internalCache->set(
1366 $v = $this->
cache->get( $key, $curTTL, [ $tKey1, $tKey2 ] );
1367 $this->assertEquals(
$value, $v,
"Value matches" );
1368 $this->assertLessThan( -4.9, $curTTL,
"Correct CTL" );
1369 $this->assertGreaterThan( -5.1, $curTTL,
"Correct CTL" );
1383 $knownPurge = time() - 60;
1384 $goodTime = microtime(
true ) - 5;
1385 $badTime = microtime(
true ) - 300;
1387 $this->internalCache->set(
1396 $this->internalCache->set(
1405 $this->internalCache->set(
1409 $this->internalCache->set(
1414 $this->assertEquals(
$value, $this->
cache->get( $vKey1 ) );
1415 $this->assertEquals(
$value, $this->
cache->get( $vKey2 ) );
1416 $this->
cache->reap( $vKey1, $knownPurge, $bad1 );
1417 $this->
cache->reap( $vKey2, $knownPurge, $bad2 );
1419 $this->assertFalse( $bad1 );
1420 $this->assertTrue( $bad2 );
1422 $this->
cache->reapCheckKey( $tKey1, $knownPurge, $tBad1 );
1423 $this->
cache->reapCheckKey( $tKey2, $knownPurge, $tBad2 );
1424 $this->assertFalse( $tBad1 );
1425 $this->assertTrue( $tBad2 );
1433 ->setMethods( [
'get',
'changeTTL' ] )->getMock();
1434 $backend->expects( $this->once() )->method(
'get' )
1441 $backend->expects( $this->once() )->method(
'changeTTL' )
1442 ->willReturn(
false );
1445 'cache' => $backend,
1446 'pool' =>
'testcache-hash',
1451 $ret = $wanCache->reap(
'key', 360, $isStale );
1452 $this->assertTrue( $isStale,
'value was stale' );
1453 $this->assertFalse(
$ret,
'changeTTL failed' );
1463 $opts = [
'lag' => 300,
'since' => microtime(
true ) ];
1465 $this->assertEquals(
$value, $this->
cache->get( $key ),
"Rep-lagged value written." );
1468 $opts = [
'lag' => 0,
'since' => microtime(
true ) - 300 ];
1470 $this->assertEquals(
false, $this->
cache->get( $key ),
"Trx-lagged value not written." );
1473 $opts = [
'lag' => 5,
'since' => microtime(
true ) - 5 ];
1475 $this->assertEquals(
false, $this->
cache->get( $key ),
"Lagged value not written." );
1485 $opts = [
'pending' =>
true ];
1487 $this->assertEquals(
false, $this->
cache->get( $key ),
"Pending value not written." );
1492 ->setMethods( [
'set',
'delete' ] )->getMock();
1493 $localBag->expects( $this->never() )->method(
'set' );
1494 $localBag->expects( $this->never() )->method(
'delete' );
1496 'cache' => $localBag,
1497 'pool' =>
'testcache-hash',
1499 'mcrouterAware' =>
true,
1500 'region' =>
'pmtpa',
1501 'cluster' =>
'mw-wan'
1503 $valFunc =
function () {
1508 $wanCache->get(
'x' );
1509 $wanCache->get(
'x', $ctl, [
'check1' ] );
1510 $wanCache->getMulti( [
'x',
'y' ] );
1511 $wanCache->getMulti( [
'x',
'y' ], $ctls, [
'check2' ] );
1512 $wanCache->getWithSetCallback(
'p', 30, $valFunc );
1513 $wanCache->getCheckKeyTime(
'zzz' );
1514 $wanCache->reap(
'x', time() - 300 );
1515 $wanCache->reap(
'zzz', time() - 300 );
1520 ->setMethods( [
'set' ] )->getMock();
1522 'cache' => $localBag,
1523 'pool' =>
'testcache-hash',
1525 'mcrouterAware' =>
true,
1526 'region' =>
'pmtpa',
1527 'cluster' =>
'mw-wan'
1530 $localBag->expects( $this->once() )->method(
'set' )
1531 ->with(
"/*/mw-wan/" . $wanCache::VALUE_KEY_PREFIX .
"test" );
1533 $wanCache->delete(
'test' );
1538 ->setMethods( [
'set' ] )->getMock();
1540 'cache' => $localBag,
1541 'pool' =>
'testcache-hash',
1543 'mcrouterAware' =>
true,
1544 'region' =>
'pmtpa',
1545 'cluster' =>
'mw-wan'
1548 $localBag->expects( $this->once() )->method(
'set' )
1549 ->with(
"/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX .
"test" );
1551 $wanCache->touchCheckKey(
'test' );
1556 ->setMethods( [
'delete' ] )->getMock();
1558 'cache' => $localBag,
1559 'pool' =>
'testcache-hash',
1561 'mcrouterAware' =>
true,
1562 'region' =>
'pmtpa',
1563 'cluster' =>
'mw-wan'
1566 $localBag->expects( $this->once() )->method(
'delete' )
1567 ->with(
"/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX .
"test" );
1569 $wanCache->resetCheckKey(
'test' );
1577 $now = microtime(
true );
1580 $cache->
set( $key,
'Do what thou Wilt' );
1585 $this->assertEquals(
'Do what thou Wilt',
$cache->
get( $key ) );
1590 'pool' =>
'testcache-hash',
1591 'epoch' => $now - 3600
1595 $this->assertEquals(
'Do what thou Wilt',
$cache->
get( $key ) );
1601 'pool' =>
'testcache-hash',
1602 'epoch' => $now + 3600
1606 $this->assertFalse(
$cache->
get( $key ),
'Key rejected due to epoch' );
1620 $mtime = $ago ? time() - $ago : $ago;
1622 $ttl = $this->
cache->adaptiveTTL( $mtime, $maxTTL, $minTTL, $factor );
1624 $this->assertGreaterThanOrEqual( $adaptiveTTL - $margin, $ttl );
1625 $this->assertLessThanOrEqual( $adaptiveTTL + $margin, $ttl );
1627 $ttl = $this->
cache->adaptiveTTL( (
string)$mtime, $maxTTL, $minTTL, $factor );
1629 $this->assertGreaterThanOrEqual( $adaptiveTTL - $margin, $ttl );
1630 $this->assertLessThanOrEqual( $adaptiveTTL + $margin, $ttl );
1635 [ 3600, 900, 30, 0.2, 720 ],
1636 [ 3600, 500, 30, 0.2, 500 ],
1637 [ 3600, 86400, 800, 0.2, 800 ],
1638 [
false, 86400, 800, 0.2, 800 ],
1639 [
null, 86400, 800, 0.2, 800 ]
1648 $this->assertInstanceOf(
1658 $this->assertSame(
null, $this->
cache->setLogger(
new Psr\Log\NullLogger ) );
1666 ->setMethods( [
'getQoS' ] )->getMock();
1667 $backend->expects( $this->once() )->method(
'getQoS' )
1672 $wanCache::QOS_UNKNOWN,
1673 $wanCache->getQoS( $wanCache::ATTR_EMULATION )
1682 ->setMethods( [
'makeKey' ] )->getMock();
1683 $backend->expects( $this->once() )->method(
'makeKey' )
1684 ->willReturn(
'special' );
1687 'cache' => $backend,
1688 'pool' =>
'testcache-hash',
1692 $this->assertSame(
'special', $wanCache->makeKey(
'a',
'b' ) );
1700 ->setMethods( [
'makeGlobalKey' ] )->getMock();
1701 $backend->expects( $this->once() )->method(
'makeGlobalKey' )
1702 ->willReturn(
'special' );
1705 'cache' => $backend,
1706 'pool' =>
'testcache-hash',
1710 $this->assertSame(
'special', $wanCache->makeGlobalKey(
'a',
'b' ) );
1715 [
'domain:page:5',
'page' ],
1716 [
'domain:main-key',
'main-key' ],
1717 [
'domain:page:history',
'page' ],
1718 [
'missingdomainkey',
'missingdomainkey' ]
1727 $wanCache = TestingAccessWrapper::newFromObject(
new WANObjectCache( [
1729 'pool' =>
'testcache-hash',
1733 $this->assertEquals( $class, $wanCache->determineKeyClass( $key ) );
1741 return ( $curTTL > 0 && ( $curTTL + self::CLOCK_SKEW ) < $lowTTL );
1747 return ( ( $now - $asOf ) > $timeTillRefresh );