source: pro-violet-viettel/sourcecode/application/libraries/Doctrine/DBAL/Connections/MasterSlaveConnection.php @ 360

Last change on this file since 360 was 345, checked in by quyenla, 11 years ago

collaborator page

File size: 9.7 KB
Line 
1<?php
2/*
3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14 *
15 * This software consists of voluntary contributions made by many individuals
16 * and is licensed under the LGPL. For more information, see
17 * <http://www.doctrine-project.org>.
18 */
19
20namespace Doctrine\DBAL\Connections;
21
22
23use Doctrine\DBAL\Connection,
24    Doctrine\DBAL\Driver,
25    Doctrine\DBAL\Configuration,
26    Doctrine\Common\EventManager,
27    Doctrine\DBAL\Events;
28
29/**
30 * Master-Slave Connection
31 *
32 * Connection can be used with master-slave setups.
33 *
34 * Important for the understanding of this connection should be how and when
35 * it picks the slave or master.
36 *
37 * 1. Slave if master was never picked before and ONLY if 'getWrappedConnection'
38 *    or 'executeQuery' is used.
39 * 2. Master picked when 'exec', 'executeUpdate', 'insert', 'delete', 'update', 'createSavepoint',
40 *    'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or
41 *    'prepare' is called.
42 * 3. If master was picked once during the lifetime of the connection it will always get picked afterwards.
43 * 4. One slave connection is randomly picked ONCE during a request.
44 *
45 * ATTENTION: You can write to the slave with this connection if you execute a write query without
46 * opening up a transaction. For example:
47 *
48 *      $conn = DriverManager::getConnection(...);
49 *      $conn->executeQuery("DELETE FROM table");
50 *
51 * Be aware that Connection#executeQuery is a method specifically for READ
52 * operations only.
53 *
54 * This connection is limited to slave operations using the
55 * Connection#executeQuery operation only, because it wouldn't be compatible
56 * with the ORM or SchemaManager code otherwise. Both use all the other
57 * operations in a context where writes could happen to a slave, which makes
58 * this restricted approach necessary.
59 *
60 * You can manually connect to the master at any time by calling:
61 *
62 *      $conn->connect('master');
63 *
64 * Instantiation through the DriverManager looks like:
65 *
66 * @example
67 *
68 * $conn = DriverManager::getConnection(array(
69 *    'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection',
70 *    'driver' => 'pdo_mysql',
71 *    'master' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''),
72 *    'slaves' => array(
73 *        array('user' => 'slave1', 'password', 'host' => '', 'dbname' => ''),
74 *        array('user' => 'slave2', 'password', 'host' => '', 'dbname' => ''),
75 *    )
76 * ));
77 *
78 * You can also pass 'driverOptions' and any other documented option to each of this drivers to pass additional information.
79 *
80 * @author Lars Strojny <lstrojny@php.net>
81 * @author Benjamin Eberlei <kontakt@beberlei.de>
82 */
83class MasterSlaveConnection extends Connection
84{
85    /**
86     * Master and slave connection (one of the randomly picked slaves)
87     *
88     * @var Doctrine\DBAL\Driver\Connection[]
89     */
90    protected $connections = array('master' => null, 'slave' => null);
91
92    /**
93     * Create Master Slave Connection
94     *
95     * @param array $params
96     * @param Driver $driver
97     * @param Configuration $config
98     * @param EventManager $eventManager
99     */
100    public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null)
101    {
102        if ( !isset($params['slaves']) || !isset($params['master']) ) {
103            throw new \InvalidArgumentException('master or slaves configuration missing');
104        }
105        if ( count($params['slaves']) == 0 ) {
106            throw new \InvalidArgumentException('You have to configure at least one slaves.');
107        }
108
109        $params['master']['driver'] = $params['driver'];
110        foreach ($params['slaves'] as $slaveKey => $slave) {
111            $params['slaves'][$slaveKey]['driver'] = $params['driver'];
112        }
113
114        parent::__construct($params, $driver, $config, $eventManager);
115    }
116
117    /**
118     * Check if the connection is currently towards the master or not.
119     *
120     * @return bool
121     */
122    public function isConnectedToMaster()
123    {
124        return $this->_conn !== null && $this->_conn === $this->connections['master'];
125    }
126
127    /**
128     * {@inheritDoc}
129     */
130    public function connect($connectionName = 'slave')
131    {
132        if ( $connectionName !== 'slave' && $connectionName !== 'master' ) {
133            throw new \InvalidArgumentException("Invalid option to connect(), only master or slave allowed.");
134        }
135
136        $forceMasterAsSlave = false;
137
138        if ($this->getTransactionNestingLevel() > 0) {
139            $connectionName = 'master';
140            $forceMasterAsSlave = true;
141        }
142
143        if ($this->connections[$connectionName]) {
144            if ($forceMasterAsSlave) {
145                $this->connections['slave'] = $this->_conn = $this->connections['master'];
146            } else {
147                $this->_conn = $this->connections[$connectionName];
148            }
149            return false;
150        }
151
152        if ($connectionName === 'master') {
153            /** Set slave connection to master to avoid invalid reads */
154            if ($this->connections['slave']) {
155                unset($this->connections['slave']);
156            }
157
158            $this->connections['master'] = $this->connections['slave'] = $this->_conn = $this->connectTo($connectionName);
159        } else {
160            $this->connections['slave'] = $this->_conn = $this->connectTo($connectionName);
161        }
162
163        if ($this->_eventManager->hasListeners(Events::postConnect)) {
164            $eventArgs = new Event\ConnectionEventArgs($this);
165            $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
166        }
167
168        return true;
169    }
170
171    /**
172     * Connect to a specific connection
173     *
174     * @param  string $connectionName
175     * @return Driver
176     */
177    protected function connectTo($connectionName)
178    {
179        $params = $this->getParams();
180
181        $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : array();
182
183        $connectionParams = $this->chooseConnectionConfiguration($connectionName, $params);
184
185        $user = isset($connectionParams['user']) ? $connectionParams['user'] : null;
186        $password = isset($connectionParams['password']) ? $connectionParams['password'] : null;
187
188        return $this->_driver->connect($connectionParams, $user, $password, $driverOptions);
189    }
190
191    protected function chooseConnectionConfiguration($connectionName, $params)
192    {
193        if ($connectionName === 'master') {
194            return $params['master'];
195        }
196
197        return $params['slaves'][array_rand($params['slaves'])];
198    }
199
200    /**
201     * {@inheritDoc}
202     */
203    public function executeUpdate($query, array $params = array(), array $types = array())
204    {
205        $this->connect('master');
206        return parent::executeUpdate($query, $params, $types);
207    }
208
209    /**
210     * {@inheritDoc}
211     */
212    public function beginTransaction()
213    {
214        $this->connect('master');
215        return parent::beginTransaction();
216    }
217
218    /**
219     * {@inheritDoc}
220     */
221    public function commit()
222    {
223        $this->connect('master');
224        return parent::commit();
225    }
226
227    /**
228     * {@inheritDoc}
229     */
230    public function rollback()
231    {
232        $this->connect('master');
233        return parent::rollback();
234    }
235
236    /**
237     * {@inheritDoc}
238     */
239    public function delete($tableName, array $identifier)
240    {
241        $this->connect('master');
242        return parent::delete($tableName, $identifier);
243    }
244
245    /**
246     * {@inheritDoc}
247     */
248    public function update($tableName, array $data, array $identifier, array $types = array())
249    {
250        $this->connect('master');
251        return parent::update($tableName, $data, $identifier, $types);
252    }
253
254    /**
255     * {@inheritDoc}
256     */
257    public function insert($tableName, array $data, array $types = array())
258    {
259        $this->connect('master');
260        return parent::insert($tableName, $data, $types);
261    }
262
263    /**
264     * {@inheritDoc}
265     */
266    public function exec($statement)
267    {
268        $this->connect('master');
269        return parent::exec($statement);
270    }
271
272    /**
273     * {@inheritDoc}
274     */
275    public function createSavepoint($savepoint)
276    {
277        $this->connect('master');
278
279        return parent::createSavepoint($savepoint);
280    }
281
282    /**
283     * {@inheritDoc}
284     */
285    public function releaseSavepoint($savepoint)
286    {
287        $this->connect('master');
288
289        return parent::releaseSavepoint($savepoint);
290    }
291
292    /**
293     * {@inheritDoc}
294     */
295    public function rollbackSavepoint($savepoint)
296    {
297        $this->connect('master');
298
299        return parent::rollbackSavepoint($savepoint);
300    }
301
302    public function query()
303    {
304        $this->connect('master');
305
306        $args = func_get_args();
307
308        $logger = $this->getConfiguration()->getSQLLogger();
309        if ($logger) {
310            $logger->startQuery($args[0]);
311        }
312
313        $statement = call_user_func_array(array($this->_conn, 'query'), $args);
314
315        if ($logger) {
316            $logger->stopQuery();
317        }
318
319        return $statement;
320    }
321
322    public function prepare($statement)
323    {
324        $this->connect('master');
325
326        return parent::prepare($statement);
327    }
328}
Note: See TracBrowser for help on using the repository browser.