MediaWiki REL1_31
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() {
52 global $wgDBname;
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
276 global $wgDBname;
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}
$wgDBuser
Database username.
$wgSQLiteDataDir
To override default SQLite data directory ($docroot/../data)
$wgDBtype
Database type.
$wgDBserver
Database host name or IP address.
$wgDBpassword
Database user's password.
Database \Wikimedia\Rdbms\LoadBalancer.
assertWriteForbidden(Database $db)
assertWriteAllowed(Database $db)
testOpenConnection()
LoadBalancer::openConnection() LoadBalancer::getAnyOpenConnection()
Database $db
Primary database.
$called
$called tracks whether the setUp and tearDown method has been called.
Database error base class.
Definition DBError.php:30
Class to handle database/prefix specification for IDatabase domains.
Relational database abstraction object.
Definition Database.php:48
endAtomic( $fname=__METHOD__)
Ends an atomic section of SQL statements.
tableName( $name, $format='quoted')
Format a table name ready for use in constructing an SQL query.
startAtomic( $fname=__METHOD__, $cancelable=self::ATOMIC_NOT_CANCELABLE)
Begin an atomic section of SQL statements.
dropTable( $tableName, $fName=__METHOD__)
Delete a table.
delete( $table, $conds, $fname=__METHOD__)
DELETE query wrapper.
rollback( $fname=__METHOD__, $flush='')
Rollback a transaction previously started using begin().
query( $sql, $fname=__METHOD__, $tempIgnore=false)
Run an SQL query and return the result.
Database connection, tracking, load balancing, and transaction manager for a cluster.
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
A helper class for throttling authentication attempts.
const DB_REPLICA
Definition defines.php:25
const DB_MASTER
Definition defines.php:29
const DBO_TRX
Definition defines.php:12