Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle Too many connections #23

Merged
merged 2 commits into from
May 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 100 additions & 83 deletions features/bootstrap/FeatureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,55 @@ public function clearConnections()
'last-error' => null,
]
];
$connection = $this->rootConnection();
$connection->exec("SET GLOBAL MAX_CONNECTIONS = 50");
$connection->close();
$connection = null;
gc_collect_cycles();
}

/**
* @BeforeScenario
*/
public function clearDatabase()
{
$params = $this->masterParams();
$dbname = $params['dbname'];
unset($params['dbname']);
$connection = DriverManager::getConnection($params);
$dbname = $this->masterParams()['dbname'];
$connection = $this->rootConnection();
$connection->exec("DROP DATABASE IF EXISTS $dbname");
$connection->exec("CREATE DATABASE $dbname");
$connection->close();
$connection = null;
gc_collect_cycles();
}

/**
* @BeforeScenario
*/
public function assertNoActiveConnection()
{
$n = $this->activeConnectionsCount();
assert($n === 0, "There is $n active connection(s) on the test server");
}

private function activeConnectionsCount()
{
$connection = $this->rootConnection();
gc_collect_cycles();
$n = (int)$connection->fetchAll("show status like 'Threads_connected'")[0]['Value'];
$connection->close();
$connection = null;
gc_collect_cycles();
return $n - 1;
}

private function rootConnection()
{
$params = $this->masterParams('root');
$dbname = $params['dbname'];
unset($params['dbname']);
return DriverManager::getConnection($params);
}

/**
* @AfterScenario
*/
Expand All @@ -43,6 +78,22 @@ public function closeConnections()
$instance->close();
}
}
$this->connections = [];
gc_collect_cycles();
}

/**
* @Given the server accept :n more connection
* @Given the server accept :n more connections
*/
public function theServerAcceptMoreConnections($n)
{
$n += $this->activeConnectionsCount();
$connection = $this->rootConnection();
$connection->exec("SET GLOBAL MAX_CONNECTIONS = $n");
$connection->close();
$connection = null;
gc_collect_cycles();
}

/**
Expand Down Expand Up @@ -75,43 +126,12 @@ public function aMasterSlavesConnectionWithSlaves($connectionName, $slaveCount)
/**
* @Given a retry master\/slaves connection :connectionName with :slaveCount slaves limited to :n retry
* @Given a retry master\/slaves connection :connectionName with :slaveCount slaves limited to :n retries
* @Given a retry master\/slaves connection :connectionName with :slaveCount slaves limited to :n retry with username :username
* @Given a retry master\/slaves connection :connectionName with :slaveCount slaves limited to :n retries with username :username
*/
public function aRetryMasterSlavesConnectionWithSlavesLimitedToRetries($connectionName, $slaveCount, $n)
{
$master = $this->masterParams();

$slaveCount = (int) $slaveCount;
$slaves = [];
while ($slaveCount--) {
$master['weight'] = 1;
$slaves[] = $master;
}

$params = [
'driverClass' => 'Ez\DbLinker\Driver\MysqlRetryDriver',
'connectionParams' => [
'master' => $master,
'slaves' => $slaves,
'driverClass' => 'Ez\DbLinker\Driver\MysqlMasterSlavesDriver',
],
'retryStrategy' => new MysqlRetryStrategy($n),
];
$this->connections[$connectionName] = [
'params' => $params,
'instance' => null,
'last-result' => null,
'last-error' => null,
];
}

