Skip to content
Open
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
70 changes: 68 additions & 2 deletions src/Executor/Runner/Docker.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,31 @@

class Docker extends Adapter
{
/**
* Max idle curl handles (open connections) kept per runtime
*/
private const int RUNTIME_HANDLES_MAX = 128;

/**
* Seconds after which idle curl handles of an unused runtime are closed
*/
private const int RUNTIME_HANDLES_TTL = 60;

/**
* Idle curl handles per runtime hostname. Reusing a handle reuses its open connection,
* instead of opening (and leaving in TIME_WAIT) a new connection for every execution.
*
* @var array<string, \CurlHandle[]>
*/
private array $runtimeHandles = [];

/**
* Last time idle curl handles were used, per runtime hostname
*
* @var array<string, int>
*/
private array $runtimeHandlesUsed = [];

/**
* @param string[] $networks
*/
Expand Down Expand Up @@ -835,7 +860,7 @@ public function createExecution(
$errNo = -1;
$executorResponse = '';

$ch = \curl_init();
$ch = $this->getRuntimeHandle($hostname);

$body = \json_encode([
'variables' => $variables,
Expand Down Expand Up @@ -877,6 +902,8 @@ public function createExecution(
];
}

$this->releaseRuntimeHandle($hostname, $ch);

// Extract response
$executorResponse = json_decode(\strval($executorResponse), false);

Expand Down Expand Up @@ -906,7 +933,7 @@ public function createExecution(
$errNo = -1;
$executorResponse = '';

$ch = \curl_init();
$ch = $this->getRuntimeHandle($hostname);

$responseHeaders = [];

Expand Down Expand Up @@ -986,6 +1013,8 @@ public function createExecution(
];
}

$this->releaseRuntimeHandle($hostname, $ch);

// Extract logs and errors from file based on fileId in header
$fileId = $responseHeaders['x-open-runtimes-log-id'] ?? '';
if (\is_array($fileId)) {
Expand Down Expand Up @@ -1177,6 +1206,43 @@ public function cleanup(): void
Console::success('Cleanup finished.');
}

/**
* Get a curl handle for a request to a runtime, reusing an idle one if available.
*/
private function getRuntimeHandle(string $hostname): \CurlHandle
{
if (($this->runtimeHandles[$hostname] ?? []) === []) {
return \curl_init();
}

$ch = \array_pop($this->runtimeHandles[$hostname]);
\curl_reset($ch); // Keeps the open connection

return $ch;
}

/**
* Return a curl handle after a successful request, so its connection can be reused.
*/
private function releaseRuntimeHandle(string $hostname, \CurlHandle $ch): void
{
$now = \time();

// Close connections of runtimes not used for a while, they are likely removed
foreach ($this->runtimeHandlesUsed as $key => $used) {
if ($now - $used > self::RUNTIME_HANDLES_TTL) {
unset($this->runtimeHandles[$key], $this->runtimeHandlesUsed[$key]);
}
}

$this->runtimeHandles[$hostname] ??= [];
if (\count($this->runtimeHandles[$hostname]) < self::RUNTIME_HANDLES_MAX) {
$this->runtimeHandles[$hostname][] = $ch;
}

$this->runtimeHandlesUsed[$hostname] = $now;
}

public function getRuntimes(): mixed
{
$runtimes = [];
Expand Down
Loading