root/docs/branches/1.1/cookbook/en/master-and-slave-connections.txt

Revision 4740, 3.5 KB (checked in by jwage, 2 years ago)

Fixing typo.

Line 
1In this tutorial we explain how you can setup Doctrine connections as master and slaves for both reading and writing data. This strategy is common when balancing load across database servers.
2
3So, the first thing we need to do is configure all the available connections for Doctrine.
4
5<code type="php">
6$connections = array(
7    'master'  => 'mysql://root:@master/dbname',
8    'slave_1' => 'mysql://root:@slave1/dbname',
9    'slave_2' => 'mysql://root:@slave2/dbname',
10    'slave_3' => 'mysql://root:@slave3/dbname',
11    'slave_4' => 'mysql://root:@slave4/dbname'
12);
13
14foreach ($connections as $name => $dsn) {
15    Doctrine_Manager::connection($dsn, $name);
16}
17</code>
18
19Now that we have one master connection and four slaves setup we can override the Doctrine_Record and Doctrine_Query classes to add our logic for switching between the connections for read and write functionality. All writes will go to the master connection and all reads will be randomly distributed across the available slaves.
20
21Lets start by adding our logic to Doctrine_Query by extending it with our own MyQuery class and switching the connection in the preQuery() hook.
22
23<code type="php">
24class MyQuery extends Doctrine_Query
25{
26    // Since php doesn't support late static binding in 5.2 we need to override
27    // this method to instantiate a new MyQuery instead of Doctrine_Query
28    public static function create($conn = null)
29    {
30        return new MyQuery($conn);
31    }
32
33    public function preQuery()
34    {
35        // If this is a select query then set connection to one of the slaves
36        if ($this->getType() == Doctrine_Query::SELECT) {
37            $this->_conn = Doctrine_Manager::getInstance()->getConnection('slave_' . rand(1, 4));
38        // All other queries are writes so they need to go to the master
39        } else {
40            $this->_conn = Doctrine_Manager::getInstance()->getConnection('master');
41        }
42    }
43}
44</code>
45
46Now we have queries taken care of, but what about when saving records? We can force the connection for writes to the master by overriding Doctrine_Record and using it as the base for all of our models.
47
48<code type="php">
49abstract class MyRecord extends Doctrine_Record
50{
51    public function save(Doctrine_Connection $conn = null)
52    {
53        // If specific connection is not provided then lets force the connection
54        // to be the master
55        if ($conn === null) {
56            $conn = Doctrine_Manager::getInstance()->getConnection('master');
57        }
58        parent::save($conn);
59    }
60}
61</code>
62
63All done! Now reads will be distributed to the slaves and writes are given to the master connection. Below are some examples of what happens now when querying and saving records.
64
65First we need to setup a model to test with.
66
67<code type="php">
68class User extends MyRecord
69{
70    public function setTableDefinition()
71    {
72      $this->setTableName('user');
73      $this->hasColumn('username', 'string', 255, array('type' => 'string', 'length' => '255'));
74      $this->hasColumn('password', 'string', 255, array('type' => 'string', 'length' => '255'));
75    }
76}
77</code>
78
79<code type="php">
80// The save() method will happen on the master connection because it is a write
81$user = new User();
82$user->username = 'jwage';
83$user->password = 'changeme';
84$user->save();
85
86// This query goes to one of the slaves because it is a read
87$q = new MyQuery();
88$q->from('User u');
89$users = $q->execute();
90
91print_r($users->toArray(true));
92
93// This query goes to the master connection because it is a write
94$q = new MyQuery();
95$q->delete('User')
96  ->from('User u')
97  ->execute();
98</code>
Note: See TracBrowser for help on using the browser.