Skip to content

Commit 571a697

Browse files
author
Gaetano Giunta
committed
refactor time measurement for cli commands; improve cli output
1 parent cf521db commit 571a697

File tree

6 files changed

+95
-28
lines changed

6 files changed

+95
-28
lines changed

TODO.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
- worker: improve sql execution cmd:
22
+ allow it to take sql snippet from file
33
+ allow it to pick a set of desired servers
4+
+ disallow execution of commands that are part of the db client instead of being sent to the server, such as eg. 'use db'
45

56
- worker: improve profile of 'user' account (esp: add APP_ENV and APP_DEBUG env vars)
67

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Db3v4l\API\Interfaces;
4+
5+
interface TimedExecutor
6+
{
7+
/**
8+
* Returns time/memory/cpu consumption of last execution
9+
* @return array
10+
*/
11+
public function getTimingData();
12+
}

app/src/Command/SqlExecute.php

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
use Symfony\Component\Console\Input\InputInterface;
66
use Symfony\Component\Console\Output\OutputInterface;
77
use Symfony\Component\Console\Input\InputOption;
8-
use Db3v4l\Util\Process;
98
use Symfony\Component\Yaml\Yaml;
9+
use Db3v4l\API\Interfaces\TimedExecutor;
1010
use Db3v4l\Service\DatabaseConfigurationManager;
1111
use Db3v4l\Service\SqlExecutorFactory;
1212
use Db3v4l\Service\ProcessManager;
13+
use Db3v4l\Util\Process;
1314