/**
* @Given a retry master\/slaves connection :connectionName with :slaveCount slaves limited to :n retry with username :userName
* @Given a retry master\/slaves connection :connectionName with :slaveCount slaves limited to :n retries with username :userName
*/
public function aRetryMasterSlavesConnectionWithSlavesLimitedToRetriesWithUsername($connectionName, $slaveCount, $n, $userName)
public function aRetryMasterSlavesConnectionWithSlavesLimitedToRetriesWithusername($connectionName, $slaveCount, $n, $username = null)
{
$master = $this->masterParams();
$master['user'] = $userName;
$master = $this->masterParams($username);

$slaveCount = (int) $slaveCount;
$slaves = [];
Expand Down Expand Up @@ -238,34 +258,15 @@ public function aTransactionIsStartedOn($connectionName)

/**
* @Given a retry connection :connectionName limited to :n retry
* @Given a retry connection :connectionName limited to :n retries
*/
public function aRetryConnection($connectionName, $n)
{
$params = [
'driverClass' => 'Ez\DbLinker\Driver\MysqlRetryDriver',
'connectionParams' => $this->masterParams(),
'retryStrategy' => new MysqlRetryStrategy($n),
];
$this->connections[$connectionName] = [
'params' => $params,
'instance' => null,
'last-result' => null,
'last-error' => null,
];
}

/**
* @Given a retry connection :connectionName limited to :n retry with username :username
*/
public function aRetryConnectionLimitedToRetryWithUsername($connectionName, $n, $username)
public function aRetryConnectionLimitedToRetryWithusername($connectionName, $n, $username = null)
{
$params = [
'driverClass' => 'Ez\DbLinker\Driver\MysqlRetryDriver',
'connectionParams' => $this->masterParams(),
'connectionParams' => $this->masterParams($username),
'retryStrategy' => new MysqlRetryStrategy($n),
];
$params['connectionParams']['user'] = $username;
$this->connections[$connectionName] = [
'params' => $params,
'instance' => null,
Expand All @@ -275,20 +276,13 @@ public function aRetryConnectionLimitedToRetryWithUsername($connectionName, $n,
}

/**
* @Given a retry master\/slaves connection :connectionName with :slaveCount slaves limited to :n retry with db :db and username :username
* @Given a retry master\/slaves connection :connectionName with :slaveCount slaves limited to :n retry with db :db
* @Given a retry master\/slaves connection :connectionName with :slaveCount slaves limited to :n retry with db :db and username :username
*/
public function aRetryMasterSlavesConnectionWithSlavesLimitedToRetryWithDb($connectionName, $slaveCount, $n, $db, $username = null, $password = '')
public function aRetryMasterSlavesConnectionWithSlavesLimitedToRetryWithDbAndUsername($connectionName, $slaveCount, $n, $db, $username = null)
{
$master = $this->masterParams();
$master = $this->masterParams($username);
$master['dbname'] = $db;
if ($username !== null) {
$master['user'] = $username;
if ($username === 'root') {
$password = getenv('DBLINKER_DB_1_ENV_MYSQL_ROOT_PASSWORD');
}
$master['password'] = $password;
}

$slaveCount = (int) $slaveCount;
$slaves = [];
Expand All @@ -315,19 +309,12 @@ public function aRetryMasterSlavesConnectionWithSlavesLimitedToRetryWithDb($conn
}

/**
* @Given a retry connection :connectionName limited to :n retry with db :db and username :username
* @Given a retry connection :connectionName limited to :n retry with db :db
* @Given a retry connection :connectionName limited to :n retry with db :db and username :username
*/
public function aRetryConnectionLimitedToRetryWithDb($connectionName, $n, $db, $username = null)
public function aRetryConnectionLimitedToRetryWithDbAndUsername($connectionName, $n, $db, $username = null)
{
$master = $this->masterParams();
if ($username !== null) {
$master['user'] = $username;
if ($username === 'root') {
$password = getenv('DBLINKER_DB_1_ENV_MYSQL_ROOT_PASSWORD');
}
$master['password'] = $password;
}
$master = $this->masterParams($username);
$params = [
'driverClass' => 'Ez\DbLinker\Driver\MysqlRetryDriver',
'connectionParams' => $master,
Expand Down Expand Up @@ -453,6 +440,20 @@ public function shouldHaveSlave($connectionName, $n)
assert($slaveCount === (int)$n, "Slaves count is $slaveCount, $n expected.");
}

/**
* @Given a connection :connectionName
* @Given a connection :connectionName with username :username
*/
public function aConnectionWithusername($connectionName, $username = null)
{
$this->connections[$connectionName] = [
'params' => $this->masterParams($username),
'instance' => null,
'last-result' => null,
'last-error' => null,
];
}

private function getWrappedConnection($connectionName)
{
return $this->getConnection($connectionName)->getWrappedConnection();
Expand All @@ -468,7 +469,24 @@ private function getConnection($connectionName)
return $this->connections[$connectionName]['instance'];
}

abstract protected function masterParams();
protected abstract function params(Array $params);

private function masterParams($username = null, $password = '') {
$params = [
'host' => getenv('DBLINKER_DB_1_PORT_3306_TCP_ADDR'),
'user' => getenv('DBLINKER_DB_1_ENV_MYSQL_USER'),
'password' => getenv('DBLINKER_DB_1_ENV_MYSQL_PASSWORD'),
'dbname' => getenv('DBLINKER_DB_1_ENV_MYSQL_DATABASE'),
];
if ($username !== null) {
$params['user'] = $username;
if ($username === 'root') {
$password = getenv('DBLINKER_DB_1_ENV_MYSQL_ROOT_PASSWORD');
}
$params['password'] = $password;
}
return $this->params($params);
}
}

class MysqlRetryStrategy extends Ez\DbLinker\RetryStrategy\MysqlRetryStrategy
Expand All @@ -478,12 +496,11 @@ class MysqlRetryStrategy extends Ez\DbLinker\RetryStrategy\MysqlRetryStrategy
public function shouldRetry(
Doctrine\DBAL\DBALException $exception,
Ez\DbLinker\Driver\Connection\RetryConnection $connection,
Doctrine\DBAL\Driver\Connection $wrappedConnection,
$method,
Array $arguments
) {
$this->lastError = $exception;
return parent::shouldRetry($exception, $connection, $wrappedConnection, $method, $arguments);
return parent::shouldRetry($exception, $connection, $method, $arguments);
}

public function lastError()
Expand Down
11 changes: 3 additions & 8 deletions features/bootstrap/MysqliContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@ class MysqliContext implements Context, SnippetAcceptingContext
{
use FeatureContext;

private function masterParams()
private function params(Array $params)
{
return [
'host' => getenv('DBLINKER_DB_1_PORT_3306_TCP_ADDR'),
'user' => getenv('DBLINKER_DB_1_ENV_MYSQL_USER'),
'password' => getenv('DBLINKER_DB_1_ENV_MYSQL_PASSWORD'),
'dbname' => getenv('DBLINKER_DB_1_ENV_MYSQL_DATABASE'),
'driver' => 'mysqli',
];
$params['driver'] = 'mysqli';
return $params;
}
}
17 changes: 6 additions & 11 deletions features/bootstrap/PdoMysqlContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,13 @@ class PdoMysqlContext implements Context, SnippetAcceptingContext
{
use FeatureContext;

private function masterParams()
private function params(Array $params)
{
return [
'host' => getenv('DBLINKER_DB_1_PORT_3306_TCP_ADDR'),
'user' => getenv('DBLINKER_DB_1_ENV_MYSQL_USER'),
'password' => getenv('DBLINKER_DB_1_ENV_MYSQL_PASSWORD'),
'dbname' => getenv('DBLINKER_DB_1_ENV_MYSQL_DATABASE'),
'driver' => 'pdo_mysql',
'driverOptions' => [
// todo move to retry driver
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
],
$params['driver'] = 'pdo_mysql';
$params['driverOptions'] = [
// todo move to retry driver
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
];
return $params;
}
}
6 changes: 3 additions & 3 deletions features/retry-master-slave.feature
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@retry @master-slaves @wip
@retry @master-slaves
Feature: Retry Master/Slaves

@bug
Scenario: ACCESS_DENIED_ERROR restart on another slave
Given a retry master/slaves connection "conn" with 2 slaves limited to 1 retry with username "nobody"
When I query "SELECT 1" on "conn"
Expand All @@ -17,7 +17,7 @@ Feature: Retry Master/Slaves
And the last error code should be 1045 on "conn"
And "conn" retry limit should be 1
And "conn" should have 2 slaves

@bug
Scenario: ER_BAD_DB_ERROR restart on another slave
Given a retry master/slaves connection "conn" with 2 slaves limited to 1 retry with db "unknown_db" and username "root"
When I query "SELECT 1" on "conn"
Expand Down
10 changes: 10 additions & 0 deletions features/retry.feature
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,13 @@ Feature: Retry
And I query "SELECT 1" on "conn"
Then the last query succeeded on "conn"
And "conn" retry limit should be 0
@wip
Scenario: Too many connections
Given the server accept 1 more connections
And a retry connection "conn1" limited to 1 retry
And a retry connection "conn2" limited to 1 retry
When I query "SELECT 1" on "conn1"
And I query "SELECT 1" on "conn2"
Then the last query failed on "conn2"
And the last error code should be 1040 on "conn2"
And "conn2" retry limit should be 0
1 change: 0 additions & 1 deletion src/Driver/Connection/RetryConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ private function callAndRetry($method, Array $arguments)
if (!$this->retryStrategy->shouldRetry(
$exception,
$this,
$connection->getWrappedConnection(),
$method,
$arguments
)) {
Expand Down
1 change: 0 additions & 1 deletion src/RetryStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ interface RetryStrategy
public function shouldRetry(
DBALException $exception,
RetryConnection $connection,
Connection $wrappedConnection,
$method,
Array $arguments
);
Expand Down
Loading