Add "generic" key methods for quickly deriving keys from key component lists in a bijective manor. This is useful for BagOStuff classes that wrap other BagOStuff instances or for parsing keys to get stats. Make the proxy BagOStuff classes (ReplicatedBagOStuff, MultiWriteBagOStuff, CachedBagOStuff) use "generic" keys so that they can convert to appropriate keys when making backing cache instance method calls. Make EmptyBagOStuff, HashBagOStuff, APCUBagOStuff, RedisBagOStuff, and RESTBagOStuff use "generic" keys rather than those of MediumSpecificBagOStuff::makeKeyInternal(). This lets proxy BagOStuff classes bypass key conversions when used with instances of these classes as backing stores. Also: * Fix missing incr(), incrWithInit(), and decr() return values in MultiWriteBagOStuff. * Make MultiWriteBagOfStuff, ReplicatedBagOStuff, and CachedBagOStuff use similar backend method forwarding styles by using a new BagOStuff method. * Improved various related bits of documentation. Bug: T250239 Bug: T235705 Change-Id: I1eb897c2cea3f5b756dd1e3c457b7cbd817599f5
161 lines
4.5 KiB
PHP
161 lines
4.5 KiB
PHP
<?php
|
|
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
/**
|
|
* @group BagOStuff
|
|
*/
|
|
class CachedBagOStuffTest extends PHPUnit\Framework\TestCase {
|
|
|
|
use MediaWikiCoversValidator;
|
|
|
|
/**
|
|
* @covers CachedBagOStuff::__construct
|
|
* @covers CachedBagOStuff::get
|
|
*/
|
|
public function testGetFromBackend() {
|
|
$backend = new HashBagOStuff;
|
|
$cache = new CachedBagOStuff( $backend );
|
|
|
|
$backend->set( 'foo', 'bar' );
|
|
$this->assertEquals( 'bar', $cache->get( 'foo' ) );
|
|
|
|
$backend->set( 'foo', 'baz' );
|
|
$this->assertEquals( 'bar', $cache->get( 'foo' ), 'cached' );
|
|
}
|
|
|
|
/**
|
|
* @covers CachedBagOStuff::set
|
|
* @covers CachedBagOStuff::delete
|
|
*/
|
|
public function testSetAndDelete() {
|
|
$backend = new HashBagOStuff;
|
|
$cache = new CachedBagOStuff( $backend );
|
|
|
|
for ( $i = 0; $i < 10; $i++ ) {
|
|
$cache->set( "key$i", 1 );
|
|
$this->assertSame( 1, $cache->get( "key$i" ) );
|
|
$this->assertSame( 1, $backend->get( "key$i" ) );
|
|
|
|
$cache->delete( "key$i" );
|
|
$this->assertFalse( $cache->get( "key$i" ) );
|
|
$this->assertFalse( $backend->get( "key$i" ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @covers CachedBagOStuff::set
|
|
* @covers CachedBagOStuff::delete
|
|
*/
|
|
public function testWriteCacheOnly() {
|
|
$backend = new HashBagOStuff;
|
|
$cache = new CachedBagOStuff( $backend );
|
|
|
|
$cache->set( 'foo', 'bar', 0, CachedBagOStuff::WRITE_CACHE_ONLY );
|
|
$this->assertEquals( 'bar', $cache->get( 'foo' ) );
|
|
$this->assertFalse( $backend->get( 'foo' ) );
|
|
|
|
$cache->set( 'foo', 'old' );
|
|
$this->assertEquals( 'old', $cache->get( 'foo' ) );
|
|
$this->assertEquals( 'old', $backend->get( 'foo' ) );
|
|
|
|
$cache->set( 'foo', 'new', 0, CachedBagOStuff::WRITE_CACHE_ONLY );
|
|
$this->assertEquals( 'new', $cache->get( 'foo' ) );
|
|
$this->assertEquals( 'old', $backend->get( 'foo' ) );
|
|
|
|
$cache->delete( 'foo', CachedBagOStuff::WRITE_CACHE_ONLY );
|
|
$this->assertEquals( 'old', $cache->get( 'foo' ) ); // Reloaded from backend
|
|
}
|
|
|
|
/**
|
|
* @covers CachedBagOStuff::get
|
|
*/
|
|
public function testCacheBackendMisses() {
|
|
$backend = new HashBagOStuff;
|
|
$cache = new CachedBagOStuff( $backend );
|
|
|
|
// First hit primes the cache with miss from the backend
|
|
$this->assertFalse( $cache->get( 'foo' ) );
|
|
|
|
// Change the value in the backend
|
|
$backend->set( 'foo', true );
|
|
|
|
// Second hit returns the cached miss
|
|
$this->assertFalse( $cache->get( 'foo' ) );
|
|
|
|
// But a fresh value is read from the backend
|
|
$backend->set( 'bar', true );
|
|
$this->assertTrue( $cache->get( 'bar' ) );
|
|
}
|
|
|
|
/**
|
|
* @covers CachedBagOStuff::setDebug
|
|
*/
|
|
public function testSetDebug() {
|
|
$backend = new HashBagOStuff();
|
|
$cache = new CachedBagOStuff( $backend );
|
|
// Access private property 'debugMode'
|
|
$backend = TestingAccessWrapper::newFromObject( $backend );
|
|
$cache = TestingAccessWrapper::newFromObject( $cache );
|
|
$this->assertFalse( $backend->debugMode );
|
|
$this->assertFalse( $cache->debugMode );
|
|
|
|
$cache->setDebug( true );
|
|
// Should have set both
|
|
$this->assertTrue( $backend->debugMode, 'sets backend' );
|
|
$this->assertTrue( $cache->debugMode, 'sets self' );
|
|
}
|
|
|
|
/**
|
|
* @covers CachedBagOStuff::deleteObjectsExpiringBefore
|
|
*/
|
|
public function testExpire() {
|
|
$backend = $this->getMockBuilder( HashBagOStuff::class )
|
|
->setMethods( [ 'deleteObjectsExpiringBefore' ] )
|
|
->getMock();
|
|
$backend->expects( $this->once() )
|
|
->method( 'deleteObjectsExpiringBefore' )
|
|
->willReturn( false );
|
|
|
|
$cache = new CachedBagOStuff( $backend );
|
|
$cache->deleteObjectsExpiringBefore( '20110401000000' );
|
|
}
|
|
|
|
/**
|
|
* @covers CachedBagOStuff::makeKey
|
|
*/
|
|
public function testMakeKey() {
|
|
$backend = $this->getMockBuilder( HashBagOStuff::class )
|
|
->setConstructorArgs( [ [ 'keyspace' => 'magic' ] ] )
|
|
->setMethods( [ 'makeKey' ] )
|
|
->getMock();
|
|
$backend->method( 'makeKey' )
|
|
->willReturn( 'special/logic' );
|
|
|
|
$cache = new CachedBagOStuff( $backend );
|
|
|
|
$this->assertSame( 'special/logic', $backend->makeKey( 'special', 'logic' ) );
|
|
$this->assertSame(
|
|
'magic:special:logic',
|
|
$cache->makeKey( 'special', 'logic' ),
|
|
"Backend keyspace used"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @covers CachedBagOStuff::makeGlobalKey
|
|
*/
|
|
public function testMakeGlobalKey() {
|
|
$backend = $this->getMockBuilder( HashBagOStuff::class )
|
|
->setConstructorArgs( [ [ 'keyspace' => 'magic' ] ] )
|
|
->setMethods( [ 'makeGlobalKey' ] )
|
|
->getMock();
|
|
$backend->method( 'makeGlobalKey' )
|
|
->willReturn( 'special/logic' );
|
|
|
|
$cache = new CachedBagOStuff( $backend );
|
|
|
|
$this->assertSame( 'special/logic', $backend->makeGlobalKey( 'special', 'logic' ) );
|
|
$this->assertSame( 'global:special:logic', $cache->makeGlobalKey( 'special', 'logic' ) );
|
|
}
|
|
}
|