Skip to content

Commit ec5337e

Browse files
committed
PERF: Deploy to all servers in parallel
Speeds up deployment when working with more than 1 webserver
1 parent 8357c08 commit ec5337e

File tree

2 files changed

+94
-31
lines changed

2 files changed

+94
-31
lines changed

src/Task/PendingTask.php

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the EasyDeploy project.
5+
*
6+
* (c) Javier Eguiluz <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace EasyCorp\Bundle\EasyDeployBundle\Task;
13+
14+
use EasyCorp\Bundle\EasyDeployBundle\Helper\Str;
15+
use EasyCorp\Bundle\EasyDeployBundle\Logger;
16+
use EasyCorp\Bundle\EasyDeployBundle\Server\Server;
17+
use Symfony\Component\Process\Process;
18+
use Symfony\Component\Process\Exception\ProcessFailedException;
19+
20+
class PendingTask {
21+
private $server;
22+
private $dryRun;
23+
private $process;
24+
private $logger;
25+
26+
public function __construct (Server $server, string $shellCommand, Logger $logger, bool $dryRun = false)
27+
{
28+
$this->server = $server;
29+
$this->dryRun = $dryRun;
30+
$this->logger = $logger;
31+
32+
if ($this->dryRun) {
33+
return;
34+
}
35+
36+
if ($server->isLocalHost()) {
37+
$this->process = $this->createProcess($shellCommand);
38+
} else {
39+
$this->process = $this->createProcess(sprintf('%s %s', $server->getSshConnectionString(), escapeshellarg($shellCommand)));
40+
}
41+
42+
$this->process->setTimeout(null);
43+
}
44+
45+
private function createProcess(string $shellCommand): Process
46+
{
47+
if (method_exists(Process::class, 'fromShellCommandline')) {
48+
return Process::fromShellCommandline($shellCommand);
49+
}
50+
51+
return new Process($shellCommand);
52+
}
53+
54+
public function start ()
55+
{
56+
if ($this->dryRun) {
57+
return;
58+
}
59+
$this->process->start(function ($type, $buffer) {
60+
if (Process::ERR === $type) {
61+
$this->logger->log(Str::prefix(rtrim($buffer, PHP_EOL), sprintf('| [<server>%s</>] <stream>err ::</> ', $this->server)));
62+
} else {
63+
$this->logger->log(Str::prefix(rtrim($buffer, PHP_EOL), sprintf('| [<server>%s</>] <stream>out ::</> ', $this->server)));
64+
}
65+
});
66+
}
67+
68+
public function getCompletionResult ()
69+
{
70+
if ($this->dryRun) {
71+
return new TaskCompleted($this->server, '', 0);
72+
}
73+
74+
// Make sure we ran without errors
75+
// As in https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Process/Process.php#L266
76+
if (0 !== $this->process->wait()) {
77+
throw new ProcessFailedException($this->process);
78+
}
79+
80+
return new TaskCompleted($this->server, $this->process->getOutput(), $this->process->getExitCode());
81+
}
82+
}

src/Task/TaskRunner.php

+12-31
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,22 @@ public function __construct(bool $isDryRun, Logger $logger)
3333
*/
3434
public function run(Task $task): array
3535
{
36-
$results = [];
36+
// Start all processes asynchronously
37+
$processes = [];
3738
foreach ($task->getServers() as $server) {
38-
$results[] = $this->doRun($server, $server->resolveProperties($task->getShellCommand()), $task->getEnvVars());
39+
$processes[] = $this->startProcess($server, $server->resolveProperties($task->getShellCommand()), $task->getEnvVars());
3940
}
4041

41-
return $results;
42-
}
43-
44-
private function createProcess(string $shellCommand): Process
45-
{
46-
if (method_exists(Process::class, 'fromShellCommandline')) {
47-
return Process::fromShellCommandline($shellCommand);
42+
// Collect all their results
43+
$results = [];
44+
foreach ($processes as $process) {
45+
$results[] = $process->getCompletionResult();
4846
}
4947

50-
return new Process($shellCommand);
48+
return $results;
5149
}
5250

53-
private function doRun(Server $server, string $shellCommand, array $envVars): TaskCompleted
51+
private function startProcess(Server $server, string $shellCommand, array $envVars): PendingTask
5452
{
5553
if ($server->has(Property::project_dir)) {
5654
$shellCommand = sprintf('cd %s && %s', $server->get(Property::project_dir), $shellCommand);
@@ -67,26 +65,9 @@ private function doRun(Server $server, string $shellCommand, array $envVars): Ta
6765

6866
$this->logger->log(sprintf('[<server>%s</>] Executing command: <command>%s</>', $server, $shellCommand));
6967

70-
if ($this->isDryRun) {
71-
return new TaskCompleted($server, '', 0);
72-
}
73-
74-
if ($server->isLocalHost()) {
75-
$process = $this->createProcess($shellCommand);
76-
} else {
77-
$process = $this->createProcess(sprintf('%s %s', $server->getSshConnectionString(), escapeshellarg($shellCommand)));
78-
}
79-
80-
$process->setTimeout(null);
81-
82-
$process = $process->mustRun(function ($type, $buffer) {
83-
if (Process::ERR === $type) {
84-
$this->logger->log(Str::prefix(rtrim($buffer, PHP_EOL), '| <stream>err ::</> '));
85-
} else {
86-
$this->logger->log(Str::prefix(rtrim($buffer, PHP_EOL), '| <stream>out ::</> '));
87-
}
88-
});
68+
$pendingTask = new PendingTask($server, $shellCommand, $this->logger, $this->isDryRun);
69+
$pendingTask->start();
8970

90-
return new TaskCompleted($server, $process->getOutput(), $process->getExitCode());
71+
return $pendingTask;
9172
}
9273
}

0 commit comments

Comments
 (0)