Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
D
doctrine-dbal
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Tomáš Trávníček
doctrine-dbal
Commits
e7d6fc2c
Commit
e7d6fc2c
authored
Dec 19, 2011
by
Benjamin Eberlei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
DBAL-20 - MasterSlave Connection
parent
f771cecc
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
155 additions
and
25 deletions
+155
-25
MasterSlaveConnection.php
lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php
+70
-25
DriverManager.php
lib/Doctrine/DBAL/DriverManager.php
+3
-0
MasterSlaveConnectionTest.php
...trine/Tests/DBAL/Functional/MasterSlaveConnectionTest.php
+82
-0
No files found.
lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php
View file @
e7d6fc2c
...
@@ -22,7 +22,7 @@ namespace Doctrine\DBAL\Connections;
...
@@ -22,7 +22,7 @@ namespace Doctrine\DBAL\Connections;
use
Doctrine\DBAL\Connection
,
use
Doctrine\DBAL\Connection
,
Doctrine\DBAL\Driver
,
Doctrine\DBAL\Driver
,
Doctrine\
ORM
\Configuration
,
Doctrine\
DBAL
\Configuration
,
Doctrine\Common\EventManager
,
Doctrine\Common\EventManager
,
Doctrine\DBAL\Events
;
Doctrine\DBAL\Events
;
...
@@ -31,18 +31,35 @@ use Doctrine\DBAL\Connection,
...
@@ -31,18 +31,35 @@ use Doctrine\DBAL\Connection,
*
*
* Connection can be used with master-slave setups.
* Connection can be used with master-slave setups.
*
*
* Important for the understanding of this connection should be how it picks the slave and master.
* Important for the understanding of this connection should be how and when
* it picks the slave or master.
*
*
* 1. Master picked when 'exec', 'executeUpdate', 'insert', 'delete', 'update', 'createSavepoint',
* 1. Slave if master was never picked before and ONLY if 'getWrappedConnection'
* 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit' or 'getWrappedConnection' is called.
* or 'executeQuery' is used.
* 2. If master was picked once during the lifetime of the connection it will always get picked afterwards.
* 2. Master picked when 'exec', 'executeUpdate', 'insert', 'delete', 'update', 'createSavepoint',
* 3. Slave if master was never picked before and 'query', 'prepare' or 'executeQuery' is used.
* 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or
* 'prepare' is called.
* 3. If master was picked once during the lifetime of the connection it will always get picked afterwards.
* 4. One slave connection is randomly picked ONCE during a request.
*
*
* ATTENTION: You can write to the slave with this connection if you execute a write query without
* ATTENTION: You can write to the slave with this connection if you execute a write query without
* opening up a transaction. For example:
* opening up a transaction. For example:
*
*
* $conn = DriverManager::getConnection(...);
* $conn = DriverManager::getConnection(...);
* $conn->query("DELETE FROM table");
* $conn->executeQuery("DELETE FROM table");
*
* Be aware that Connection#executeQuery is a method specifically for READ
* operations only.
*
* This connection is limited to slave operations using the
* Connection#executeQuery operation only, because it wouldn't be compatible
* with the ORM or SchemaManager code otherwise. Both use all the other
* operations in a context where writes could happen to a slave, which makes
* this restricted approach necessary.
*
* You can manually connect to the master at any time by calling:
*
* $conn->connect('master');
*
*
* Instantiation through the DriverManager looks like:
* Instantiation through the DriverManager looks like:
*
*
...
@@ -50,7 +67,8 @@ use Doctrine\DBAL\Connection,
...
@@ -50,7 +67,8 @@ use Doctrine\DBAL\Connection,
*
*
* $conn = DriverManager::getConnection(array(
* $conn = DriverManager::getConnection(array(
* 'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection',
* 'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection',
* 'master' => array('driver' => 'pdo_mysql', 'user' => '', 'password' => '', 'host' => '', 'dbname' => ''),
* 'driver' => 'pdo_mysql',
* 'master' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''),
* 'slaves' => array(
* 'slaves' => array(
* array('user' => 'slave1', 'password', 'host' => '', 'dbname' => ''),
* array('user' => 'slave1', 'password', 'host' => '', 'dbname' => ''),
* array('user' => 'slave2', 'password', 'host' => '', 'dbname' => ''),
* array('user' => 'slave2', 'password', 'host' => '', 'dbname' => ''),
...
@@ -84,8 +102,8 @@ class MasterSlaveConnection extends Connection
...
@@ -84,8 +102,8 @@ class MasterSlaveConnection extends Connection
if
(
!
isset
(
$params
[
'slaves'
])
||
!
isset
(
$params
[
'master'
])
)
{
if
(
!
isset
(
$params
[
'slaves'
])
||
!
isset
(
$params
[
'master'
])
)
{
throw
new
\InvalidArgumentException
(
'master or slaves configuration missing'
);
throw
new
\InvalidArgumentException
(
'master or slaves configuration missing'
);
}
}
if
(
count
(
array_filter
(
$params
[
'slaves'
],
function
(
$v
)
{
return
!
is_numeric
(
$v
);
}))
>
0
)
{
if
(
count
(
$params
[
'slaves'
])
==
0
)
{
throw
new
\InvalidArgumentException
(
'You have to configure
multipl
e slaves.'
);
throw
new
\InvalidArgumentException
(
'You have to configure
at least on
e slaves.'
);
}
}
$params
[
'master'
][
'driver'
]
=
$params
[
'driver'
];
$params
[
'master'
][
'driver'
]
=
$params
[
'driver'
];
...
@@ -96,6 +114,16 @@ class MasterSlaveConnection extends Connection
...
@@ -96,6 +114,16 @@ class MasterSlaveConnection extends Connection
parent
::
__construct
(
$params
,
$driver
,
$config
,
$eventManager
);
parent
::
__construct
(
$params
,
$driver
,
$config
,
$eventManager
);
}
}
/**
* Check if the connection is currently towards the master or not.
*
* @return bool
*/
public
function
isConnectedToMaster
()
{
return
$this
->
_conn
!==
null
&&
$this
->
_conn
===
$this
->
connections
[
'master'
];
}
/**
/**
* {@inheritDoc}
* {@inheritDoc}
*/
*/
...
@@ -124,7 +152,7 @@ class MasterSlaveConnection extends Connection
...
@@ -124,7 +152,7 @@ class MasterSlaveConnection extends Connection
if
(
$connectionName
===
'master'
)
{
if
(
$connectionName
===
'master'
)
{
/** Set slave connection to master to avoid invalid reads */
/** Set slave connection to master to avoid invalid reads */
if
(
$this
->
connections
[
'slave'
])
{
if
(
$this
->
connections
[
'slave'
])
{
$this
->
connections
[
'slave'
]
->
close
(
);
unset
(
$this
->
connections
[
'slave'
]
);
}
}
$this
->
connections
[
'master'
]
=
$this
->
connections
[
'slave'
]
=
$this
->
_conn
=
$this
->
connectTo
(
$connectionName
);
$this
->
connections
[
'master'
]
=
$this
->
connections
[
'slave'
]
=
$this
->
_conn
=
$this
->
connectTo
(
$connectionName
);
...
@@ -217,19 +245,19 @@ class MasterSlaveConnection extends Connection
...
@@ -217,19 +245,19 @@ class MasterSlaveConnection extends Connection
/**
/**
* {@inheritDoc}
* {@inheritDoc}
*/
*/
public
function
update
(
$tableName
,
array
$data
,
array
$identifier
)
public
function
update
(
$tableName
,
array
$data
,
array
$identifier
,
array
$types
=
array
()
)
{
{
$this
->
connect
(
'master'
);
$this
->
connect
(
'master'
);
return
parent
::
update
(
$tableName
,
$data
,
$identifier
);
return
parent
::
update
(
$tableName
,
$data
,
$identifier
,
$types
);
}
}
/**
/**
* {@inheritDoc}
* {@inheritDoc}
*/
*/
public
function
insert
(
$tableName
,
array
$data
)
public
function
insert
(
$tableName
,
array
$data
,
array
$types
=
array
()
)
{
{
$this
->
connect
(
'master'
);
$this
->
connect
(
'master'
);
return
parent
::
insert
(
$tableName
,
$data
);
return
parent
::
insert
(
$tableName
,
$data
,
$types
);
}
}
/**
/**
...
@@ -241,16 +269,6 @@ class MasterSlaveConnection extends Connection
...
@@ -241,16 +269,6 @@ class MasterSlaveConnection extends Connection
return
parent
::
exec
(
$statement
);
return
parent
::
exec
(
$statement
);
}
}
/**
* {@inheritDoc}
*/
public
function
getWrappedConnection
()
{
$this
->
connect
(
'master'
);
return
$this
->
_conn
;
}
/**
/**
* {@inheritDoc}
* {@inheritDoc}
*/
*/
...
@@ -280,4 +298,31 @@ class MasterSlaveConnection extends Connection
...
@@ -280,4 +298,31 @@ class MasterSlaveConnection extends Connection
return
parent
::
rollbackSavepoint
(
$savepoint
);
return
parent
::
rollbackSavepoint
(
$savepoint
);
}
}
public
function
query
()
{
$this
->
connect
(
'master'
);
$args
=
func_get_args
();
$logger
=
$this
->
getConfiguration
()
->
getSQLLogger
();
if
(
$logger
)
{
$logger
->
startQuery
(
$args
[
0
]);
}
$statement
=
call_user_func_array
(
array
(
$this
->
_conn
,
'query'
),
$args
);
if
(
$logger
)
{
$logger
->
stopQuery
();
}
return
$statement
;
}
public
function
prepare
(
$statement
)
{
$this
->
connect
(
'master'
);
return
parent
::
prepare
(
$statement
);
}
}
}
lib/Doctrine/DBAL/DriverManager.php
View file @
e7d6fc2c
...
@@ -86,6 +86,9 @@ final class DriverManager
...
@@ -86,6 +86,9 @@ final class DriverManager
* You may specify a custom wrapper class through the 'wrapperClass'
* You may specify a custom wrapper class through the 'wrapperClass'
* parameter but this class MUST inherit from Doctrine\DBAL\Connection.
* parameter but this class MUST inherit from Doctrine\DBAL\Connection.
*
*
* <b>driverClass</b>:
* The driver class to use.
*
* @param array $params The parameters.
* @param array $params The parameters.
* @param Doctrine\DBAL\Configuration The configuration to use.
* @param Doctrine\DBAL\Configuration The configuration to use.
* @param Doctrine\Common\EventManager The event manager to use.
* @param Doctrine\Common\EventManager The event manager to use.
...
...
tests/Doctrine/Tests/DBAL/Functional/MasterSlaveConnectionTest.php
0 → 100644
View file @
e7d6fc2c
<?php
namespace
Doctrine\Tests\DBAL\Functional
;
use
Doctrine\Tests\DbalFunctionalTestCase
;
use
Doctrine\DBAL\DriverManager
;
/**
* @group DBAL-20
*/
class
MasterSlaveConnectionTest
extends
DbalFunctionalTestCase
{
public
function
setUp
()
{
parent
::
setUp
();
if
(
$this
->
_conn
->
getDatabasePlatform
()
->
getName
()
==
"sqlite"
)
{
$this
->
markTestSkipped
(
'Test does not work on sqlite.'
);
}
try
{
/* @var $sm \Doctrine\DBAL\Schema\AbstractSchemaManager */
$table
=
new
\Doctrine\DBAL\Schema\Table
(
"master_slave_table"
);
$table
->
addColumn
(
'test_int'
,
'integer'
);
$table
->
setPrimaryKey
(
array
(
'test_int'
));
$sm
=
$this
->
_conn
->
getSchemaManager
();
$sm
->
createTable
(
$table
);
$this
->
_conn
->
insert
(
'master_slave_table'
,
array
(
'test_int'
=>
1
));
}
catch
(
\Exception
$e
)
{
}
}
public
function
createMasterSlaveConnection
()
{
$params
=
$this
->
_conn
->
getParams
();
$params
[
'master'
]
=
$params
;
$params
[
'slaves'
]
=
array
(
$params
,
$params
);
$params
[
'wrapperClass'
]
=
'Doctrine\DBAL\Connections\MasterSlaveConnection'
;
return
DriverManager
::
getConnection
(
$params
);
}
public
function
testMasterOnConnect
()
{
$conn
=
$this
->
createMasterSlaveConnection
();
$this
->
assertFalse
(
$conn
->
isConnectedToMaster
());
$conn
->
connect
(
'slave'
);
$this
->
assertFalse
(
$conn
->
isConnectedToMaster
());
$conn
->
connect
(
'master'
);
$this
->
assertTrue
(
$conn
->
isConnectedToMaster
());
}
public
function
testNoMasterOnExecuteQuery
()
{
$conn
=
$this
->
createMasterSlaveConnection
();
$sql
=
"SELECT count(*) as num FROM master_slave_table"
;
$data
=
$conn
->
fetchAll
(
$sql
);
$data
[
0
]
=
array_change_key_case
(
$data
[
0
],
CASE_LOWER
);
$this
->
assertEquals
(
1
,
$data
[
0
][
'num'
]);
$this
->
assertFalse
(
$conn
->
isConnectedToMaster
());
}
public
function
testMasterOnWriteOperation
()
{
$conn
=
$this
->
createMasterSlaveConnection
();
$conn
->
insert
(
'master_slave_table'
,
array
(
'test_int'
=>
30
));
$this
->
assertTrue
(
$conn
->
isConnectedToMaster
());
$sql
=
"SELECT count(*) as num FROM master_slave_table"
;
$data
=
$conn
->
fetchAll
(
$sql
);
$data
[
0
]
=
array_change_key_case
(
$data
[
0
],
CASE_LOWER
);
$this
->
assertEquals
(
2
,
$data
[
0
][
'num'
]);
$this
->
assertTrue
(
$conn
->
isConnectedToMaster
());
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment