MediaWiki  1.31.0
LoadBalancerTest.php
Go to the documentation of this file.
1 <?php
2 
29 
35  private function makeServerConfig() {
37 
38  return [
39  'host' => $wgDBserver,
40  'dbname' => $wgDBname,
41  'tablePrefix' => $this->dbPrefix(),
42  'user' => $wgDBuser,
43  'password' => $wgDBpassword,
44  'type' => $wgDBtype,
45  'dbDirectory' => $wgSQLiteDataDir,
46  'load' => 0,
47  'flags' => DBO_TRX // REPEATABLE-READ for consistency
48  ];
49  }
50 
51  public function testWithoutReplica() {
53 
54  $called = false;
55  $lb = new LoadBalancer( [
56  'servers' => [ $this->makeServerConfig() ],
57  'queryLogger' => MediaWiki\Logger\LoggerFactory::getInstance( 'DBQuery' ),
58  'localDomain' => new DatabaseDomain( $wgDBname, null, $this->dbPrefix() ),
59  'chronologyCallback' => function () use ( &$called ) {
60  $called = true;
61  }
62  ] );
63 
64  $ld = DatabaseDomain::newFromId( $lb->getLocalDomainID() );
65  $this->assertEquals( $wgDBname, $ld->getDatabase(), 'local domain DB set' );
66  $this->assertEquals( $this->dbPrefix(), $ld->getTablePrefix(), 'local domain prefix set' );
67 
68  $this->assertFalse( $called );
69  $dbw = $lb->getConnection( DB_MASTER );
70  $this->assertTrue( $called );
71  $this->assertTrue( $dbw->getLBInfo( 'master' ), 'master shows as master' );
72  $this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on master" );
73  $this->assertWriteAllowed( $dbw );
74 
75  $dbr = $lb->getConnection( DB_REPLICA );
76  $this->assertTrue( $dbr->getLBInfo( 'master' ), 'DB_REPLICA also gets the master' );
77  $this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on replica" );
78 
79  if ( !$lb->getServerAttributes( $lb->getWriterIndex() )[$dbw::ATTR_DB_LEVEL_LOCKING] ) {
80  $dbwAuto = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTOCOMMIT );
81  $this->assertFalse(
82  $dbwAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTOCOMMIT" );
83  $this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on master" );
84  $this->assertNotEquals(
85  $dbw, $dbwAuto, "CONN_TRX_AUTOCOMMIT uses separate connection" );
86 
87  $dbrAuto = $lb->getConnection( DB_REPLICA, [], false, $lb::CONN_TRX_AUTOCOMMIT );
88  $this->assertFalse(
89  $dbrAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTOCOMMIT" );
90  $this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on replica" );
91  $this->assertNotEquals(
92  $dbr, $dbrAuto, "CONN_TRX_AUTOCOMMIT uses separate connection" );
93 
94  $dbwAuto2 = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTOCOMMIT );
95  $this->assertEquals( $dbwAuto2, $dbwAuto, "CONN_TRX_AUTOCOMMIT reuses connections" );
96  }
97 
98  $lb->closeAll();
99  }
100 
101  public function testWithReplica() {
103 
104  $servers = [
105  [ // master
106  'host' => $wgDBserver,
107  'dbname' => $wgDBname,
108  'tablePrefix' => $this->dbPrefix(),
109  'user' => $wgDBuser,
110  'password' => $wgDBpassword,
111  'type' => $wgDBtype,
112  'dbDirectory' => $wgSQLiteDataDir,
113  'load' => 0,
114  'flags' => DBO_TRX // REPEATABLE-READ for consistency
115  ],
116  [ // emulated replica
117  'host' => $wgDBserver,
118  'dbname' => $wgDBname,
119  'tablePrefix' => $this->dbPrefix(),
120  'user' => $wgDBuser,
121  'password' => $wgDBpassword,
122  'type' => $wgDBtype,
123  'dbDirectory' => $wgSQLiteDataDir,
124  'load' => 100,
125  'flags' => DBO_TRX // REPEATABLE-READ for consistency
126  ]
127  ];
128 
129  $lb = new LoadBalancer( [
130  'servers' => $servers,
131  'localDomain' => new DatabaseDomain( $wgDBname, null, $this->dbPrefix() ),
132  'queryLogger' => MediaWiki\Logger\LoggerFactory::getInstance( 'DBQuery' ),
133  'loadMonitorClass' => LoadMonitorNull::class
134  ] );
135 
136  $dbw = $lb->getConnection( DB_MASTER );
137  $this->assertTrue( $dbw->getLBInfo( 'master' ), 'master shows as master' );
138  $this->assertEquals(
139  ( $wgDBserver != '' ) ? $wgDBserver : 'localhost',
140  $dbw->getLBInfo( 'clusterMasterHost' ),
141  'cluster master set' );
142  $this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on master" );
143  $this->assertWriteAllowed( $dbw );
144 
145  $dbr = $lb->getConnection( DB_REPLICA );
146  $this->assertTrue( $dbr->getLBInfo( 'replica' ), 'replica shows as replica' );
147  $this->assertEquals(
148  ( $wgDBserver != '' ) ? $wgDBserver : 'localhost',
149  $dbr->getLBInfo( 'clusterMasterHost' ),
150  'cluster master set' );
151  $this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on replica" );
152  $this->assertWriteForbidden( $dbr );
153 
154  if ( !$lb->getServerAttributes( $lb->getWriterIndex() )[$dbw::ATTR_DB_LEVEL_LOCKING] ) {
155  $dbwAuto = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTOCOMMIT );
156  $this->assertFalse(
157  $dbwAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTOCOMMIT" );
158  $this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on master" );
159  $this->assertNotEquals(
160  $dbw, $dbwAuto, "CONN_TRX_AUTOCOMMIT uses separate connection" );
161 
162  $dbrAuto = $lb->getConnection( DB_REPLICA, [], false, $lb::CONN_TRX_AUTOCOMMIT );
163  $this->assertFalse(
164  $dbrAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTOCOMMIT" );
165  $this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on replica" );
166  $this->assertNotEquals(
167  $dbr, $dbrAuto, "CONN_TRX_AUTOCOMMIT uses separate connection" );
168 
169  $dbwAuto2 = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTOCOMMIT );
170  $this->assertEquals( $dbwAuto2, $dbwAuto, "CONN_TRX_AUTOCOMMIT reuses connections" );
171  }
172 
173  $lb->closeAll();
174  }
175 
176  private function assertWriteForbidden( Database $db ) {
177  try {
178  $db->delete( 'some_table', [ 'id' => 57634126 ], __METHOD__ );
179  $this->fail( 'Write operation should have failed!' );
180  } catch ( DBError $ex ) {
181  // check that the exception message contains "Write operation"
182  $constraint = new PHPUnit_Framework_Constraint_StringContains( 'Write operation' );
183 
184  if ( !$constraint->evaluate( $ex->getMessage(), '', true ) ) {
185  // re-throw original error, to preserve stack trace
186  throw $ex;
187  }
188  }
189  }
190 
191  private function assertWriteAllowed( Database $db ) {
192  $table = $db->tableName( 'some_table' );
193  try {
194  $db->dropTable( 'some_table' ); // clear for sanity
195 
196  // Trigger DBO_TRX to create a transaction so the flush below will
197  // roll everything here back in sqlite. But don't actually do the
198  // code below inside an atomic section becaue MySQL and Oracle
199  // auto-commit transactions for DDL statements like CREATE TABLE.
200  $db->startAtomic( __METHOD__ );
201  $db->endAtomic( __METHOD__ );
202 
203  // Use only basic SQL and trivial types for these queries for compatibility
204  $this->assertNotSame(
205  false,
206  $db->query( "CREATE TABLE $table (id INT, time INT)", __METHOD__ ),
207  "table created"
208  );
209  $this->assertNotSame(
210  false,
211  $db->query( "DELETE FROM $table WHERE id=57634126", __METHOD__ ),
212  "delete query"
213  );
214  } finally {
215  // Drop the table to clean up, ignoring any error.
216  $db->query( "DROP TABLE $table", __METHOD__, true );
217  // Rollback the DBO_TRX transaction for sqlite's benefit.
218  $db->rollback( __METHOD__, 'flush' );
219  }
220  }
221 
222  public function testServerAttributes() {
223  $servers = [
224  [ // master
225  'dbname' => 'my_unittest_wiki',
226  'tablePrefix' => 'unittest_',
227  'type' => 'sqlite',
228  'dbDirectory' => "some_directory",
229  'load' => 0
230  ]
231  ];
232 
233  $lb = new LoadBalancer( [
234  'servers' => $servers,
235  'localDomain' => new DatabaseDomain( 'my_unittest_wiki', null, 'unittest_' ),
236  'loadMonitorClass' => LoadMonitorNull::class
237  ] );
238 
239  $this->assertTrue( $lb->getServerAttributes( 0 )[Database::ATTR_DB_LEVEL_LOCKING] );
240 
241  $servers = [
242  [ // master
243  'host' => 'db1001',
244  'user' => 'wikiuser',
245  'password' => 'none',
246  'dbname' => 'my_unittest_wiki',
247  'tablePrefix' => 'unittest_',
248  'type' => 'mysql',
249  'load' => 100
250  ],
251  [ // emulated replica
252  'host' => 'db1002',
253  'user' => 'wikiuser',
254  'password' => 'none',
255  'dbname' => 'my_unittest_wiki',
256  'tablePrefix' => 'unittest_',
257  'type' => 'mysql',
258  'load' => 100
259  ]
260  ];
261 
262  $lb = new LoadBalancer( [
263  'servers' => $servers,
264  'localDomain' => new DatabaseDomain( 'my_unittest_wiki', null, 'unittest_' ),
265  'loadMonitorClass' => LoadMonitorNull::class
266  ] );
267 
268  $this->assertFalse( $lb->getServerAttributes( 1 )[Database::ATTR_DB_LEVEL_LOCKING] );
269  }
270 
275  function testOpenConnection() {
277 
278  $lb = new LoadBalancer( [
279  'servers' => [ $this->makeServerConfig() ],
280  'localDomain' => new DatabaseDomain( $wgDBname, null, $this->dbPrefix() )
281  ] );
282 
283  $i = $lb->getWriterIndex();
284  $this->assertEquals( null, $lb->getAnyOpenConnection( $i ) );
285  $conn1 = $lb->getConnection( $i );
286  $this->assertNotEquals( null, $conn1 );
287  $this->assertEquals( $conn1, $lb->getAnyOpenConnection( $i ) );
288  $conn2 = $lb->getConnection( $i, [], false, $lb::CONN_TRX_AUTOCOMMIT );
289  $this->assertNotEquals( null, $conn2 );
290  if ( $lb->getServerAttributes( $i )[Database::ATTR_DB_LEVEL_LOCKING] ) {
291  $this->assertEquals( null,
292  $lb->getAnyOpenConnection( $i, $lb::CONN_TRX_AUTOCOMMIT ) );
293  $this->assertEquals( $conn1,
294  $lb->getConnection(
295  $i, [], false, $lb::CONN_TRX_AUTOCOMMIT ), $lb::CONN_TRX_AUTOCOMMIT );
296  } else {
297  $this->assertEquals( $conn2,
298  $lb->getAnyOpenConnection( $i, $lb::CONN_TRX_AUTOCOMMIT ) );
299  $this->assertEquals( $conn2,
300  $lb->getConnection( $i, [], false, $lb::CONN_TRX_AUTOCOMMIT ) );
301  }
302 
303  $lb->closeAll();
304  }
305 }
LoadBalancerTest\makeServerConfig
makeServerConfig()
Definition: LoadBalancerTest.php:35
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:48
$wgDBserver
$wgDBserver
Database host name or IP address.
Definition: DefaultSettings.php:1773
LoadBalancerTest\assertWriteAllowed
assertWriteAllowed(Database $db)
Definition: LoadBalancerTest.php:191
$wgDBtype
$wgDBtype
Database type.
Definition: DefaultSettings.php:1798
LoadBalancerTest\testWithReplica
testWithReplica()
Definition: LoadBalancerTest.php:101
Wikimedia\Rdbms\Database\endAtomic
endAtomic( $fname=__METHOD__)
Ends an atomic section of SQL statements.
Definition: Database.php:3562
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
Wikimedia\Rdbms\DBError
Database error base class.
Definition: DBError.php:30
$wgDBpassword
$wgDBpassword
Database user's password.
Definition: DefaultSettings.php:1793
DBO_TRX
const DBO_TRX
Definition: defines.php:12
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
$dbr
$dbr
Definition: testCompression.php:50
MediaWikiTestCase\$called
$called
$called tracks whether the setUp and tearDown method has been called.
Definition: MediaWikiTestCase.php:43
Wikimedia\Rdbms\Database\delete
delete( $table, $conds, $fname=__METHOD__)
DELETE query wrapper.
Definition: Database.php:2830
Wikimedia\Rdbms\Database\rollback
rollback( $fname=__METHOD__, $flush='')
Rollback a transaction previously started using begin().
Definition: Database.php:3811
$wgSQLiteDataDir
$wgSQLiteDataDir
To override default SQLite data directory ($docroot/../data)
Definition: DefaultSettings.php:1873
$wgDBname
controlled by $wgMainCacheType controlled by $wgParserCacheType controlled by $wgMessageCacheType If you set CACHE_NONE to one of the three control default value for MediaWiki still create a but requests to it are no ops and we always fall through to the database If the cache daemon can t be it should also disable itself fairly smoothly By $wgMemc is used but when it is $parserMemc or $messageMemc this is mentioned $wgDBname
Definition: memcached.txt:96
Wikimedia\Rdbms\Database\startAtomic
startAtomic( $fname=__METHOD__, $cancelable=self::ATOMIC_NOT_CANCELABLE)
Begin an atomic section of SQL statements.
Definition: Database.php:3534
LoadBalancerTest\testWithoutReplica
testWithoutReplica()
Definition: LoadBalancerTest.php:51
MediaWikiTestCase
Definition: MediaWikiTestCase.php:17
LoadBalancerTest\assertWriteForbidden
assertWriteForbidden(Database $db)
Definition: LoadBalancerTest.php:176
MediaWiki
A helper class for throttling authentication attempts.
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
Wikimedia\Rdbms\Database\dropTable
dropTable( $tableName, $fName=__METHOD__)
Delete a table.
Definition: Database.php:4425
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
DB_MASTER
const DB_MASTER
Definition: defines.php:26
LoadBalancerTest\testOpenConnection
testOpenConnection()
LoadBalancer::openConnection() LoadBalancer::getAnyOpenConnection()
Definition: LoadBalancerTest.php:275
Wikimedia\Rdbms\LoadMonitorNull
Definition: LoadMonitorNull.php:28
Wikimedia\Rdbms\LoadBalancer
Database connection, tracking, load balancing, and transaction manager for a cluster.
Definition: LoadBalancer.php:40
MediaWikiTestCase\dbPrefix
dbPrefix()
Definition: MediaWikiTestCase.php:998
Wikimedia\Rdbms\Database\tableName
tableName( $name, $format='quoted')
Format a table name ready for use in constructing an SQL query.
Definition: Database.php:2212
Wikimedia\Rdbms\Database\query
query( $sql, $fname=__METHOD__, $tempIgnore=false)
Run an SQL query and return the result.
Definition: Database.php:1094
LoadBalancerTest
Database \Wikimedia\Rdbms\LoadBalancer.
Definition: LoadBalancerTest.php:34
LoadBalancerTest\testServerAttributes
testServerAttributes()
Definition: LoadBalancerTest.php:222
true
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition: hooks.txt:1987
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
$wgDBuser
$wgDBuser
Database username.
Definition: DefaultSettings.php:1788
Wikimedia\Rdbms\DatabaseDomain
Class to handle database/prefix specification for IDatabase domains.
Definition: DatabaseDomain.php:28
MediaWikiTestCase\$db
Database $db
Primary database.
Definition: MediaWikiTestCase.php:57