objectcache: make use of new modtoken field in SqlBagOStuff
Add a multi-primary mode option that supports MySQL DB setups that use circular replication with STATEMENT formatted binlogs. The `modtoken` column is only used when multi-primary mode is explicitly enabled in configuration. The column is used by write queries to determine the "winning" version of keys, with the goal of approximating "Last-Write-Wins" eventual consistency. Writes with different timestamps can be handled by picking the one with the highest timestamp as the "winner". Writes with the same timestamp, from different primary DBs, can be handled by picking the one from the primary DB with the highest server_id. Writes with the same token timestamp from the same primary DB can be handled by picking the last write to appear in the binlog. The delete() operation uses tombstones in multi-primary mode, since there must be a key version to actually compare with the versions from other operations. Also: * Remove "LOCK IN SHARE MODE" that was made obsolete by the CONN_TRX_AUTOCOMMIT flag. For the SQLite transaction case, it is serializable anyway. * Simplified handleWriteError() to match handleReadError() and merged them into handleDBError(). Bug: T274174 Change-Id: Icc5eff9a032dd3403b5718058f20e38f8ea84af5
This commit is contained in:
parent
579b057f29
commit
d56a686f83
3 changed files with 1062 additions and 432 deletions
|
|
@ -51,7 +51,7 @@ use Wikimedia\ScopedCallback;
|
||||||
* among datacenters.
|
* among datacenters.
|
||||||
*
|
*
|
||||||
* Subclasses should override the default "segmentationSize" field with an appropriate value.
|
* Subclasses should override the default "segmentationSize" field with an appropriate value.
|
||||||
* The value should not be larger than what the storage backend (by default) supports. It also
|
* The value should not be larger than what the backing store (by default) supports. It also
|
||||||
* should be roughly informed by common performance bottlenecks (e.g. values over a certain size
|
* should be roughly informed by common performance bottlenecks (e.g. values over a certain size
|
||||||
* having poor scalability). The same goes for the "segmentedValueMaxSize" member, which limits
|
* having poor scalability). The same goes for the "segmentedValueMaxSize" member, which limits
|
||||||
* the maximum size and chunk count (indirectly) of values.
|
* the maximum size and chunk count (indirectly) of values.
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -13,14 +13,16 @@ abstract class BagOStuffTestBase extends MediaWikiIntegrationTestCase {
|
||||||
private $cache;
|
private $cache;
|
||||||
|
|
||||||
private const TEST_KEY = 'test';
|
private const TEST_KEY = 'test';
|
||||||
|
private const TEST_TIME = 1563892142;
|
||||||
|
|
||||||
protected function setUp(): void {
|
protected function setUp(): void {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->cache = $this->newCacheInstance();
|
$this->cache = $this->newCacheInstance();
|
||||||
|
$this->cache->deleteMulti( [
|
||||||
$this->cache->delete( $this->cache->makeKey( self::TEST_KEY ) );
|
$this->cache->makeKey( self::TEST_KEY ),
|
||||||
$this->cache->delete( $this->cache->makeKey( self::TEST_KEY ) . ':lock' );
|
$this->cache->makeKey( self::TEST_KEY ) . ':lock'
|
||||||
|
] );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -123,9 +125,6 @@ abstract class BagOStuffTestBase extends MediaWikiIntegrationTestCase {
|
||||||
* @covers MediumSpecificBagOStuff::changeTTL
|
* @covers MediumSpecificBagOStuff::changeTTL
|
||||||
*/
|
*/
|
||||||
public function testChangeTTLRenew() {
|
public function testChangeTTLRenew() {
|
||||||
$now = microtime( true ); // need real time
|
|
||||||
$this->cache->setMockTime( $now );
|
|
||||||
|
|
||||||
$key = $this->cache->makeKey( self::TEST_KEY );
|
$key = $this->cache->makeKey( self::TEST_KEY );
|
||||||
$value = 'meow';
|
$value = 'meow';
|
||||||
|
|
||||||
|
|
@ -144,39 +143,36 @@ abstract class BagOStuffTestBase extends MediaWikiIntegrationTestCase {
|
||||||
* @covers MediumSpecificBagOStuff::changeTTL
|
* @covers MediumSpecificBagOStuff::changeTTL
|
||||||
*/
|
*/
|
||||||
public function testChangeTTLExpireRel() {
|
public function testChangeTTLExpireRel() {
|
||||||
$now = microtime( true ); // need real time
|
|
||||||
$this->cache->setMockTime( $now );
|
|
||||||
|
|
||||||
$key = $this->cache->makeKey( self::TEST_KEY );
|
$key = $this->cache->makeKey( self::TEST_KEY );
|
||||||
$value = 'meow';
|
$value = 'meow';
|
||||||
|
|
||||||
$this->cache->add( $key, $value, 5 );
|
$this->cache->add( $key, $value, 5 );
|
||||||
|
$this->assertSame( $value, $this->cache->get( $key ) );
|
||||||
$this->assertTrue( $this->cache->changeTTL( $key, -3600 ) );
|
$this->assertTrue( $this->cache->changeTTL( $key, -3600 ) );
|
||||||
$this->assertFalse( $this->cache->get( $key ) );
|
$this->assertFalse( $this->cache->get( $key ) );
|
||||||
|
$this->assertFalse( $this->cache->changeTTL( $key, -3600 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers MediumSpecificBagOStuff::changeTTL
|
* @covers MediumSpecificBagOStuff::changeTTL
|
||||||
*/
|
*/
|
||||||
public function testChangeTTLExpireAbs() {
|
public function testChangeTTLExpireAbs() {
|
||||||
$now = microtime( true ); // need real time
|
|
||||||
$this->cache->setMockTime( $now );
|
|
||||||
|
|
||||||
$key = $this->cache->makeKey( self::TEST_KEY );
|
$key = $this->cache->makeKey( self::TEST_KEY );
|
||||||
$value = 'meow';
|
$value = 'meow';
|
||||||
|
|
||||||
$this->cache->add( $key, $value, 5 );
|
$this->cache->add( $key, $value, 5 );
|
||||||
$this->assertTrue( $this->cache->changeTTL( $key, $now - 3600 ) );
|
$this->assertSame( $value, $this->cache->get( $key ) );
|
||||||
|
|
||||||
|
$now = $this->cache->getCurrentTime();
|
||||||
|
$this->assertTrue( $this->cache->changeTTL( $key, (int)$now - 3600 ) );
|
||||||
$this->assertFalse( $this->cache->get( $key ) );
|
$this->assertFalse( $this->cache->get( $key ) );
|
||||||
|
$this->assertFalse( $this->cache->changeTTL( $key, (int)$now - 3600 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers MediumSpecificBagOStuff::changeTTLMulti
|
* @covers MediumSpecificBagOStuff::changeTTLMulti
|
||||||
*/
|
*/
|
||||||
public function testChangeTTLMulti() {
|
public function testChangeTTLMulti() {
|
||||||
$now = 1563892142;
|
|
||||||
$this->cache->setMockTime( $now );
|
|
||||||
|
|
||||||
$key1 = $this->cache->makeKey( 'test-key1' );
|
$key1 = $this->cache->makeKey( 'test-key1' );
|
||||||
$key2 = $this->cache->makeKey( 'test-key2' );
|
$key2 = $this->cache->makeKey( 'test-key2' );
|
||||||
$key3 = $this->cache->makeKey( 'test-key3' );
|
$key3 = $this->cache->makeKey( 'test-key3' );
|
||||||
|
|
@ -206,11 +202,11 @@ abstract class BagOStuffTestBase extends MediaWikiIntegrationTestCase {
|
||||||
$this->assertFalse( $ok, "One key missing" );
|
$this->assertFalse( $ok, "One key missing" );
|
||||||
$this->assertSame( 1, $this->cache->get( $key1 ), "Key still live" );
|
$this->assertSame( 1, $this->cache->get( $key1 ), "Key still live" );
|
||||||
|
|
||||||
$now = microtime( true ); // real time
|
|
||||||
$ok = $this->cache->setMulti( [ $key1 => 1, $key2 => 2, $key3 => 3 ] );
|
$ok = $this->cache->setMulti( [ $key1 => 1, $key2 => 2, $key3 => 3 ] );
|
||||||
$this->assertTrue( $ok, "setMulti() succeeded" );
|
$this->assertTrue( $ok, "setMulti() succeeded" );
|
||||||
|
|
||||||
$ok = $this->cache->changeTTLMulti( [ $key1, $key2, $key3 ], $now + 86400 );
|
$now = $this->cache->getCurrentTime();
|
||||||
|
$ok = $this->cache->changeTTLMulti( [ $key1, $key2, $key3 ], (int)$now + 86400 );
|
||||||
$this->assertTrue( $ok, "Expiry set for all keys" );
|
$this->assertTrue( $ok, "Expiry set for all keys" );
|
||||||
$this->assertSame( 1, $this->cache->get( $key1 ), "Key still live" );
|
$this->assertSame( 1, $this->cache->get( $key1 ), "Key still live" );
|
||||||
|
|
||||||
|
|
@ -240,7 +236,7 @@ abstract class BagOStuffTestBase extends MediaWikiIntegrationTestCase {
|
||||||
|
|
||||||
$key = $this->cache->makeKey( self::TEST_KEY );
|
$key = $this->cache->makeKey( self::TEST_KEY );
|
||||||
$this->cache->add( $key, $value, 5 );
|
$this->cache->add( $key, $value, 5 );
|
||||||
$this->assertEquals( $this->cache->get( $key ), $value );
|
$this->assertSame( $this->cache->get( $key ), $value );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -249,7 +245,7 @@ abstract class BagOStuffTestBase extends MediaWikiIntegrationTestCase {
|
||||||
* @covers MediumSpecificBagOStuff::getWithSetCallback
|
* @covers MediumSpecificBagOStuff::getWithSetCallback
|
||||||
*/
|
*/
|
||||||
public function testGetWithSetCallback() {
|
public function testGetWithSetCallback() {
|
||||||
$now = 1563892142;
|
$now = self::TEST_TIME;
|
||||||
$cache = new HashBagOStuff( [] );
|
$cache = new HashBagOStuff( [] );
|
||||||
$cache->setMockTime( $now );
|
$cache->setMockTime( $now );
|
||||||
$key = $cache->makeKey( self::TEST_KEY );
|
$key = $cache->makeKey( self::TEST_KEY );
|
||||||
|
|
@ -504,11 +500,4 @@ abstract class BagOStuffTestBase extends MediaWikiIntegrationTestCase {
|
||||||
$this->assertTrue( $this->cache->unlock( $key2 ) );
|
$this->assertTrue( $this->cache->unlock( $key2 ) );
|
||||||
$this->assertTrue( $this->cache->unlock( $key2 ) );
|
$this->assertTrue( $this->cache->unlock( $key2 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function tearDown(): void {
|
|
||||||
$this->cache->delete( $this->cache->makeKey( self::TEST_KEY ) );
|
|
||||||
$this->cache->delete( $this->cache->makeKey( self::TEST_KEY ) . ':lock' );
|
|
||||||
|
|
||||||
parent::tearDown();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue