Skip to content

Commit 432db0a

Browse files
committed
Adding PHPBench performance test
1 parent 7f9e22f commit 432db0a

File tree

7 files changed

+347
-91
lines changed

7 files changed

+347
-91
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ vendor/*
1313
/.travis/
1414
/vendor-bin/require-checker/vendor/
1515
/vendor-bin/couscous/vendor/
16-
.phpunit.result.cache
16+
.phpunit.result.cache
17+
/phpbench.sh
18+
/xdebug/

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
"thecodingmachine/tdbm-fluid-schema-builder": "^1.0.0",
5151
"phpstan/phpstan": "^0.11.5",
5252
"thecodingmachine/phpstan-strict-rules": "^0.11.0",
53-
"bamarni/composer-bin-plugin": "^1.2"
53+
"bamarni/composer-bin-plugin": "^1.2",
54+
"phpbench/phpbench": "^0.16.10"
5455
},
5556
"conflict": {
5657
"mouf/database.tdbm": "~5.0.0"

phpbench.dist.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
3+
export DB_DRIVER=pdo_mysql
4+
export DB_HOST=localhost
5+
export DB_PORT=3306
6+
export DB_USERNAME=root
7+
#export DB_ADMIN_USERNAME=root
8+
export DB_PASSWORD=
9+
export DB_NAME=tdbm_benchmark
10+
11+
vendor/bin/phpbench

phpbench.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"bootstrap": "vendor/autoload.php",
3+
"path": "tests/Performance"
4+
}

tests/ConnectionFactory.php

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\TDBM;
5+
6+
use Doctrine\Common\EventManager;
7+
use Doctrine\DBAL\Connection;
8+
use Doctrine\DBAL\DriverManager;
9+
use Doctrine\DBAL\Event\Listeners\OracleSessionInit;
10+
use function strtoupper;
11+
12+
class ConnectionFactory
13+
{
14+
/**
15+
* Resets the database and returns a (root) connection
16+
*/
17+
public static function resetDatabase(
18+
string $dbDriver,
19+
?string $dbHost = null,
20+
?string $dbPort = null,
21+
?string $dbUserName = null,
22+
?string $dbAdminUserName = null,
23+
?string $dbPassword = null,
24+
?string $dbName = null
25+
): Connection {
26+
$config = new \Doctrine\DBAL\Configuration();
27+
28+
$dbDriver = $dbDriver;
29+
30+
if ($dbDriver === 'pdo_sqlite') {
31+
$dbConnection = self::getConnection();
32+
$dbConnection->exec('PRAGMA foreign_keys = ON;');
33+
} elseif ($dbDriver === 'oci8') {
34+
$connectionParams = array(
35+
'servicename' => 'XE',
36+
'user' => $dbAdminUserName,
37+
// Because of issues in DBAL, admin and normal user password have to be the same.
38+
'password' => $dbPassword,
39+
'host' => $dbHost,
40+
'port' => $dbPort,
41+
'driver' => $dbDriver,
42+
'dbname' => $dbAdminUserName,
43+
'charset' => 'AL32UTF8',
44+
);
45+
46+
$adminConn = DriverManager::getConnection($connectionParams, $config);
47+
48+
// When dropAndCreateDatabase is run several times, Oracle can have some issues releasing the TDBM user.
49+
// Let's forcefully delete the connection!
50+
$adminConn->exec("select 'alter system kill session ''' || sid || ',' || serial# || ''';' from v\$session where username = '".strtoupper($dbName)."'");
51+
52+
$adminConn->getSchemaManager()->dropAndCreateDatabase($dbName);
53+
54+
$dbConnection = self::getConnection();
55+
} else {
56+
$connectionParams = array(
57+
'user' => $dbUserName,
58+
'password' => $dbPassword,
59+
'host' => $dbHost,
60+
'port' => $dbPort,
61+
'driver' => $dbDriver,
62+
);
63+
64+
$adminConn = DriverManager::getConnection($connectionParams, $config);
65+
66+
$adminConn->getSchemaManager()->dropAndCreateDatabase($dbName);
67+
68+
$connectionParams['dbname'] = $dbName;
69+
70+
$dbConnection = DriverManager::getConnection($connectionParams, $config);
71+
}
72+
73+
return $dbConnection;
74+
}
75+
76+
public static function createConnection(
77+
string $dbDriver,
78+
?string $dbHost = null,
79+
?string $dbPort = null,
80+
?string $dbUserName = null,
81+
?string $dbPassword = null,
82+
?string $dbName = null
83+
): Connection {
84+
$config = new \Doctrine\DBAL\Configuration();
85+
86+
$dbDriver = $dbDriver;
87+
88+
if ($dbDriver === 'pdo_sqlite') {
89+
$connectionParams = array(
90+
'memory' => true,
91+
'driver' => 'pdo_sqlite',
92+
);
93+
$dbConnection = DriverManager::getConnection($connectionParams, $config);
94+
} elseif ($dbDriver === 'oci8') {
95+
$evm = new EventManager();
96+
$evm->addEventSubscriber(new OracleSessionInit(array(
97+
'NLS_TIME_FORMAT' => 'HH24:MI:SS',
98+
'NLS_DATE_FORMAT' => 'YYYY-MM-DD HH24:MI:SS',
99+
'NLS_TIMESTAMP_FORMAT' => 'YYYY-MM-DD HH24:MI:SS',
100+
)));
101+
102+
$connectionParams = array(
103+
'servicename' => 'XE',
104+
'user' => $dbUserName,
105+
'password' => $dbPassword,
106+
'host' => $dbHost,
107+
'port' => $dbPort,
108+
'driver' => $dbDriver,
109+
'dbname' => $dbName,
110+
'charset' => 'AL32UTF8',
111+
);
112+
$dbConnection = DriverManager::getConnection($connectionParams, $config, $evm);
113+
$dbConnection->setAutoCommit(true);
114+
} else {
115+
$connectionParams = array(
116+
'user' => $dbUserName,
117+
'password' => $dbPassword,
118+
'host' => $dbHost,
119+
'port' => $dbPort,
120+
'driver' => $dbDriver,
121+
'dbname' => $dbName,
122+
);
123+
$dbConnection = DriverManager::getConnection($connectionParams, $config);
124+
}
125+
126+
return $dbConnection;
127+
}
128+
}

tests/Performance/ManyToOneBench.php

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\TDBM\Performance;
5+
6+
use Doctrine\Common\Cache\ArrayCache;
7+
use Doctrine\DBAL\Connection;
8+
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
9+
use PhpBench\Benchmark\Metadata\Annotations\Iterations;
10+
use TheCodingMachine\FluidSchema\TdbmFluidSchema;
11+
use TheCodingMachine\TDBM\Configuration;
12+
use TheCodingMachine\TDBM\ConfigurationInterface;
13+
use TheCodingMachine\TDBM\ConnectionFactory;
14+
use TheCodingMachine\TDBM\DummyGeneratorListener;
15+
use TheCodingMachine\TDBM\TDBMAbstractServiceTest;
16+
use TheCodingMachine\TDBM\TDBMSchemaAnalyzer;
17+
use TheCodingMachine\TDBM\TDBMService;
18+
use TheCodingMachine\TDBM\Test\Dao\UserDao;
19+
use TheCodingMachine\TDBM\Utils\PathFinder\PathFinder;
20+
use TheCodingMachine\TDBM\Utils\TDBMDaoGenerator;
21+
use function dirname;
22+
use function getenv;
23+
use function glob;
24+
use function is_dir;
25+
use function is_file;
26+
use function rmdir;
27+
use function rtrim;
28+
use function unlink;
29+
30+
/**
31+
* @BeforeClassMethods({"initDatabase"})
32+
*/
33+
class ManyToOneBench
34+
{
35+
public static function initDatabase(): void
36+
{
37+
$dbConnection = ConnectionFactory::resetDatabase(
38+
getenv('DB_DRIVER') ?: null,
39+
getenv('DB_HOST') ?: null,
40+
getenv('DB_PORT') ?: null,
41+
getenv('DB_USERNAME') ?: null,
42+
getenv('DB_ADMIN_USERNAME') ?: null,
43+
getenv('DB_PASSWORD') ?: null,
44+
getenv('DB_NAME') ?: null
45+
);
46+
47+
self::initSchema($dbConnection);
48+
49+
self::generateDaosAndBeans($dbConnection);
50+
}
51+
52+
private static function initSchema(Connection $connection): void
53+
{
54+
$fromSchema = $connection->getSchemaManager()->createSchema();
55+
$toSchema = clone $fromSchema;
56+
57+
$db = new TdbmFluidSchema($toSchema, new \TheCodingMachine\FluidSchema\DefaultNamingStrategy($connection->getDatabasePlatform()));
58+
59+
$db->table('countries')
60+
->column('id')->integer()->primaryKey()
61+
->column('label')->string(255)->unique();
62+
63+
$db->table('users')
64+
->column('id')->integer()->primaryKey()
65+
->column('name')->string(255)
66+
->column('country_id')->references('countries');
67+
68+
$sqlStmts = $toSchema->getMigrateFromSql($fromSchema, $connection->getDatabasePlatform());
69+
70+
foreach ($sqlStmts as $sqlStmt) {
71+
$connection->exec($sqlStmt);
72+
}
73+
74+
for ($i = 1; $i<200; $i++) {
75+
TDBMAbstractServiceTest::insert($connection, 'countries', [
76+
'id' => $i,
77+
'label' => 'Country '.$i,
78+
]);
79+
}
80+
81+
for ($i = 1; $i<1000; $i++) {
82+
TDBMAbstractServiceTest::insert($connection, 'users', [
83+
'id' => $i,
84+
'name' => 'User '.$i,
85+
'country_id' => ($i%199) +1,
86+
]);
87+
}
88+
}
89+
90+
private static function generateDaosAndBeans(Connection $connection): void
91+
{
92+
$schemaManager = $connection->getSchemaManager();
93+
$schemaAnalyzer = new SchemaAnalyzer($schemaManager);
94+
$tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer($connection, new ArrayCache(), $schemaAnalyzer);
95+
$tdbmDaoGenerator = new TDBMDaoGenerator(self::createConfiguration(), $tdbmSchemaAnalyzer);
96+
$rootPath = __DIR__ . '/../';
97+
self::recursiveDelete(__DIR__. '/../../src/Test/Dao/');
98+
99+
$tdbmDaoGenerator->generateAllDaosAndBeans();
100+
}
101+
102+
/**
103+
* Delete a file or recursively delete a directory.
104+
*
105+
* @param string $str Path to file or directory
106+
* @return bool
107+
*/
108+
private static function recursiveDelete(string $str): bool
109+
{
110+
if (is_file($str)) {
111+
return @unlink($str);
112+
} elseif (is_dir($str)) {
113+
$scan = glob(rtrim($str, '/') . '/*');
114+
foreach ($scan as $index => $path) {
115+
self::recursiveDelete($path);
116+
}
117+
118+
return @rmdir($str);
119+
}
120+
return false;
121+
}
122+
123+
private function getConnection(): Connection
124+
{
125+
return ConnectionFactory::createConnection(
126+
getenv('DB_DRIVER') ?: null,
127+
getenv('DB_HOST') ?: null,
128+
getenv('DB_PORT') ?: null,
129+
getenv('DB_USERNAME') ?: null,
130+
getenv('DB_PASSWORD') ?: null,
131+
getenv('DB_NAME') ?: null
132+
);
133+
}
134+
135+
protected function getTdbmService(): TDBMService
136+
{
137+
return new TDBMService($this->getConfiguration());
138+
}
139+
140+
private static $cache;
141+
142+
protected static function getCache(): ArrayCache
143+
{
144+
if (self::$cache === null) {
145+
self::$cache = new ArrayCache();
146+
}
147+
return self::$cache;
148+
}
149+
150+
private static function createConfiguration(): ConfigurationInterface
151+
{
152+
$configuration = new Configuration('TheCodingMachine\\TDBM\\Test\\Dao\\Bean', 'TheCodingMachine\\TDBM\\Test\\Dao', self::getConnection(), null, self::getCache(), null, null, []);
153+
$configuration->setPathFinder(new PathFinder(null, dirname(__DIR__, 5)));
154+
return $configuration;
155+
}
156+
157+
/**
158+
* @var ConfigurationInterface
159+
*/
160+
private $configuration;
161+
162+
protected function getConfiguration() : ConfigurationInterface
163+
{
164+
if ($this->configuration === null) {
165+
return self::createConfiguration();
166+
}
167+
return $this->configuration;
168+
}
169+
170+
/**
171+
* @Iterations(10)
172+
*/
173+
public function benchManyToOne(): void
174+
{
175+
$tdbmService = $this->getTdbmService();
176+
$userDao = new UserDao($tdbmService);
177+
foreach ($userDao->findAll() as $user) {
178+
$label = $user->getCountry()->getLabel();
179+
}
180+
}
181+
}

0 commit comments

Comments
 (0)