Skip to content

Commit

Permalink
Merge pull request #23 from mathroc/feature/too-many-connections
Browse files Browse the repository at this point in the history
handle Too many connections
  • Loading branch information
mathroc committed May 28, 2015
2 parents b2cc143 + cc5b530 commit a81b6c2
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 146 deletions.
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

0 comments on commit a81b6c2

Please sign in to comment.