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 $priorTime = microtime(
true );
234 $key, 30, $func, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
236 $this->assertEquals(
$value, $v,
"Value returned" );
237 $this->assertEquals( 1, $wasSet,
"Value regenerated due to check keys" );
238 $this->assertEquals(
$value, $priorValue,
"Has prior value" );
239 $this->assertInternalType(
'float', $priorAsOf,
"Has prior value" );
241 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check keys generated on miss' );
243 $this->assertGreaterThanOrEqual( $priorTime, $t2,
'Check keys generated on miss' );
249 $key, 30, $func, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
251 $this->assertEquals(
$value, $v,
"Value returned" );
252 $this->assertEquals( 1, $wasSet,
"Value regenerated due to still-recent check keys" );
254 $this->assertLessThanOrEqual( $priorTime, $t1,
'Check keys did not change again' );
256 $this->assertLessThanOrEqual( $priorTime, $t2,
'Check keys did not change again' );
259 $v =
$cache->
get( $key, $curTTL, [ $cKey1, $cKey2 ] );
261 $this->assertEquals(
$value, $v[$cache::VFLD_DATA],
"Value returned" );
263 $this->assertEquals(
$value, $v,
"Value returned" );
265 $this->assertLessThanOrEqual( 0, $curTTL,
"Value has current TTL < 0 due to check keys" );
270 $this->assertEquals(
$value, $v,
"Value returned" );
273 $this->assertEquals(
$value, $v,
"Value still returned after deleted" );
274 $this->assertEquals( 1, $wasSet,
"Value process cached while deleted" );
276 $oldValReceived = -1;
277 $oldAsOfReceived = -1;
278 $checkFunc =
function ( $oldVal, &$ttl,
array $setOpts, $oldAsOf )
279 use ( &$oldValReceived, &$oldAsOfReceived, &$wasSet ) {
281 $oldValReceived = $oldVal;
282 $oldAsOfReceived = $oldAsOf;
284 return 'xxx' . $wasSet;
287 $now = microtime(
true );
292 $key, 30, $checkFunc, [
'staleTTL' => 50 ] + $extOpts );
293 $this->assertEquals(
'xxx1', $v,
"Value returned" );
294 $this->assertEquals(
false, $oldValReceived,
"Callback got no stale value" );
295 $this->assertEquals(
null, $oldAsOfReceived,
"Callback got no stale value" );
299 $key, 30, $checkFunc, [
'staleTTL' => 50 ] + $extOpts );
300 $this->assertEquals(
'xxx2', $v,
"Value still returned after expired" );
301 $this->assertEquals( 2, $wasSet,
"Value recalculated while expired" );
302 $this->assertEquals(
'xxx1', $oldValReceived,
"Callback got stale value" );
303 $this->assertNotEquals(
null, $oldAsOfReceived,
"Callback got stale value" );
307 $key, 30, $checkFunc, [
'staleTTL' => 50 ] + $extOpts );
308 $this->assertEquals(
'xxx3', $v,
"Value still returned after expired" );
309 $this->assertEquals( 3, $wasSet,
"Value recalculated while expired" );
310 $this->assertEquals(
false, $oldValReceived,
"Callback got no stale value" );
311 $this->assertEquals(
null, $oldAsOfReceived,
"Callback got no stale value" );
321 $cache::TTL_INDEFINITE,
323 [
'graceTTL' => $cache::TTL_WEEK,
'checkKeys' => [ $checkKey ] ] + $extOpts
325 $this->assertEquals(
'xxx1', $v,
"Value returned" );
326 $this->assertEquals( 1, $wasSet,
"Value computed" );
327 $this->assertEquals(
false, $oldValReceived,
"Callback got no stale value" );
328 $this->assertEquals(
null, $oldAsOfReceived,
"Callback got no stale value" );
333 $cache::TTL_INDEFINITE,
335 [
'graceTTL' => $cache::TTL_WEEK,
'checkKeys' => [ $checkKey ] ] + $extOpts
337 $this->assertEquals(
'xxx1', $v,
"Cached value returned" );
338 $this->assertEquals( 1, $wasSet,
"Cached value returned" );
345 $cache::TTL_INDEFINITE,
347 [
'graceTTL' => $cache::TTL_WEEK,
'checkKeys' => [ $checkKey ] ] + $extOpts
349 $this->assertEquals(
'xxx1', $v,
"Value still returned after expired (in grace)" );
350 $this->assertEquals( 1, $wasSet,
"Value still returned after expired (in grace)" );
357 $cache::TTL_INDEFINITE,
359 [
'graceTTL' => $cache::TTL_WEEK,
'checkKeys' => [ $checkKey ] ] + $extOpts
361 $this->assertEquals(
'xxx2', $v,
"Value was recomputed (past grace)" );
362 $this->assertEquals( 2, $wasSet,
"Value was recomputed (past grace)" );
363 $this->assertEquals(
'xxx1', $oldValReceived,
"Callback got post-grace stale value" );
364 $this->assertNotEquals(
null, $oldAsOfReceived,
"Callback got post-grace stale value" );
370 [ [
'version' => 1 ],
true ]
377 $func =
function ( $old, &$ttl, &$opts, $asOf )
use ( &$wasSet, &
$value )
390 $opts = [
'lowTTL' => 30 ];
392 $this->assertEquals(
$value, $v,
"Value returned" );
393 $this->assertEquals( 1, $wasSet,
"Value calculated" );
395 $this->assertEquals( 2, $wasSet,
"Value re-calculated" );
399 $opts = [
'lowTTL' => 1 ];
401 $this->assertEquals(
$value, $v,
"Value returned" );
402 $this->assertEquals( 1, $wasSet,
"Value calculated" );
404 $this->assertEquals( 1, $wasSet,
"Value cached" );
407 $asyncHandler =
function ( $callback )
use ( &$asycList ) {
408 $asycList[] = $callback;
413 'asyncHandler' => $asyncHandler
416 $now = microtime(
true );
422 $opts = [
'lowTTL' => 100 ];
424 $this->assertEquals(
$value, $v,
"Value returned" );
425 $this->assertEquals( 1, $wasSet,
"Value calculated" );
427 $this->assertEquals( 1, $wasSet,
"Cached value used" );
428 $this->assertEquals( $v,
$value,
"Value cached" );
432 $this->assertEquals(
$value, $v,
"Value returned" );
433 $this->assertEquals( 1, $wasSet,
"Stale value used" );
434 $this->assertEquals( 1,
count( $asycList ),
"Refresh deferred." );
438 $this->assertEquals( 2, $wasSet,
"Value calculated at later time" );
439 $this->assertEquals( 0,
count( $asycList ),
"No deferred refreshes added." );
441 $this->assertEquals(
$value, $v,
"New value stored" );
452 $opts = [
'hotTTR' => 900 ];
454 $this->assertEquals(
$value, $v,
"Value returned" );
455 $this->assertEquals( 1, $wasSet,
"Value calculated" );
458 $this->assertEquals( 1, $wasSet,
"Value cached" );
462 $opts = [
'hotTTR' => 10 ];
465 $this->assertEquals(
$value, $v,
"Value returned" );
466 $this->assertEquals( 1, $wasSet,
"Value calculated" );
469 $this->assertEquals(
$value, $v,
"Value returned" );
470 $this->assertEquals( 2, $wasSet,
"Value re-calculated" );
479 $this->
cache->getWithSetCallback(
'key', 30,
'invalid callback' );
502 $genFunc =
function ( $id, $old, &$ttl, &$opts, $asOf )
use (
503 &$wasSet, &$priorValue, &$priorAsOf
513 $keyedIds =
new ArrayIterator( [ $keyA => 3353 ] );
516 $keyedIds, 30, $genFunc, [
'lockTSE' => 5 ] + $extOpts );
517 $this->assertEquals(
$value, $v[$keyA],
"Value returned" );
518 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
519 $this->assertFalse( $priorValue,
"No prior value" );
520 $this->assertNull( $priorAsOf,
"No prior value" );
524 $this->assertLessThanOrEqual( 20, $curTTL,
'Current TTL between 19-20 (overriden)' );
525 $this->assertGreaterThanOrEqual( 19, $curTTL,
'Current TTL between 19-20 (overriden)' );
529 $keyedIds =
new ArrayIterator( [ $keyB =>
'efef' ] );
531 $keyedIds, 30, $genFunc, [
'lowTTL' => 0,
'lockTSE' => 5, ] + $extOpts );
532 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
533 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
536 $keyedIds, 30, $genFunc, [
'lowTTL' => 0,
'lockTSE' => 5, ] + $extOpts );
537 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
538 $this->assertEquals( 1, $wasSet,
"Value not regenerated" );
541 $priorTime = microtime(
true );
547 $keyedIds =
new ArrayIterator( [ $keyB =>
'efef' ] );
549 $keyedIds, 30, $genFunc, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
551 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
552 $this->assertEquals( 1, $wasSet,
"Value regenerated due to check keys" );
553 $this->assertEquals(
$value, $priorValue,
"Has prior value" );
554 $this->assertInternalType(
'float', $priorAsOf,
"Has prior value" );
556 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check keys generated on miss' );
558 $this->assertGreaterThanOrEqual( $priorTime, $t2,
'Check keys generated on miss' );
563 $keyedIds =
new ArrayIterator( [ $keyC => 43636 ] );
565 $keyedIds, 30, $genFunc, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
567 $this->assertEquals(
$value, $v[$keyC],
"Value returned" );
568 $this->assertEquals( 1, $wasSet,
"Value regenerated due to still-recent check keys" );
570 $this->assertLessThanOrEqual( $priorTime, $t1,
'Check keys did not change again' );
572 $this->assertLessThanOrEqual( $priorTime, $t2,
'Check keys did not change again' );
575 $v =
$cache->
get( $keyC, $curTTL, [ $cKey1, $cKey2 ] );
577 $this->assertEquals(
$value, $v[$cache::VFLD_DATA],
"Value returned" );
579 $this->assertEquals(
$value, $v,
"Value returned" );
581 $this->assertLessThanOrEqual( 0, $curTTL,
"Value has current TTL < 0 due to check keys" );
585 $keyedIds =
new ArrayIterator( [ $key => 242424 ] );
587 $keyedIds, 30, $genFunc, [
'pcTTL' => 5 ] + $extOpts );
588 $this->assertEquals(
"@{$keyedIds[$key]}$", $v[$key],
"Value returned" );
590 $keyedIds =
new ArrayIterator( [ $key => 242424 ] );
592 $keyedIds, 30, $genFunc, [
'pcTTL' => 5 ] + $extOpts );
593 $this->assertEquals(
"@{$keyedIds[$key]}$", $v[$key],
"Value still returned after deleted" );
594 $this->assertEquals( 1, $wasSet,
"Value process cached while deleted" );
597 $ids = [ 1, 2, 3, 4, 5, 6 ];
599 return $wanCache->makeKey(
'test', $id );
602 $genFunc =
function ( $id, $oldValue, &$ttl,
array &$setops )
use ( &$calls ) {
610 [
"val-1",
"val-2",
"val-3",
"val-4",
"val-5",
"val-6" ],
611 array_values( $values ),
612 "Correct values in correct order"
615 array_map( $keyFunc, $ids, array_fill( 0,
count( $ids ), $this->
cache ) ),
616 array_keys( $values ),
617 "Correct keys in correct order"
619 $this->assertEquals(
count( $ids ), $calls );
622 $this->assertEquals(
count( $ids ), $calls,
"Values cached" );
626 ->setMethods( [
'getMulti' ] )->getMock();
627 $localBag->expects( $this->exactly( 1 ) )->method(
'getMulti' )->willReturn( [
631 $wanCache =
new WANObjectCache( [
'cache' => $localBag,
'pool' =>
'testcache-hash' ] );
634 $keyedIds =
new ArrayIterator( [
'k1' =>
'id1',
'k2' =>
'id2' ] );
636 [
'k1' =>
'val-id1',
'k2' =>
'val-id2' ],
637 $wanCache->getMultiWithSetCallback( $keyedIds, 10, $genFunc, [
'pcTTL' => 5 ] )
641 [
'k1' =>
'val-id1',
'k2' =>
'val-id2' ],
642 $wanCache->getMultiWithSetCallback( $keyedIds, 10, $genFunc, [
'pcTTL' => 5 ] )
649 [ [
'version' => 1 ],
true ]
671 &$wasSet, &$priorValue, &$priorAsOf
674 foreach ( $ids
as $id ) {
676 $newValues[$id] =
"@$id$";
684 $keyedIds =
new ArrayIterator( [ $keyA => 3353 ] );
687 $keyedIds, 30, $genFunc, $extOpts );
688 $this->assertEquals(
$value, $v[$keyA],
"Value returned" );
689 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
693 $this->assertLessThanOrEqual( 20, $curTTL,
'Current TTL between 19-20 (overriden)' );
694 $this->assertGreaterThanOrEqual( 19, $curTTL,
'Current TTL between 19-20 (overriden)' );
698 $keyedIds =
new ArrayIterator( [ $keyB =>
'efef' ] );
700 $keyedIds, 30, $genFunc, [
'lowTTL' => 0 ] + $extOpts );
701 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
702 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
705 $keyedIds, 30, $genFunc, [
'lowTTL' => 0 ] + $extOpts );
706 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
707 $this->assertEquals( 1, $wasSet,
"Value not regenerated" );
710 $priorTime = microtime(
true );
716 $keyedIds =
new ArrayIterator( [ $keyB =>
'efef' ] );
718 $keyedIds, 30, $genFunc, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
720 $this->assertEquals(
$value, $v[$keyB],
"Value returned" );
721 $this->assertEquals( 1, $wasSet,
"Value regenerated due to check keys" );
723 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check keys generated on miss' );
725 $this->assertGreaterThanOrEqual( $priorTime, $t2,
'Check keys generated on miss' );
730 $keyedIds =
new ArrayIterator( [ $keyC => 43636 ] );
732 $keyedIds, 30, $genFunc, [
'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
734 $this->assertEquals(
$value, $v[$keyC],
"Value returned" );
735 $this->assertEquals( 1, $wasSet,
"Value regenerated due to still-recent check keys" );
737 $this->assertLessThanOrEqual( $priorTime, $t1,
'Check keys did not change again' );
739 $this->assertLessThanOrEqual( $priorTime, $t2,
'Check keys did not change again' );
742 $v =
$cache->
get( $keyC, $curTTL, [ $cKey1, $cKey2 ] );
744 $this->assertEquals(
$value, $v[$cache::VFLD_DATA],
"Value returned" );
746 $this->assertEquals(
$value, $v,
"Value returned" );
748 $this->assertLessThanOrEqual( 0, $curTTL,
"Value has current TTL < 0 due to check keys" );
752 $keyedIds =
new ArrayIterator( [ $key => 242424 ] );
754 $keyedIds, 30, $genFunc, [
'pcTTL' => 5 ] + $extOpts );
755 $this->assertEquals(
"@{$keyedIds[$key]}$", $v[$key],
"Value returned" );
757 $keyedIds =
new ArrayIterator( [ $key => 242424 ] );
759 $keyedIds, 30, $genFunc, [
'pcTTL' => 5 ] + $extOpts );
760 $this->assertEquals(
"@{$keyedIds[$key]}$", $v[$key],
"Value still returned after deleted" );
761 $this->assertEquals( 1, $wasSet,
"Value process cached while deleted" );
764 $ids = [ 1, 2, 3, 4, 5, 6 ];
766 return $wanCache->makeKey(
'test', $id );
771 foreach ( $ids
as $id ) {
773 $newValues[$id] =
"val-{$id}";
781 [
"val-1",
"val-2",
"val-3",
"val-4",
"val-5",
"val-6" ],
782 array_values( $values ),
783 "Correct values in correct order"
786 array_map( $keyFunc, $ids, array_fill( 0,
count( $ids ), $this->
cache ) ),
787 array_keys( $values ),
788 "Correct keys in correct order"
790 $this->assertEquals(
count( $ids ), $calls );
793 $this->assertEquals(
count( $ids ), $calls,
"Values cached" );
799 [ [
'version' => 1 ],
true ]
820 $this->assertEquals( 1, $calls,
'Value was populated' );
823 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
827 [
'lockTSE' => 5,
'checkKeys' => $checkKeys ] );
828 $this->assertEquals(
$value,
$ret,
'Old value used' );
829 $this->assertEquals( 1, $calls,
'Callback was not used' );
833 [
'lockTSE' => 5,
'checkKeys' => $checkKeys ] );
834 $this->assertEquals(
$value,
$ret,
'Callback was used; interim saved' );
835 $this->assertEquals( 2, $calls,
'Callback was used; interim saved' );
838 [
'lockTSE' => 5,
'checkKeys' => $checkKeys ] );
839 $this->assertEquals(
$value,
$ret,
'Callback was not used; used interim (mutex failed)' );
840 $this->assertEquals( 2, $calls,
'Callback was not used; used interim (mutex failed)' );
854 $func =
function ( $oldValue, &$ttl, &$setOpts )
use ( &$calls,
$value,
$cache, $key ) {
856 $setOpts[
'since'] = microtime(
true ) - 10;
866 $this->assertEquals(
$value,
$cache->
get( $key, $curTTL ),
'Value was populated' );
867 $this->assertLessThan( 0, $curTTL,
'Value has negative curTTL' );
868 $this->assertEquals( 1, $calls,
'Value was generated' );
871 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
874 $this->assertEquals( 1, $calls,
'Callback was not used' );
897 $this->assertEquals( 1, $calls,
'Value was populated' );
900 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
904 [
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
905 $this->assertEquals(
$value,
$ret,
'Callback used' );
906 $this->assertEquals( 2, $calls,
'Callback used' );
909 [
'lockTSE' => 30,
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
910 $this->assertEquals(
$value,
$ret,
'Old value used' );
911 $this->assertEquals( 2, $calls,
'Callback was not used' );
915 [
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
916 $this->assertEquals( $busyValue,
$ret,
'Callback was not used; used busy value' );
917 $this->assertEquals( 2, $calls,
'Callback was not used; used busy value' );
919 $this->internalCache->delete( $cache::MUTEX_KEY_PREFIX . $key );
921 [
'lockTSE' => 30,
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
922 $this->assertEquals(
$value,
$ret,
'Callback was used; saved interim' );
923 $this->assertEquals( 3, $calls,
'Callback was used; saved interim' );
925 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
927 [
'busyValue' => $busyValue,
'checkKeys' => $checkKeys ] );
928 $this->assertEquals(
$value,
$ret,
'Callback was not used; used interim' );
929 $this->assertEquals( 3, $calls,
'Callback was not used; used interim' );
938 $value1 = [
'this' =>
'is',
'a' =>
'test' ];
939 $value2 = [
'this' =>
'is',
'another' =>
'test' ];
950 [ $key1 => $value1, $key2 => $value2 ],
952 'Result array populated'
955 $this->assertEquals( 2,
count( $curTTLs ),
"Two current TTLs in array" );
956 $this->assertGreaterThan( 0, $curTTLs[$key1],
"Key 1 has current TTL > 0" );
957 $this->assertGreaterThan( 0, $curTTLs[$key2],
"Key 2 has current TTL > 0" );
962 $priorTime = microtime(
true );
969 [ $key1 => $value1, $key2 => $value2 ],
970 $cache->
getMulti( [ $key1, $key2, $key3 ], $curTTLs, [ $cKey1, $cKey2 ] ),
971 "Result array populated even with new check keys"
974 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check key 1 generated on miss' );
976 $this->assertGreaterThanOrEqual( $priorTime, $t2,
'Check key 2 generated on miss' );
977 $this->assertEquals( 2,
count( $curTTLs ),
"Current TTLs array set" );
978 $this->assertLessThanOrEqual( 0, $curTTLs[$key1],
'Key 1 has current TTL <= 0' );
979 $this->assertLessThanOrEqual( 0, $curTTLs[$key2],
'Key 2 has current TTL <= 0' );
985 [ $key1 => $value1, $key2 => $value2 ],
986 $cache->
getMulti( [ $key1, $key2, $key3 ], $curTTLs, [ $cKey1, $cKey2 ] ),
987 "Result array still populated even with new check keys"
989 $this->assertEquals( 2,
count( $curTTLs ),
"Current TTLs still array set" );
990 $this->assertLessThan( 0, $curTTLs[$key1],
'Key 1 has negative current TTL' );
991 $this->assertLessThan( 0, $curTTLs[$key2],
'Key 2 has negative current TTL' );
1012 foreach ( [ $checkAll, $check1, $check2 ]
as $checkKey ) {
1028 $this->assertEquals(
1029 [
'key1' => $value1,
'key2' => $value2 ],
1033 $this->assertGreaterThanOrEqual( 9.5, $curTTLs[
'key1'],
'Initial ttls' );
1034 $this->assertLessThanOrEqual( 10.5, $curTTLs[
'key1'],
'Initial ttls' );
1035 $this->assertGreaterThanOrEqual( 9.5, $curTTLs[
'key2'],
'Initial ttls' );
1036 $this->assertLessThanOrEqual( 10.5, $curTTLs[
'key2'],
'Initial ttls' );
1048 $this->assertEquals(
1049 [
'key1' => $value1,
'key2' => $value2 ],
1051 'key1 expired by check1, but value still provided'
1053 $this->assertLessThan( 0, $curTTLs[
'key1'],
'key1 TTL expired' );
1054 $this->assertGreaterThan( 0, $curTTLs[
'key2'],
'key2 still valid' );
1065 $this->assertEquals(
1066 [
'key1' => $value1,
'key2' => $value2 ],
1068 'All keys expired by checkAll, but value still provided'
1070 $this->assertLessThan( 0, $curTTLs[
'key1'],
'key1 expired by checkAll' );
1071 $this->assertLessThan( 0, $curTTLs[
'key2'],
'key2 expired by checkAll' );
1081 for ( $i = 0; $i < 500; ++$i ) {
1085 $cache->
get( $key, $curTTL, [ $checkKey ] );
1088 $v =
$cache->
get( $key, $curTTL, [ $checkKey ] );
1090 $this->assertEquals(
'val', $v );
1091 $this->assertLessThan( 0, $curTTL,
"Step $i: CTL < 0 (miss/set/hit)" );
1094 for ( $i = 0; $i < 500; ++$i ) {
1100 $v =
$cache->
get( $key, $curTTL, [ $checkKey ] );
1102 $this->assertEquals(
'val', $v );
1103 $this->assertLessThan( 0, $curTTL,
"Step $i: CTL < 0 (set/hit)" );
1118 $v = $this->
cache->get( $key, $curTTL );
1119 $this->assertEquals(
$value, $v,
"Key was created with value" );
1120 $this->assertGreaterThan( 0, $curTTL,
"Existing key has current TTL > 0" );
1122 $this->
cache->delete( $key );
1125 $v = $this->
cache->get( $key, $curTTL );
1126 $this->assertFalse( $v,
"Deleted key has false value" );
1127 $this->assertLessThan( 0, $curTTL,
"Deleted key has current TTL < 0" );
1130 $v = $this->
cache->get( $key, $curTTL );
1131 $this->assertFalse( $v,
"Deleted key is tombstoned and has false value" );
1132 $this->assertLessThan( 0, $curTTL,
"Deleted key is tombstoned and has current TTL < 0" );
1138 $v = $this->
cache->get( $key, $curTTL );
1139 $this->assertFalse( $v,
"Deleted key has false value" );
1140 $this->assertNull( $curTTL,
"Deleted key has null current TTL" );
1143 $v = $this->
cache->get( $key, $curTTL );
1144 $this->assertEquals(
$value, $v,
"Key was created with value" );
1145 $this->assertGreaterThan( 0, $curTTL,
"Existing key has current TTL > 0" );
1163 $funcV1 =
function ()
use ( &$wasSet, $valueV1 ) {
1169 $priorValue =
false;
1171 $funcV2 =
function ( $oldValue, &$ttl, $setOpts, $oldAsOf )
1172 use ( &$wasSet, $valueV2, &$priorValue, &$priorAsOf ) {
1173 $priorValue = $oldValue;
1174 $priorAsOf = $oldAsOf;
1183 $this->assertEquals( $valueV1, $v,
"Value returned" );
1184 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
1186 $this->assertEquals( 1, $wasSet,
"Value not regenerated" );
1187 $this->assertEquals( $valueV1, $v,
"Value not regenerated" );
1191 $verOpts = [
'version' => $extOpts[
'version'] + 1 ];
1194 $verOpts = [
'version' => 1 ];
1200 $this->assertEquals( $valueV2, $v,
"Value returned" );
1201 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
1202 $this->assertEquals(
false, $priorValue,
"Old value not given due to old format" );
1203 $this->assertEquals(
null, $priorAsOf,
"Old value not given due to old format" );
1207 $this->assertEquals( $valueV2, $v,
"Value not regenerated (secondary key)" );
1208 $this->assertEquals( 0, $wasSet,
"Value not regenerated (secondary key)" );
1216 $this->assertEquals( $valueV2, $v,
"Value returned" );
1217 $this->assertEquals( 1, $wasSet,
"Value regenerated" );
1220 $this->assertEquals( $valueV2, $v,
"Value not regenerated (main key)" );
1221 $this->assertEquals( 1, $wasSet,
"Value not regenerated (main key)" );
1227 [ [
'version' => 1 ],
true ]
1240 $func =
function ()
use ( &$wasCalled,
$value ) {
1251 $this->assertEquals( 1, $wasCalled,
'Value cached' );
1254 $this->assertEquals( 2, $wasCalled,
'Value regenerated (got mutex)' );
1256 $this->assertEquals( 3, $wasCalled,
'Value regenerated (got mutex)' );
1258 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
1260 $this->assertEquals( 3, $wasCalled,
'Value interim cached (failed mutex)' );
1261 $this->internalCache->delete( $cache::MUTEX_KEY_PREFIX . $key );
1269 $this->assertEquals( 1, $wasCalled,
'Value cached' );
1272 $this->assertEquals( 2, $wasCalled,
'Value regenerated (got mutex)' );
1274 $this->assertEquals( 3, $wasCalled,
'Value still regenerated (got mutex)' );
1276 $this->assertEquals( 4, $wasCalled,
'Value still regenerated (got mutex)' );
1278 $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
1280 $this->assertEquals( 5, $wasCalled,
'Value still regenerated (failed mutex)' );
1295 $priorTime = microtime(
true );
1300 $this->assertGreaterThanOrEqual( $priorTime, $t0,
'Check key auto-created' );
1302 $priorTime = $newTime;
1306 $this->assertGreaterThanOrEqual( $priorTime, $t1,
'Check key created' );
1309 $this->assertEquals( $t1, $t2,
'Check key time did not change' );
1314 $this->assertGreaterThan( $t2, $t3,
'Check key time increased' );
1317 $this->assertEquals( $t3, $t4,
'Check key time did not change' );
1322 $this->assertGreaterThan( $t4, $t5,
'Check key time increased' );
1325 $this->assertEquals( $t5, $t6,
'Check key time did not change' );
1338 $this->internalCache->set(
1342 $this->internalCache->set(
1346 $this->internalCache->set(
1353 $v = $this->
cache->get( $key, $curTTL, [ $tKey1, $tKey2 ] );
1354 $this->assertEquals(
$value, $v,
"Value matches" );
1355 $this->assertLessThan( -4.9, $curTTL,
"Correct CTL" );
1356 $this->assertGreaterThan( -5.1, $curTTL,
"Correct CTL" );
1370 $knownPurge = time() - 60;
1371 $goodTime = microtime(
true ) - 5;
1372 $badTime = microtime(
true ) - 300;
1374 $this->internalCache->set(
1383 $this->internalCache->set(
1392 $this->internalCache->set(
1396 $this->internalCache->set(
1401 $this->assertEquals(
$value, $this->
cache->get( $vKey1 ) );
1402 $this->assertEquals(
$value, $this->
cache->get( $vKey2 ) );
1403 $this->
cache->reap( $vKey1, $knownPurge, $bad1 );
1404 $this->
cache->reap( $vKey2, $knownPurge, $bad2 );
1406 $this->assertFalse( $bad1 );
1407 $this->assertTrue( $bad2 );
1409 $this->
cache->reapCheckKey( $tKey1, $knownPurge, $tBad1 );
1410 $this->
cache->reapCheckKey( $tKey2, $knownPurge, $tBad2 );
1411 $this->assertFalse( $tBad1 );
1412 $this->assertTrue( $tBad2 );
1420 ->setMethods( [
'get',
'changeTTL' ] )->getMock();
1421 $backend->expects( $this->once() )->method(
'get' )
1428 $backend->expects( $this->once() )->method(
'changeTTL' )
1429 ->willReturn(
false );
1432 'cache' => $backend,
1433 'pool' =>
'testcache-hash',
1438 $ret = $wanCache->reap(
'key', 360, $isStale );
1439 $this->assertTrue( $isStale,
'value was stale' );
1440 $this->assertFalse(
$ret,
'changeTTL failed' );
1450 $opts = [
'lag' => 300,
'since' => microtime(
true ) ];
1452 $this->assertEquals(
$value, $this->
cache->get( $key ),
"Rep-lagged value written." );
1455 $opts = [
'lag' => 0,
'since' => microtime(
true ) - 300 ];
1457 $this->assertEquals(
false, $this->
cache->get( $key ),
"Trx-lagged value not written." );
1460 $opts = [
'lag' => 5,
'since' => microtime(
true ) - 5 ];
1462 $this->assertEquals(
false, $this->
cache->get( $key ),
"Lagged value not written." );
1472 $opts = [
'pending' =>
true ];
1474 $this->assertEquals(
false, $this->
cache->get( $key ),
"Pending value not written." );
1479 ->setMethods( [
'set',
'delete' ] )->getMock();
1480 $localBag->expects( $this->never() )->method(
'set' );
1481 $localBag->expects( $this->never() )->method(
'delete' );
1483 'cache' => $localBag,
1484 'pool' =>
'testcache-hash',
1486 'mcrouterAware' =>
true,
1487 'region' =>
'pmtpa',
1488 'cluster' =>
'mw-wan'
1490 $valFunc =
function () {
1495 $wanCache->get(
'x' );
1496 $wanCache->get(
'x', $ctl, [
'check1' ] );
1497 $wanCache->getMulti( [
'x',
'y' ] );
1498 $wanCache->getMulti( [
'x',
'y' ], $ctls, [
'check2' ] );
1499 $wanCache->getWithSetCallback(
'p', 30, $valFunc );
1500 $wanCache->getCheckKeyTime(
'zzz' );
1501 $wanCache->reap(
'x', time() - 300 );
1502 $wanCache->reap(
'zzz', time() - 300 );
1507 ->setMethods( [
'set' ] )->getMock();
1509 'cache' => $localBag,
1510 'pool' =>
'testcache-hash',
1512 'mcrouterAware' =>
true,
1513 'region' =>
'pmtpa',
1514 'cluster' =>
'mw-wan'
1517 $localBag->expects( $this->once() )->method(
'set' )
1518 ->with(
"/*/mw-wan/" . $wanCache::VALUE_KEY_PREFIX .
"test" );
1520 $wanCache->delete(
'test' );
1525 ->setMethods( [
'set' ] )->getMock();
1527 'cache' => $localBag,
1528 'pool' =>
'testcache-hash',
1530 'mcrouterAware' =>
true,
1531 'region' =>
'pmtpa',
1532 'cluster' =>
'mw-wan'
1535 $localBag->expects( $this->once() )->method(
'set' )
1536 ->with(
"/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX .
"test" );
1538 $wanCache->touchCheckKey(
'test' );
1543 ->setMethods( [
'delete' ] )->getMock();
1545 'cache' => $localBag,
1546 'pool' =>
'testcache-hash',
1548 'mcrouterAware' =>
true,
1549 'region' =>
'pmtpa',
1550 'cluster' =>
'mw-wan'
1553 $localBag->expects( $this->once() )->method(
'delete' )
1554 ->with(
"/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX .
"test" );
1556 $wanCache->resetCheckKey(
'test' );
1569 $mtime = $ago ? time() - $ago : $ago;
1571 $ttl = $this->
cache->adaptiveTTL( $mtime, $maxTTL, $minTTL, $factor );
1573 $this->assertGreaterThanOrEqual( $adaptiveTTL - $margin, $ttl );
1574 $this->assertLessThanOrEqual( $adaptiveTTL + $margin, $ttl );
1576 $ttl = $this->
cache->adaptiveTTL( (
string)$mtime, $maxTTL, $minTTL, $factor );
1578 $this->assertGreaterThanOrEqual( $adaptiveTTL - $margin, $ttl );
1579 $this->assertLessThanOrEqual( $adaptiveTTL + $margin, $ttl );
1584 [ 3600, 900, 30, 0.2, 720 ],
1585 [ 3600, 500, 30, 0.2, 500 ],
1586 [ 3600, 86400, 800, 0.2, 800 ],
1587 [
false, 86400, 800, 0.2, 800 ],
1588 [
null, 86400, 800, 0.2, 800 ]
1597 $this->assertInstanceOf(
1607 $this->assertSame(
null, $this->
cache->setLogger(
new Psr\Log\NullLogger ) );
1615 ->setMethods( [
'getQoS' ] )->getMock();
1616 $backend->expects( $this->once() )->method(
'getQoS' )
1621 $wanCache::QOS_UNKNOWN,
1622 $wanCache->getQoS( $wanCache::ATTR_EMULATION )
1631 ->setMethods( [
'makeKey' ] )->getMock();
1632 $backend->expects( $this->once() )->method(
'makeKey' )
1633 ->willReturn(
'special' );
1636 'cache' => $backend,
1637 'pool' =>
'testcache-hash',
1641 $this->assertSame(
'special', $wanCache->makeKey(
'a',
'b' ) );
1649 ->setMethods( [
'makeGlobalKey' ] )->getMock();
1650 $backend->expects( $this->once() )->method(
'makeGlobalKey' )
1651 ->willReturn(
'special' );
1654 'cache' => $backend,
1655 'pool' =>
'testcache-hash',
1659 $this->assertSame(
'special', $wanCache->makeGlobalKey(
'a',
'b' ) );
1664 [
'domain:page:5',
'page' ],
1665 [
'domain:main-key',
'main-key' ],
1666 [
'domain:page:history',
'page' ],
1667 [
'missingdomainkey',
'missingdomainkey' ]
1676 $wanCache = TestingAccessWrapper::newFromObject(
new WANObjectCache( [
1678 'pool' =>
'testcache-hash',
1682 $this->assertEquals( $class, $wanCache->determineKeyClass( $key ) );
1690 $this->timeOverride =
$time;
1694 return $this->timeOverride ?: parent::getCurrentTime();
1702 $this->timeOverride =
$time;
1709 $this->timeOverride +=
$time;
1711 $this->
cache->setTime( $this->timeOverride );
1718 return $this->timeOverride ?: parent::getCurrentTime();
1726 return ( $curTTL > 0 && ( $curTTL + self::CLOCK_SKEW ) < $lowTTL );
1732 return ( ( $now - $asOf ) > $timeTillRefresh );