MasterSlaveConnectionTest.php 5.67 KB
Newer Older
1 2 3 4
<?php

namespace Doctrine\Tests\DBAL\Functional;

5
use Doctrine\DBAL\Connections\MasterSlaveConnection;
6
use Doctrine\DBAL\DriverManager;
Sergei Morozov's avatar
Sergei Morozov committed
7 8
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\Table;
jeroendedauw's avatar
jeroendedauw committed
9
use Doctrine\Tests\DbalFunctionalTestCase;
Sergei Morozov's avatar
Sergei Morozov committed
10
use Throwable;
11 12 13 14 15 16
use const CASE_LOWER;
use function array_change_key_case;
use function sprintf;
use function strlen;
use function strtolower;
use function substr;
17 18 19 20 21 22

/**
 * @group DBAL-20
 */
class MasterSlaveConnectionTest extends DbalFunctionalTestCase
{
23
    protected function setUp()
24 25 26
    {
        parent::setUp();

Sergei Morozov's avatar
Sergei Morozov committed
27
        $platformName = $this->connection->getDatabasePlatform()->getName();
28 29

        // This is a MySQL specific test, skip other vendors.
Sergei Morozov's avatar
Sergei Morozov committed
30
        if ($platformName !== 'mysql') {
31
            $this->markTestSkipped(sprintf('Test does not work on %s.', $platformName));
32 33 34
        }

        try {
Sergei Morozov's avatar
Sergei Morozov committed
35 36
            /** @var AbstractSchemaManager $sm */
            $table = new Table('master_slave_table');
37
            $table->addColumn('test_int', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
38
            $table->setPrimaryKey(['test_int']);
39

Sergei Morozov's avatar
Sergei Morozov committed
40
            $sm = $this->connection->getSchemaManager();
41
            $sm->createTable($table);
Sergei Morozov's avatar
Sergei Morozov committed
42
        } catch (Throwable $e) {
43
        }
44

Sergei Morozov's avatar
Sergei Morozov committed
45 46
        $this->connection->executeUpdate('DELETE FROM master_slave_table');
        $this->connection->insert('master_slave_table', ['test_int' => 1]);
47 48
    }

49 50 51 52 53
    private function createMasterSlaveConnection(bool $keepSlave = false) : MasterSlaveConnection
    {
        return DriverManager::getConnection($this->createMasterSlaveConnectionParams($keepSlave));
    }

Sergei Morozov's avatar
Sergei Morozov committed
54 55 56
    /**
     * @return mixed[]
     */
57
    private function createMasterSlaveConnectionParams(bool $keepSlave = false) : array
58
    {
Sergei Morozov's avatar
Sergei Morozov committed
59
        $params                 = $this->connection->getParams();
60
        $params['master']       = $params;
Sergei Morozov's avatar
Sergei Morozov committed
61
        $params['slaves']       = [$params, $params];
62
        $params['keepSlave']    = $keepSlave;
63 64 65 66 67 68 69 70 71
        $params['wrapperClass'] = MasterSlaveConnection::class;

        return $params;
    }

    public function testInheritCharsetFromMaster() : void
    {
        $charsets = [
            'utf8',
Sergei Morozov's avatar
Sergei Morozov committed
72
            'latin1',
73
        ];
74

75
        foreach ($charsets as $charset) {
Sergei Morozov's avatar
Sergei Morozov committed
76
            $params                      = $this->createMasterSlaveConnectionParams();
77 78 79
            $params['master']['charset'] = $charset;

            foreach ($params['slaves'] as $index => $slaveParams) {
Sergei Morozov's avatar
Sergei Morozov committed
80 81
                if (! isset($slaveParams['charset'])) {
                    continue;
82
                }
Sergei Morozov's avatar
Sergei Morozov committed
83 84

                unset($params['slaves'][$index]['charset']);
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
            }

            /** @var MasterSlaveConnection $conn */
            $conn = DriverManager::getConnection($params);
            $conn->connect('slave');

            self::assertFalse($conn->isConnectedToMaster());

            $clientCharset = $conn->fetchColumn('select @@character_set_client as c');

            self::assertSame(
                $charset,
                substr(strtolower($clientCharset), 0, strlen($charset))
            );
        }
100 101 102 103 104 105
    }

    public function testMasterOnConnect()
    {
        $conn = $this->createMasterSlaveConnection();

106
        self::assertFalse($conn->isConnectedToMaster());
107
        $conn->connect('slave');
108
        self::assertFalse($conn->isConnectedToMaster());
109
        $conn->connect('master');
110
        self::assertTrue($conn->isConnectedToMaster());
111 112 113 114 115 116
    }

    public function testNoMasterOnExecuteQuery()
    {
        $conn = $this->createMasterSlaveConnection();

Sergei Morozov's avatar
Sergei Morozov committed
117 118
        $sql     = 'SELECT count(*) as num FROM master_slave_table';
        $data    = $conn->fetchAll($sql);
119 120
        $data[0] = array_change_key_case($data[0], CASE_LOWER);

121 122
        self::assertEquals(1, $data[0]['num']);
        self::assertFalse($conn->isConnectedToMaster());
123 124 125 126 127
    }

    public function testMasterOnWriteOperation()
    {
        $conn = $this->createMasterSlaveConnection();
Sergei Morozov's avatar
Sergei Morozov committed
128
        $conn->insert('master_slave_table', ['test_int' => 30]);
129

130
        self::assertTrue($conn->isConnectedToMaster());
131

Sergei Morozov's avatar
Sergei Morozov committed
132 133
        $sql     = 'SELECT count(*) as num FROM master_slave_table';
        $data    = $conn->fetchAll($sql);
134 135
        $data[0] = array_change_key_case($data[0], CASE_LOWER);

136 137
        self::assertEquals(2, $data[0]['num']);
        self::assertTrue($conn->isConnectedToMaster());
138
    }
139 140 141 142 143 144 145 146

    /**
     * @group DBAL-335
     */
    public function testKeepSlaveBeginTransactionStaysOnMaster()
    {
        $conn = $this->createMasterSlaveConnection($keepSlave = true);
        $conn->connect('slave');
147

148
        $conn->beginTransaction();
Sergei Morozov's avatar
Sergei Morozov committed
149
        $conn->insert('master_slave_table', ['test_int' => 30]);
150
        $conn->commit();
151

152
        self::assertTrue($conn->isConnectedToMaster());
153 154

        $conn->connect();
155
        self::assertTrue($conn->isConnectedToMaster());
156 157

        $conn->connect('slave');
158
        self::assertFalse($conn->isConnectedToMaster());
159
    }
160 161 162 163 164 165 166 167 168

    /**
     * @group DBAL-335
     */
    public function testKeepSlaveInsertStaysOnMaster()
    {
        $conn = $this->createMasterSlaveConnection($keepSlave = true);
        $conn->connect('slave');

Sergei Morozov's avatar
Sergei Morozov committed
169
        $conn->insert('master_slave_table', ['test_int' => 30]);
170

171
        self::assertTrue($conn->isConnectedToMaster());
172 173

        $conn->connect();
174
        self::assertTrue($conn->isConnectedToMaster());
175 176

        $conn->connect('slave');
177
        self::assertFalse($conn->isConnectedToMaster());
178
    }
179 180 181 182 183

    public function testMasterSlaveConnectionCloseAndReconnect()
    {
        $conn = $this->createMasterSlaveConnection();
        $conn->connect('master');
184
        self::assertTrue($conn->isConnectedToMaster());
185 186

        $conn->close();
187
        self::assertFalse($conn->isConnectedToMaster());
188 189

        $conn->connect('master');
190
        self::assertTrue($conn->isConnectedToMaster());
191
    }
192
}