3 use Wikimedia\TestingAccessWrapper;
21 use MediaWikiCoversValidator;
22 use PHPUnit4And6Compat;
36 $wanCache = TestingAccessWrapper::newFromObject( $this->
cache );
38 $this->internalCache = $wanCache->cache;
54 $this->
cache->get( $key, $curTTL, [], $asOf );
55 $this->assertNull( $curTTL,
"Current TTL is null" );
56 $this->assertNull( $asOf,
"Current as-of-time is infinite" );
58 $t = microtime(
true );
61 $this->assertEquals(
$value, $this->
cache->get( $key, $curTTL, [], $asOf ) );
62 if ( is_infinite( $ttl ) || $ttl == 0 ) {
63 $this->assertTrue( is_infinite( $curTTL ),
"Current TTL is infinite" );
65 $this->assertGreaterThan( 0, $curTTL,
"Current TTL > 0" );
66 $this->assertLessThanOrEqual( $ttl, $curTTL,
"Current TTL < nominal TTL" );
68 $this->assertGreaterThanOrEqual(
$t - 1, $asOf,
"As-of-time in range of set() time" );
69 $this->assertLessThanOrEqual(
$t + 1, $asOf,
"As-of-time in range of set() time" );
95 $this->assertFalse(
$value,
"Non-existing key has false value" );
96 $this->assertNull( $curTTL,
"Non-existing key has null current TTL" );
104 for ( $i = 0; $i < 3; ++$i ) {
108 $this->assertEquals( $this->
cache->get( $key ),
$value );
118 $this->
cache->set( $key,
$value, 3, [
'since' => microtime(
true ) - 30 ] );
120 $this->assertFalse( $this->
cache->get( $key ),
"Stale set() value ignored" );
124 $mockWallClock = 1549343530.2053;
125 $this->
cache->setMockTime( $mockWallClock );
128 $callback =
function ()
use ( &$hit ) {
133 $groups = [
'thiscache:1',
'thatcache:1',
'somecache:1' ];
135 foreach (
$keys as $i => $key ) {
136 $this->
cache->getWithSetCallback(
137 $key, 100, $callback, [
'pcTTL' => 5,
'pcGroup' => $groups[$i] ] );
139 $this->assertEquals( 3, $hit );
141 foreach (
$keys as $i => $key ) {
142 $this->
cache->getWithSetCallback(
143 $key, 100, $callback, [
'pcTTL' => 5,
'pcGroup' => $groups[$i] ] );
145 $this->assertEquals( 3, $hit,
"Values cached" );
147 foreach (
$keys as $i => $key ) {
148 $this->
cache->getWithSetCallback(
149 "$key-2", 100, $callback, [
'pcTTL' => 5,
'pcGroup' => $groups[$i] ] );
151 $this->assertEquals( 6, $hit );
153 foreach (
$keys as $i => $key ) {
154 $this->
cache->getWithSetCallback(
155 "$key-2", 100, $callback, [
'pcTTL' => 5,
'pcGroup' => $groups[$i] ] );
157 $this->assertEquals( 6, $hit,
"New values cached" );
159 foreach (
$keys as $i => $key ) {
161 $this->
cache->delete( $key );
162 $mockWallClock += 0.001;
164 $this->
cache->getWithSetCallback(
165 $key, 100, $callback, [
'pcTTL' => 5,
'pcGroup' => $groups[$i] ] );
167 $this->assertEquals( 9, $hit,
"Values evicted by delete()" );
170 $key = reset(
$keys );
171 $this->
cache->getWithSetCallback( $key, 100, $callback, [
'pcTTL' => 5 ] );
172 $this->assertEquals( 9, $hit,
"Value recently interim-cached" );
174 $mockWallClock += 0.2;
175 $this->
cache->clearProcessCache();
176 $this->
cache->getWithSetCallback( $key, 100, $callback, [
'pcTTL' => 5 ] );
177 $this->assertEquals( 10, $hit,
"Value calculated (interim key not recent and reset)" );
178 $this->
cache->getWithSetCallback( $key, 100, $callback, [
'pcTTL' => 5 ] );
179 $this->assertEquals( 10, $hit,
"Value process cached" );
181 $mockWallClock += 0.2;
182 $outerCallback =
function ()
use ( &$callback, $key ) {
183 $v = $this->
cache->getWithSetCallback( $key, 100, $callback, [
'pcTTL' => 5 ] );
188 $this->
cache->getWithSetCallback(
"$key-miss-outer", 100, $outerCallback );
189 $this->assertEquals( 11, $hit,
"Nested callback value process cache skipped" );
210 $func =
function ( $old, &$ttl, &$opts, $asOf )
211 use ( &$wasSet, &$priorValue, &$priorAsOf,
$value ) {
219 $mockWallClock = 1549343530.2053;
220 $priorTime = $mockWallClock;
225 $this->assertEquals(
$value, $v,
"Value returned" );
226 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
227 $this->assertFalse( $priorValue,
"No prior value" );
228 $this->assertNull( $priorAsOf,
"No prior value" );
232 $this->assertLessThanOrEqual( 20, $curTTL,
'Current TTL between 19-20 (overriden)' );
233 $this->assertGreaterThanOrEqual( 19, $curTTL,
'Current TTL between 19-20 (overriden)' );
237 $key, 30, $func, [
'lowTTL' => 0,
'lockTSE' => 5 ] + $extOpts );
238 $this->assertEquals(
$value, $v,
"Value returned" );
239 $this->assertEquals( 0, $wasSet,
"Value not regenerated" );
245 $key, 30, $func, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
247 $this->assertEquals(
$value, $v,
"Value returned" );
248 $this->assertEquals( 1, $wasSet,
"Value regenerated due to check keys" );
249 $this->assertEquals(
$value, $priorValue,
"Has prior value" );
250 $this->assertInternalType(
'float', $priorAsOf,
"Has prior value" );
252 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check keys generated on miss' );
254 $this->assertGreaterThanOrEqual( $priorTime, $t2,
'Check keys generated on miss' );
256 $mockWallClock += 0.2;
257 $priorTime = $mockWallClock;
260 $key, 30, $func, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
262 $this->assertEquals(
$value, $v,
"Value returned" );
263 $this->assertEquals( 1, $wasSet,
"Value regenerated due to still-recent check keys" );
265 $this->assertLessThanOrEqual( $priorTime, $t1,
'Check keys did not change again' );
267 $this->assertLessThanOrEqual( $priorTime, $t2,
'Check keys did not change again' );
270 $v =
$cache->
get( $key, $curTTL, [ $cKey1, $cKey2 ] );
272 $this->assertEquals(
$value, $v[$cache::VFLD_DATA],
"Value returned" );
274 $this->assertEquals(
$value, $v,
"Value returned" );
276 $this->assertLessThanOrEqual( 0, $curTTL,
"Value has current TTL < 0 due to check keys" );
281 $this->assertEquals(
$value, $v,
"Value returned" );
284 $this->assertEquals(
$value, $v,
"Value still returned after deleted" );
285 $this->assertEquals( 1, $wasSet,
"Value process cached while deleted" );
287 $oldValReceived = -1;
288 $oldAsOfReceived = -1;
289 $checkFunc =
function ( $oldVal, &$ttl,
array $setOpts, $oldAsOf )
290 use ( &$oldValReceived, &$oldAsOfReceived, &$wasSet ) {
292 $oldValReceived = $oldVal;
293 $oldAsOfReceived = $oldAsOf;
295 return 'xxx' . $wasSet;
298 $mockWallClock = 1549343530.2053;
299 $priorTime = $mockWallClock;
304 $key, 30, $checkFunc, [
'staleTTL' => 50 ] + $extOpts );
305 $this->assertEquals(
'xxx1', $v,
"Value returned" );
306 $this->assertEquals(
false, $oldValReceived,
"Callback got no stale value" );
307 $this->assertEquals(
null, $oldAsOfReceived,
"Callback got no stale value" );
309 $mockWallClock += 40;
311 $key, 30, $checkFunc, [
'staleTTL' => 50 ] + $extOpts );
312 $this->assertEquals(
'xxx2', $v,
"Value still returned after expired" );
313 $this->assertEquals( 2, $wasSet,
"Value recalculated while expired" );
314 $this->assertEquals(
'xxx1', $oldValReceived,
"Callback got stale value" );
315 $this->assertNotEquals(
null, $oldAsOfReceived,
"Callback got stale value" );
317 $mockWallClock += 260;
319 $key, 30, $checkFunc, [
'staleTTL' => 50 ] + $extOpts );
320 $this->assertEquals(
'xxx3', $v,
"Value still returned after expired" );
321 $this->assertEquals( 3, $wasSet,
"Value recalculated while expired" );
322 $this->assertEquals(
false, $oldValReceived,
"Callback got no stale value" );
323 $this->assertEquals(
null, $oldAsOfReceived,
"Callback got no stale value" );
325 $mockWallClock = ( $priorTime - $cache::HOLDOFF_TTL - 1 );
330 $mockWallClock = $priorTime;
333 $cache::TTL_INDEFINITE,
335 [
'graceTTL' => $cache::TTL_WEEK,
'checkKeys' => [ $checkKey ] ] + $extOpts
337 $this->assertEquals(
'xxx1', $v,
"Value returned" );
338 $this->assertEquals( 1, $wasSet,
"Value computed" );
339 $this->assertEquals(
false, $oldValReceived,
"Callback got no stale value" );
340 $this->assertEquals(
null, $oldAsOfReceived,
"Callback got no stale value" );
342 $mockWallClock += $cache::TTL_HOUR;
345 $cache::TTL_INDEFINITE,
347 [
'graceTTL' => $cache::TTL_WEEK,
'checkKeys' => [ $checkKey ] ] + $extOpts
349 $this->assertEquals(
'xxx1', $v,
"Cached value returned" );
350 $this->assertEquals( 1, $wasSet,
"Cached value returned" );
353 $mockWallClock += 0.01;
357 $cache::TTL_INDEFINITE,
359 [
'graceTTL' => $cache::TTL_WEEK,
'checkKeys' => [ $checkKey ] ] + $extOpts
361 $this->assertEquals(
'xxx1', $v,
"Value still returned after expired (in grace)" );
362 $this->assertEquals( 1, $wasSet,
"Value still returned after expired (in grace)" );
365 $mockWallClock += $cache::TTL_WEEK;
368 $cache::TTL_INDEFINITE,
370 [
'graceTTL' => $cache::TTL_WEEK,
'checkKeys' => [ $checkKey ] ] + $extOpts
372 $this->assertEquals(
'xxx2', $v,
"Value was recomputed (past grace)" );
373 $this->assertEquals( 2, $wasSet,
"Value was recomputed (past grace)" );
374 $this->assertEquals(
'xxx1', $oldValReceived,
"Callback got post-grace stale value" );
375 $this->assertNotEquals(
null, $oldAsOfReceived,
"Callback got post-grace stale value" );
388 $mockWallClock = 1549343530.2053;
391 $checkFunc =
function ( $oldVal, &$ttl,
array $setOpts, $oldAsOf )
395 return 'xxx' . $wasSet;
401 $touchedCallback =
function ()
use ( &$touched ) {
406 $cache::TTL_INDEFINITE,
408 [
'touchedCallback' => $touchedCallback ] + $extOpts
410 $mockWallClock += 60;
413 $cache::TTL_INDEFINITE,
415 [
'touchedCallback' => $touchedCallback ] + $extOpts
417 $this->assertEquals(
'xxx1', $v,
"Value was computed once" );
418 $this->assertEquals( 1, $wasSet,
"Value was computed once" );
420 $touched = $mockWallClock - 10;
423 $cache::TTL_INDEFINITE,
425 [
'touchedCallback' => $touchedCallback ] + $extOpts
429 $cache::TTL_INDEFINITE,
431 [
'touchedCallback' => $touchedCallback ] + $extOpts
433 $this->assertEquals(
'xxx2', $v,
"Value was recomputed once" );
434 $this->assertEquals( 2, $wasSet,
"Value was recomputed once" );
440 [ [
'version' => 1 ],
true ]
447 $func =
function ( $old, &$ttl, &$opts, $asOf )
use ( &$wasSet, &
$value )
454 $mockWallClock = 1549343530.2053;
459 $opts = [
'lowTTL' => 30 ];
461 $this->assertEquals(
$value, $v,
"Value returned" );
462 $this->assertEquals( 1, $wasSet,
"Value calculated" );
464 $mockWallClock += 0.2;
466 $this->assertEquals( 2, $wasSet,
"Value re-calculated" );
470 $opts = [
'lowTTL' => 1 ];
472 $this->assertEquals(
$value, $v,
"Value returned" );
473 $this->assertEquals( 1, $wasSet,
"Value calculated" );
475 $this->assertEquals( 1, $wasSet,
"Value cached" );
478 $asyncHandler =
function ( $callback )
use ( &$asycList ) {
479 $asycList[] = $callback;
483 'asyncHandler' => $asyncHandler
486 $mockWallClock = 1549343530.2053;
487 $priorTime = $mockWallClock;
492 $opts = [
'lowTTL' => 100 ];
494 $this->assertEquals(
$value, $v,
"Value returned" );
495 $this->assertEquals( 1, $wasSet,
"Value calculated" );
497 $this->assertEquals( 1, $wasSet,
"Cached value used" );
498 $this->assertEquals( $v,
$value,
"Value cached" );
500 $mockWallClock += 250;
502 $this->assertEquals(
$value, $v,
"Value returned" );
503 $this->assertEquals( 1, $wasSet,
"Stale value used" );
504 $this->assertEquals( 1,
count( $asycList ),
"Refresh deferred." );
508 $this->assertEquals( 2, $wasSet,
"Value calculated at later time" );
509 $this->assertEquals( 0,
count( $asycList ),
"No deferred refreshes added." );
511 $this->assertEquals(
$value, $v,
"New value stored" );
517 $mockWallClock = $priorTime;
522 $opts = [
'hotTTR' => 900 ];
524 $this->assertEquals(
$value, $v,
"Value returned" );
525 $this->assertEquals( 1, $wasSet,
"Value calculated" );
527 $mockWallClock += 30;
530 $this->assertEquals( 1, $wasSet,
"Value cached" );
532 $mockWallClock = $priorTime;
535 $opts = [
'hotTTR' => 10 ];
537 $this->assertEquals(
$value, $v,
"Value returned" );
538 $this->assertEquals( 1, $wasSet,
"Value calculated" );
540 $mockWallClock += 30;
543 $this->assertEquals(
$value, $v,
"Value returned" );
544 $this->assertEquals( 2, $wasSet,
"Value re-calculated" );
553 $this->
cache->getWithSetCallback(
'key', 30,
'invalid callback' );
576 $genFunc =
function ( $id, $old, &$ttl, &$opts, $asOf )
use (
577 &$wasSet, &$priorValue, &$priorAsOf
586 $mockWallClock = 1549343530.2053;
587 $priorTime = $mockWallClock;
591 $keyedIds =
new ArrayIterator( [ $keyA => 3353 ] );
594 $keyedIds, 30, $genFunc, [
'lockTSE' => 5 ] + $extOpts );
595 $this->assertEquals(
$value, $v[$keyA],
"Value returned" );
596 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
597 $this->assertFalse( $priorValue,
"No prior value" );
598 $this->assertNull( $priorAsOf,
"No prior value" );
602 $this->assertLessThanOrEqual( 20, $curTTL,
'Current TTL between 19-20 (overriden)' );
603 $this->assertGreaterThanOrEqual( 19, $curTTL,
'Current TTL between 19-20 (overriden)' );
607 $keyedIds =
new ArrayIterator( [ $keyB =>
'efef' ] );
609 $keyedIds, 30, $genFunc, [
'lowTTL' => 0,
'lockTSE' => 5, ] + $extOpts );
610 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
611 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
614 $keyedIds, 30, $genFunc, [
'lowTTL' => 0,
'lockTSE' => 5, ] + $extOpts );
615 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
616 $this->assertEquals( 1, $wasSet,
"Value not regenerated" );
622 $keyedIds =
new ArrayIterator( [ $keyB =>
'efef' ] );
624 $keyedIds, 30, $genFunc, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
626 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
627 $this->assertEquals( 1, $wasSet,
"Value regenerated due to check keys" );
628 $this->assertEquals(
$value, $priorValue,
"Has prior value" );
629 $this->assertInternalType(
'float', $priorAsOf,
"Has prior value" );
631 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check keys generated on miss' );
633 $this->assertGreaterThanOrEqual( $priorTime, $t2,
'Check keys generated on miss' );
635 $mockWallClock += 0.01;
636 $priorTime = $mockWallClock;
639 $keyedIds =
new ArrayIterator( [ $keyC => 43636 ] );
641 $keyedIds, 30, $genFunc, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
643 $this->assertEquals(
$value, $v[$keyC],
"Value returned" );
644 $this->assertEquals( 1, $wasSet,
"Value regenerated due to still-recent check keys" );
646 $this->assertLessThanOrEqual( $priorTime, $t1,
'Check keys did not change again' );
648 $this->assertLessThanOrEqual( $priorTime, $t2,
'Check keys did not change again' );
651 $v =
$cache->
get( $keyC, $curTTL, [ $cKey1, $cKey2 ] );
653 $this->assertEquals(
$value, $v[$cache::VFLD_DATA],
"Value returned" );
655 $this->assertEquals(
$value, $v,
"Value returned" );
657 $this->assertLessThanOrEqual( 0, $curTTL,
"Value has current TTL < 0 due to check keys" );
661 $keyedIds =
new ArrayIterator( [ $key => 242424 ] );
663 $keyedIds, 30, $genFunc, [
'pcTTL' => 5 ] + $extOpts );
664 $this->assertEquals(
"@{$keyedIds[$key]}$", $v[$key],
"Value returned" );
666 $keyedIds =
new ArrayIterator( [ $key => 242424 ] );
668 $keyedIds, 30, $genFunc, [
'pcTTL' => 5 ] + $extOpts );
669 $this->assertEquals(
"@{$keyedIds[$key]}$", $v[$key],
"Value still returned after deleted" );
670 $this->assertEquals( 1, $wasSet,
"Value process cached while deleted" );
673 $ids = [ 1, 2, 3, 4, 5, 6 ];
675 return $wanCache->makeKey(
'test', $id );
678 $genFunc =
function ( $id, $oldValue, &$ttl,
array &$setops )
use ( &$calls ) {
686 [
"val-1",
"val-2",
"val-3",
"val-4",
"val-5",
"val-6" ],
687 array_values( $values ),
688 "Correct values in correct order"
691 array_map( $keyFunc, $ids, array_fill( 0,
count( $ids ), $this->
cache ) ),
692 array_keys( $values ),
693 "Correct keys in correct order"
695 $this->assertEquals(
count( $ids ), $calls );
698 $this->assertEquals(
count( $ids ), $calls,
"Values cached" );
702 ->setMethods( [
'getMulti' ] )->getMock();
703 $localBag->expects( $this->exactly( 1 ) )->method(
'getMulti' )->willReturn( [
710 $keyedIds =
new ArrayIterator( [
'k1' =>
'id1',
'k2' =>
'id2' ] );
712 [
'k1' =>
'val-id1',
'k2' =>
'val-id2' ],
713 $wanCache->getMultiWithSetCallback( $keyedIds, 10, $genFunc, [
'pcTTL' => 5 ] )
717 [
'k1' =>
'val-id1',
'k2' =>
'val-id2' ],
718 $wanCache->getMultiWithSetCallback( $keyedIds, 10, $genFunc, [
'pcTTL' => 5 ] )
725 [ [
'version' => 1 ],
true ]
747 &$wasSet, &$priorValue, &$priorAsOf
750 foreach ( $ids
as $id ) {
752 $newValues[$id] =
"@$id$";
759 $mockWallClock = 1549343530.2053;
760 $priorTime = $mockWallClock;
764 $keyedIds =
new ArrayIterator( [ $keyA => 3353 ] );
767 $keyedIds, 30, $genFunc, $extOpts );
768 $this->assertEquals(
$value, $v[$keyA],
"Value returned" );
769 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
773 $this->assertLessThanOrEqual( 20, $curTTL,
'Current TTL between 19-20 (overriden)' );
774 $this->assertGreaterThanOrEqual( 19, $curTTL,
'Current TTL between 19-20 (overriden)' );
778 $keyedIds =
new ArrayIterator( [ $keyB =>
'efef' ] );
780 $keyedIds, 30, $genFunc, [
'lowTTL' => 0 ] + $extOpts );
781 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
782 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
785 $keyedIds, 30, $genFunc, [
'lowTTL' => 0 ] + $extOpts );
786 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
787 $this->assertEquals( 1, $wasSet,
"Value not regenerated" );
793 $keyedIds =
new ArrayIterator( [ $keyB =>
'efef' ] );
795 $keyedIds, 30, $genFunc, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
797 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
798 $this->assertEquals( 1, $wasSet,
"Value regenerated due to check keys" );
800 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check keys generated on miss' );
802 $this->assertGreaterThanOrEqual( $priorTime, $t2,
'Check keys generated on miss' );
804 $mockWallClock += 0.01;
805 $priorTime = $mockWallClock;
808 $keyedIds =
new ArrayIterator( [ $keyC => 43636 ] );
810 $keyedIds, 30, $genFunc, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
812 $this->assertEquals(
$value, $v[$keyC],
"Value returned" );
813 $this->assertEquals( 1, $wasSet,
"Value regenerated due to still-recent check keys" );
815 $this->assertLessThanOrEqual( $priorTime, $t1,
'Check keys did not change again' );
817 $this->assertLessThanOrEqual( $priorTime, $t2,
'Check keys did not change again' );
820 $v =
$cache->
get( $keyC, $curTTL, [ $cKey1, $cKey2 ] );
822 $this->assertEquals(
$value, $v[$cache::VFLD_DATA],
"Value returned" );
824 $this->assertEquals(
$value, $v,
"Value returned" );
826 $this->assertLessThanOrEqual( 0, $curTTL,
"Value has current TTL < 0 due to check keys" );
830 $keyedIds =
new ArrayIterator( [ $key => 242424 ] );
832 $keyedIds, 30, $genFunc, [
'pcTTL' => 5 ] + $extOpts );
833 $this->assertEquals(
"@{$keyedIds[$key]}$", $v[$key],
"Value returned" );
835 $keyedIds =
new ArrayIterator( [ $key => 242424 ] );
837 $keyedIds, 30, $genFunc, [
'pcTTL' => 5 ] + $extOpts );
838 $this->assertEquals(
"@{$keyedIds[$key]}$", $v[$key],
"Value still returned after deleted" );
839 $this->assertEquals( 1, $wasSet,
"Value process cached while deleted" );
842 $ids = [ 1, 2, 3, 4, 5, 6 ];
844 return $wanCache->makeKey(
'test', $id );
849 foreach ( $ids
as $id ) {
851 $newValues[$id] =
"val-{$id}";
859 [
"val-1",
"val-2",
"val-3",
"val-4",
"val-5",
"val-6" ],
860 array_values( $values ),
861 "Correct values in correct order"
864 array_map( $keyFunc, $ids, array_fill( 0,
count( $ids ), $this->
cache ) ),
865 array_keys( $values ),
866 "Correct keys in correct order"
868 $this->assertEquals(
count( $ids ), $calls );
871 $this->assertEquals(
count( $ids ), $calls,
"Values cached" );
877 [ [
'version' => 1 ],
true ]
890 $mockWallClock = 1549343530.2053;
901 $this->assertEquals( 1, $calls,
'Value was populated' );
904 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
908 [
'lockTSE' => 5,
'checkKeys' => $checkKeys ] );
909 $this->assertEquals(
$value,
$ret,
'Old value used' );
910 $this->assertEquals( 1, $calls,
'Callback was not used' );
913 $mockWallClock += 0.001;
915 [
'lockTSE' => 5,
'checkKeys' => $checkKeys ] );
916 $this->assertEquals(
$value,
$ret,
'Callback was used; interim saved' );
917 $this->assertEquals( 2, $calls,
'Callback was used; interim saved' );
920 [
'lockTSE' => 5,
'checkKeys' => $checkKeys ] );
921 $this->assertEquals(
$value,
$ret,
'Callback was not used; used interim (mutex failed)' );
922 $this->assertEquals( 2, $calls,
'Callback was not used; used interim (mutex failed)' );
936 $mockWallClock = 1549343530.2053;
940 $func =
function ( $oldValue, &$ttl, &$setOpts )
use ( &$calls,
$value, &$mockWallClock ) {
942 $setOpts[
'since'] = $mockWallClock - 10;
950 $this->assertEquals(
$value,
$cache->
get( $key, $curTTL ),
'Value was populated' );
951 $this->assertEquals( 1, $curTTL,
'Value has reduced logical TTL', 0.01 );
952 $this->assertEquals( 1, $calls,
'Value was generated' );
958 $this->assertEquals( 2, $calls,
'Callback used (mutex acquired)' );
962 $this->assertEquals( 2, $calls,
'Callback was not used (interim value used)' );
966 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
970 $this->assertEquals( 2, $calls,
'Callback was not used (mutex not acquired)' );
972 $mockWallClock += 301;
974 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
978 $this->assertEquals( 3, $calls,
'Callback was used (mutex not acquired, not in cache)' );
981 $func2 =
function ( $oldValue, &$ttl, &$setOpts )
use ( &$calls,
$value ) {
983 $setOpts[
'lag'] = 15;
991 $this->assertEquals(
$value,
$cache->
get( $key2, $curTTL ),
'Value was populated' );
992 $this->assertEquals( 30, $curTTL,
'Value has reduced logical TTL', 0.01 );
993 $this->assertEquals( 1, $calls,
'Value was generated' );
997 $this->assertEquals( 1, $calls,
'Callback was used (not expired)' );
999 $mockWallClock += 31;
1003 $this->assertEquals( 2, $calls,
'Callback was used (mutex acquired)' );
1016 $mockWallClock = 1549343530.2053;
1027 $this->assertEquals( 1, $calls,
'Value was populated' );
1029 $mockWallClock += 0.2;
1032 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
1036 [
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
1037 $this->assertEquals(
$value,
$ret,
'Callback used' );
1038 $this->assertEquals( 2, $calls,
'Callback used' );
1041 [
'lockTSE' => 30,
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
1042 $this->assertEquals(
$value,
$ret,
'Old value used' );
1043 $this->assertEquals( 2, $calls,
'Callback was not used' );
1047 [
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
1048 $this->assertEquals( $busyValue,
$ret,
'Callback was not used; used busy value' );
1049 $this->assertEquals( 2, $calls,
'Callback was not used; used busy value' );
1051 $this->internalCache->delete( $cache::MUTEX_KEY_PREFIX . $key );
1052 $mockWallClock += 0.001;
1054 [
'lockTSE' => 30,
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
1055 $this->assertEquals(
$value,
$ret,
'Callback was used; saved interim' );
1056 $this->assertEquals( 3, $calls,
'Callback was used; saved interim' );
1058 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
1060 [
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
1061 $this->assertEquals(
$value,
$ret,
'Callback was not used; used interim' );
1062 $this->assertEquals( 3, $calls,
'Callback was not used; used interim' );
1071 $value1 = [
'this' =>
'is',
'a' =>
'test' ];
1072 $value2 = [
'this' =>
'is',
'another' =>
'test' ];
1078 $mockWallClock = 1549343530.2053;
1079 $priorTime = $mockWallClock;
1086 $this->assertEquals(
1087 [ $key1 => $value1, $key2 => $value2 ],
1089 'Result array populated'
1092 $this->assertEquals( 2,
count( $curTTLs ),
"Two current TTLs in array" );
1093 $this->assertGreaterThan( 0, $curTTLs[$key1],
"Key 1 has current TTL > 0" );
1094 $this->assertGreaterThan( 0, $curTTLs[$key2],
"Key 2 has current TTL > 0" );
1099 $mockWallClock += 1;
1102 $this->assertEquals(
1103 [ $key1 => $value1, $key2 => $value2 ],
1104 $cache->
getMulti( [ $key1, $key2, $key3 ], $curTTLs, [ $cKey1, $cKey2 ] ),
1105 "Result array populated even with new check keys"
1108 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check key 1 generated on miss' );
1110 $this->assertGreaterThanOrEqual( $priorTime, $t2,
'Check key 2 generated on miss' );
1111 $this->assertEquals( 2,
count( $curTTLs ),
"Current TTLs array set" );
1112 $this->assertLessThanOrEqual( 0, $curTTLs[$key1],
'Key 1 has current TTL <= 0' );
1113 $this->assertLessThanOrEqual( 0, $curTTLs[$key2],
'Key 2 has current TTL <= 0' );
1115 $mockWallClock += 1;
1118 $this->assertEquals(
1119 [ $key1 => $value1, $key2 => $value2 ],
1120 $cache->
getMulti( [ $key1, $key2, $key3 ], $curTTLs, [ $cKey1, $cKey2 ] ),
1121 "Result array still populated even with new check keys"
1123 $this->assertEquals( 2,
count( $curTTLs ),
"Current TTLs still array set" );
1124 $this->assertLessThan( 0, $curTTLs[$key1],
'Key 1 has negative current TTL' );
1125 $this->assertLessThan( 0, $curTTLs[$key2],
'Key 2 has negative current TTL' );
1142 $mockWallClock = 1549343530.2053;
1147 foreach ( [ $checkAll, $check1, $check2 ]
as $checkKey ) {
1151 $mockWallClock += 0.100;
1163 $this->assertEquals(
1164 [
'key1' => $value1,
'key2' => $value2 ],
1168 $this->assertGreaterThanOrEqual( 9.5, $curTTLs[
'key1'],
'Initial ttls' );
1169 $this->assertLessThanOrEqual( 10.5, $curTTLs[
'key1'],
'Initial ttls' );
1170 $this->assertGreaterThanOrEqual( 9.5, $curTTLs[
'key2'],
'Initial ttls' );
1171 $this->assertLessThanOrEqual( 10.5, $curTTLs[
'key2'],
'Initial ttls' );
1173 $mockWallClock += 0.100;
1183 $this->assertEquals(
1184 [
'key1' => $value1,
'key2' => $value2 ],
1186 'key1 expired by check1, but value still provided'
1188 $this->assertLessThan( 0, $curTTLs[
'key1'],
'key1 TTL expired' );
1189 $this->assertGreaterThan( 0, $curTTLs[
'key2'],
'key2 still valid' );
1200 $this->assertEquals(
1201 [
'key1' => $value1,
'key2' => $value2 ],
1203 'All keys expired by checkAll, but value still provided'
1205 $this->assertLessThan( 0, $curTTLs[
'key1'],
'key1 expired by checkAll' );
1206 $this->assertLessThan( 0, $curTTLs[
'key2'],
'key2 expired by checkAll' );
1216 for ( $i = 0; $i < 500; ++$i ) {
1220 $cache->
get( $key, $curTTL, [ $checkKey ] );
1223 $v =
$cache->
get( $key, $curTTL, [ $checkKey ] );
1225 $this->assertEquals(
'val', $v );
1226 $this->assertLessThan( 0, $curTTL,
"Step $i: CTL < 0 (miss/set/hit)" );
1229 for ( $i = 0; $i < 500; ++$i ) {
1235 $v =
$cache->
get( $key, $curTTL, [ $checkKey ] );
1237 $this->assertEquals(
'val', $v );
1238 $this->assertLessThan( 0, $curTTL,
"Step $i: CTL < 0 (set/hit)" );
1253 $v = $this->
cache->get( $key, $curTTL );
1254 $this->assertEquals(
$value, $v,
"Key was created with value" );
1255 $this->assertGreaterThan( 0, $curTTL,
"Existing key has current TTL > 0" );
1257 $this->
cache->delete( $key );
1260 $v = $this->
cache->get( $key, $curTTL );
1261 $this->assertFalse( $v,
"Deleted key has false value" );
1262 $this->assertLessThan( 0, $curTTL,
"Deleted key has current TTL < 0" );
1265 $v = $this->
cache->get( $key, $curTTL );
1266 $this->assertFalse( $v,
"Deleted key is tombstoned and has false value" );
1267 $this->assertLessThan( 0, $curTTL,
"Deleted key is tombstoned and has current TTL < 0" );
1273 $v = $this->
cache->get( $key, $curTTL );
1274 $this->assertFalse( $v,
"Deleted key has false value" );
1275 $this->assertNull( $curTTL,
"Deleted key has null current TTL" );
1278 $v = $this->
cache->get( $key, $curTTL );
1279 $this->assertEquals(
$value, $v,
"Key was created with value" );
1280 $this->assertGreaterThan( 0, $curTTL,
"Existing key has current TTL > 0" );
1298 $funcV1 =
function ()
use ( &$wasSet, $valueV1 ) {
1304 $priorValue =
false;
1306 $funcV2 =
function ( $oldValue, &$ttl, $setOpts, $oldAsOf )
1307 use ( &$wasSet, $valueV2, &$priorValue, &$priorAsOf ) {
1308 $priorValue = $oldValue;
1309 $priorAsOf = $oldAsOf;
1318 $this->assertEquals( $valueV1, $v,
"Value returned" );
1319 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
1321 $this->assertEquals( 1, $wasSet,
"Value not regenerated" );
1322 $this->assertEquals( $valueV1, $v,
"Value not regenerated" );
1326 $verOpts = [
'version' => $extOpts[
'version'] + 1 ];
1329 $verOpts = [
'version' => 1 ];
1335 $this->assertEquals( $valueV2, $v,
"Value returned" );
1336 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
1337 $this->assertEquals(
false, $priorValue,
"Old value not given due to old format" );
1338 $this->assertEquals(
null, $priorAsOf,
"Old value not given due to old format" );
1342 $this->assertEquals( $valueV2, $v,
"Value not regenerated (secondary key)" );
1343 $this->assertEquals( 0, $wasSet,
"Value not regenerated (secondary key)" );
1351 $this->assertEquals( $valueV2, $v,
"Value returned" );
1352 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
1355 $this->assertEquals( $valueV2, $v,
"Value not regenerated (main key)" );
1356 $this->assertEquals( 1, $wasSet,
"Value not regenerated (main key)" );
1362 [ [
'version' => 1 ],
true ]
1373 $mockWallClock = 1549343530.2053;
1378 $func =
function ()
use ( &$wasCalled,
$value ) {
1389 $this->assertEquals( 1, $wasCalled,
'Value cached' );
1392 $mockWallClock += 0.001;
1394 $this->assertEquals( 2, $wasCalled,
'Value regenerated (got mutex)' );
1396 $this->assertEquals( 2, $wasCalled,
'Value interim cached' );
1398 $mockWallClock += 0.2;
1400 $this->assertEquals( 3, $wasCalled,
'Value regenerated (got mutex)' );
1402 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
1404 $this->assertEquals( 3, $wasCalled,
'Value interim cached (failed mutex)' );
1405 $this->internalCache->delete( $cache::MUTEX_KEY_PREFIX . $key );
1413 $this->assertEquals( 1, $wasCalled,
'Value cached' );
1416 $this->assertEquals( 2, $wasCalled,
'Value regenerated (got mutex)' );
1418 $this->assertEquals( 3, $wasCalled,
'Value still regenerated (got mutex)' );
1420 $this->assertEquals( 4, $wasCalled,
'Value still regenerated (got mutex)' );
1422 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
1424 $this->assertEquals( 5, $wasCalled,
'Value still regenerated (failed mutex)' );
1439 $mockWallClock = 1549343530.2053;
1440 $priorTime = $mockWallClock;
1443 $mockWallClock += 0.100;
1445 $this->assertGreaterThanOrEqual( $priorTime, $t0,
'Check key auto-created' );
1447 $priorTime = $mockWallClock;
1448 $mockWallClock += 0.100;
1451 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check key created' );
1454 $this->assertEquals( $t1, $t2,
'Check key time did not change' );
1456 $mockWallClock += 0.100;
1459 $this->assertGreaterThan( $t2, $t3,
'Check key time increased' );
1462 $this->assertEquals( $t3, $t4,
'Check key time did not change' );
1464 $mockWallClock += 0.100;
1467 $this->assertGreaterThan( $t4, $t5,
'Check key time increased' );
1470 $this->assertEquals( $t5, $t6,
'Check key time did not change' );
1482 $mockWallClock = 1549343530.2053;
1483 $priorTime = $mockWallClock;
1484 $this->
cache->setMockTime( $mockWallClock );
1487 $this->internalCache->set(
1491 $this->internalCache->set(
1495 $this->internalCache->set(
1502 $v = $this->
cache->get( $key, $curTTL, [ $tKey1, $tKey2 ] );
1503 $this->assertEquals(
$value, $v,
"Value matches" );
1504 $this->assertLessThan( -4.9, $curTTL,
"Correct CTL" );
1505 $this->assertGreaterThan( -5.1, $curTTL,
"Correct CTL" );
1519 $knownPurge = time() - 60;
1520 $goodTime = microtime(
true ) - 5;
1521 $badTime = microtime(
true ) - 300;
1523 $this->internalCache->set(
1532 $this->internalCache->set(
1541 $this->internalCache->set(
1545 $this->internalCache->set(
1550 $this->assertEquals(
$value, $this->
cache->get( $vKey1 ) );
1551 $this->assertEquals(
$value, $this->
cache->get( $vKey2 ) );
1552 $this->
cache->reap( $vKey1, $knownPurge, $bad1 );
1553 $this->
cache->reap( $vKey2, $knownPurge, $bad2 );
1555 $this->assertFalse( $bad1 );
1556 $this->assertTrue( $bad2 );
1558 $this->
cache->reapCheckKey( $tKey1, $knownPurge, $tBad1 );
1559 $this->
cache->reapCheckKey( $tKey2, $knownPurge, $tBad2 );
1560 $this->assertFalse( $tBad1 );
1561 $this->assertTrue( $tBad2 );
1569 ->setMethods( [
'get',
'changeTTL' ] )->getMock();
1570 $backend->expects( $this->once() )->method(
'get' )
1577 $backend->expects( $this->once() )->method(
'changeTTL' )
1578 ->willReturn(
false );
1585 $ret = $wanCache->reap(
'key', 360, $isStale );
1586 $this->assertTrue( $isStale,
'value was stale' );
1587 $this->assertFalse(
$ret,
'changeTTL failed' );
1597 $opts = [
'lag' => 300,
'since' => microtime(
true ) ];
1599 $this->assertEquals(
$value, $this->
cache->get( $key ),
"Rep-lagged value written." );
1602 $opts = [
'lag' => 0,
'since' => microtime(
true ) - 300 ];
1604 $this->assertEquals(
false, $this->
cache->get( $key ),
"Trx-lagged value not written." );
1607 $opts = [
'lag' => 5,
'since' => microtime(
true ) - 5 ];
1609 $this->assertEquals(
false, $this->
cache->get( $key ),
"Lagged value not written." );
1619 $opts = [
'pending' =>
true ];
1621 $this->assertEquals(
false, $this->
cache->get( $key ),
"Pending value not written." );
1626 ->setMethods( [
'set',
'delete' ] )->getMock();
1627 $localBag->expects( $this->never() )->method(
'set' );
1628 $localBag->expects( $this->never() )->method(
'delete' );
1630 'cache' => $localBag,
1631 'mcrouterAware' =>
true,
1632 'region' =>
'pmtpa',
1633 'cluster' =>
'mw-wan'
1635 $valFunc =
function () {
1640 $wanCache->get(
'x' );
1641 $wanCache->get(
'x', $ctl, [
'check1' ] );
1642 $wanCache->getMulti( [
'x',
'y' ] );
1643 $wanCache->getMulti( [
'x',
'y' ], $ctls, [
'check2' ] );
1644 $wanCache->getWithSetCallback(
'p', 30, $valFunc );
1645 $wanCache->getCheckKeyTime(
'zzz' );
1646 $wanCache->reap(
'x', time() - 300 );
1647 $wanCache->reap(
'zzz', time() - 300 );
1652 ->setMethods( [
'set' ] )->getMock();
1654 'cache' => $localBag,
1655 'mcrouterAware' =>
true,
1656 'region' =>
'pmtpa',
1657 'cluster' =>
'mw-wan'
1660 $localBag->expects( $this->once() )->method(
'set' )
1661 ->with(
"/*/mw-wan/" . $wanCache::VALUE_KEY_PREFIX .
"test" );
1663 $wanCache->delete(
'test' );
1668 ->setMethods( [
'set' ] )->getMock();
1670 'cache' => $localBag,
1671 'mcrouterAware' =>
true,
1672 'region' =>
'pmtpa',
1673 'cluster' =>
'mw-wan'
1676 $localBag->expects( $this->once() )->method(
'set' )
1677 ->with(
"/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX .
"test" );
1679 $wanCache->touchCheckKey(
'test' );
1684 ->setMethods( [
'delete' ] )->getMock();
1686 'cache' => $localBag,
1687 'mcrouterAware' =>
true,
1688 'region' =>
'pmtpa',
1689 'cluster' =>
'mw-wan'
1692 $localBag->expects( $this->once() )->method(
'delete' )
1693 ->with(
"/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX .
"test" );
1695 $wanCache->resetCheckKey(
'test' );
1703 $now = microtime(
true );
1706 $cache->
set( $key,
'Do what thou Wilt' );
1711 $this->assertEquals(
'Do what thou Wilt',
$cache->
get( $key ) );
1716 'epoch' => $now - 3600
1720 $this->assertEquals(
'Do what thou Wilt',
$cache->
get( $key ) );
1726 'epoch' => $now + 3600
1730 $this->assertFalse(
$cache->
get( $key ),
'Key rejected due to epoch' );
1744 $mtime = $ago ? time() - $ago : $ago;
1746 $ttl = $this->
cache->adaptiveTTL( $mtime, $maxTTL, $minTTL, $factor );
1748 $this->assertGreaterThanOrEqual( $adaptiveTTL - $margin, $ttl );
1749 $this->assertLessThanOrEqual( $adaptiveTTL + $margin, $ttl );
1751 $ttl = $this->
cache->adaptiveTTL( (
string)$mtime, $maxTTL, $minTTL, $factor );
1753 $this->assertGreaterThanOrEqual( $adaptiveTTL - $margin, $ttl );
1754 $this->assertLessThanOrEqual( $adaptiveTTL + $margin, $ttl );
1759 [ 3600, 900, 30, 0.2, 720 ],
1760 [ 3600, 500, 30, 0.2, 500 ],
1761 [ 3600, 86400, 800, 0.2, 800 ],
1762 [
false, 86400, 800, 0.2, 800 ],
1763 [
null, 86400, 800, 0.2, 800 ]
1772 $this->assertInstanceOf(
1782 $this->assertSame(
null, $this->
cache->setLogger(
new Psr\Log\NullLogger ) );
1790 ->setMethods( [
'getQoS' ] )->getMock();
1791 $backend->expects( $this->once() )->method(
'getQoS' )
1796 $wanCache::QOS_UNKNOWN,
1797 $wanCache->getQoS( $wanCache::ATTR_EMULATION )
1806 ->setMethods( [
'makeKey' ] )->getMock();
1807 $backend->expects( $this->once() )->method(
'makeKey' )
1808 ->willReturn(
'special' );
1814 $this->assertSame(
'special', $wanCache->makeKey(
'a',
'b' ) );
1822 ->setMethods( [
'makeGlobalKey' ] )->getMock();
1823 $backend->expects( $this->once() )->method(
'makeGlobalKey' )
1824 ->willReturn(
'special' );
1830 $this->assertSame(
'special', $wanCache->makeGlobalKey(
'a',
'b' ) );
1835 [
'domain:page:5',
'page' ],
1836 [
'domain:main-key',
'main-key' ],
1837 [
'domain:page:history',
'page' ],
1838 [
'missingdomainkey',
'missingdomainkey' ]
1847 $wanCache = TestingAccessWrapper::newFromObject(
new WANObjectCache( [
1851 $this->assertEquals( $class, $wanCache->determineKeyClassForStats( $key ) );
1859 return ( $curTTL > 0 && ( $curTTL + self::CLOCK_SKEW ) < $lowTTL );
1865 return ( ( $now - $asOf ) > $timeTillRefresh );