1415
class SqlExecute extends BaseCommand
1516
{
@@ -83,21 +84,17 @@ protected function execute(InputInterface $input, OutputInterface $output)
8384

8485
/** @var Process[] $processes */
8586
$processes = [];
86-
$timingFiles = [];
87+
$executors = [];
8788
foreach ($dbList as $dbName) {
8889
$dbConnectionSpec = $this->dbManager->getDatabaseConnectionSpecification($dbName);
89-
$process = $this->executorFactory->createForkedExecutor($dbConnectionSpec)->getProcess($sql);
9090

91-
// wrap in a `time` call
92-
$timingFile = tempnam(sys_get_temp_dir(), 'db3val_');
93-
$process->setCommandLine(
94-
'time ' . escapeshellarg('--output=' . $timingFile) . ' ' . escapeshellarg('--format=%M %e') . ' '
95-
. $process->getCommandLine());
91+
$executor = $this->executorFactory->createForkedExecutor($dbConnectionSpec);
92+
$process = $executor->getProcess($sql);
9693

9794
$process->setTimeout($timeout);
9895

96+
$executors[$dbName] = $executor;
9997
$processes[$dbName] = $process;
100-
$timingFiles[$dbName] = $timingFile;
10198
}
10299

103100
if ($format === 'text') {
@@ -116,13 +113,10 @@ protected function execute(InputInterface $input, OutputInterface $output)
116113
'exitcode' => $process->getExitCode()
117114
);
118115

119-
$timingData = file_get_contents($timingFiles[$dbName]);
120-
if ($timingData != '') {
121-
$timingData = explode(' ', $timingData, 2);
122-
$results[$dbName]['time'] = $timingData[1];
123-
$results[$dbName]['memory'] = $timingData[0];
116+
if ($executors[$dbName] instanceof TimedExecutor) {
117+
$timingData = $executors[$dbName]->getTimingData();
118+
$results[$dbName] = array_merge($results[$dbName], $timingData);
124119
}
125-
unlink($timingFiles[$dbName]);
126120

127121
if ($process->isSuccessful()) {
128122
$succeeded++;
@@ -135,7 +129,6 @@ protected function execute(InputInterface $input, OutputInterface $output)
135129
$time = microtime(true) - $start;
136130

137131
$this->writeResults($results, $succeeded, $failed, $time, $format);
138-
139132
}
140133

141134
/**
@@ -147,13 +140,6 @@ protected function execute(InputInterface $input, OutputInterface $output)
147140
*/
148141
protected function writeResults(array $results, $succeeded, $failed, $time, $format = 'text')
149142
{
150-
if ($format === 'text') {
151-
$this->writeln($succeeded . ' succeeded, ' . $failed . ' failed');
152-
153-
// since we use subprocesses, we can not measure max memory used
154-
$this->writeln("<info>Time taken: ".sprintf('%.2f', $time)." secs</info>");
155-
}
156-
157143
switch ($format) {
158144
case 'json':
159145
$results = json_encode($results, JSON_PRETTY_PRINT);
@@ -170,8 +156,14 @@ protected function writeResults(array $results, $succeeded, $failed, $time, $for
170156
throw new \Exception("Unsupported output format: '$format'");
171157
break;
172158
}
173-
174159
$this->writeln($results, OutputInterface::VERBOSITY_QUIET, OutputInterface::OUTPUT_RAW);
160+
161+
if ($format === 'text') {
162+
$this->writeln($succeeded . ' succeeded, ' . $failed . ' failed');
163+
164+
// since we use subprocesses, we can not measure max memory used
165+
$this->writeln("<info>Time taken: ".sprintf('%.2f', $time)." secs</info>");
166+
}
175167
}
176168

177169
/**

app/src/Service/SqlExecutor/Forked/NativeClient.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public function getProcess($sql)
2424
'--port=' . $this->databaseConfiguration['port'] ?? '3306',
2525
'--user=' . $this->databaseConfiguration['user'],
2626
'-p' . $this->databaseConfiguration['password'],
27+
'--binary-mode', // 'It also disables all mysql commands except charset and delimiter in non-interactive mode (for input piped to mysql or loaded using the source command)'
2728
'--execute=' . $sql,
2829
// $dbname
2930
];
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Db3v4l\Service\SqlExecutor\Forked;
4+
5+
use Db3v4l\API\Interfaces\ForkedSqlExecutor;
6+
use Db3v4l\API\Interfaces\TimedExecutor as TimedExecutorInterface;
7+
8+
class TimedExecutor implements ForkedSqlExecutor, TimedExecutorInterface
9+
{
10+
/** @var ForkedSqlExecutor */
11+
protected $wrappedExecutor;
12+
protected $timingFile;
13+
14+
public function __construct(ForkedSqlExecutor $wrappedExecutor)
15+
{
16+
$this->wrappedExecutor = $wrappedExecutor;
17+
}
18+
19+
public function getProcess($sql)
20+
{
21+
$process = $this->wrappedExecutor->getProcess($sql);
22+
23+
// wrap in a `time` call
24+
$this->timingFile = tempnam(sys_get_temp_dir(), 'db3val_');
25+
$process->setCommandLine(
26+
'time ' . escapeshellarg('--output=' . $this->timingFile) . ' ' . escapeshellarg('--format=%M %e') . ' '
27+
. $process->getCommandLine());
28+
29+
return $process;
30+
}
31+
32+
public function getTimingData($onceIsEnough = true)
33+
{
34+
if (!is_file($this->timingFile)) {
35+
throw new \Exception("File with timing data gone missing: '{$this->timingFile}'");
36+
}
37+
38+
$timingData = file_get_contents($this->timingFile);
39+
if ($timingData != '') {
40+
$timingData = explode(' ', $timingData, 2);
41+
$results['time'] = $timingData[1];
42+
$results['memory'] = $timingData[0];
43+
}
44+
45+
if ($onceIsEnough) {
46+
unlink ($this->timingFile);
47+
}
48+
49+
return $results;
50+
}
51+
}

app/src/Service/SqlExecutorFactory.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,34 @@
55
use Db3v4l\API\Interfaces\ForkedSqlExecutor;
66
use Db3v4l\Service\SqlExecutor\Forked\NativeClient;
77
use Db3v4l\Service\SqlExecutor\Forked\Doctrine;
8+
use Db3v4l\Service\SqlExecutor\Forked\TimedExecutor;
89

910
class SqlExecutorFactory
1011
{
1112
/**
1213
* @param array $databaseConnectionConfiguration
1314
* @param string $executionStrategy
15+
* @bool $timed
1416
* @return ForkedSqlExecutor
1517
* @throws \OutOfBoundsException
1618
*/
17-
public function createForkedExecutor($databaseConnectionConfiguration, $executionStrategy = 'NativeClient')
19+
public function createForkedExecutor($databaseConnectionConfiguration, $executionStrategy = 'NativeClient', $timed = true)
1820
{
1921
switch ($executionStrategy) {
2022
case 'Doctrine':
21-
return new Doctrine($databaseConnectionConfiguration);
23+
$executor = new Doctrine($databaseConnectionConfiguration);
24+
break;
2225
case 'NativeClient':
23-
return new NativeClient($databaseConnectionConfiguration);
26+
$executor = new NativeClient($databaseConnectionConfiguration);
27+
break;
2428
default:
2529
throw new \OutOfBoundsException("Unsupported executor strategy '$executionStrategy'");
2630
}
31+
32+
if ($timed) {
33+
$executor = new TimedExecutor($executor);
34+
}
35+
36+
return $executor;
2737
}
28-
}
38+
}

0 commit comments

Comments
 (0)