Implement cpu usage stats

This commit is contained in:
Matej Bačo
2022-10-16 14:28:36 +00:00
parent 2337da5d6c
commit b79bdc56a1
7 changed files with 91 additions and 74 deletions
+3 -3
View File
@@ -650,11 +650,11 @@ App::post('/v1/execution')
App::get('/v1/health')
->param('name', '', new Text(64), 'Name of the executor.')
->desc("Get usage stats of host machine")
->desc("Get usage stats of host machine CPU usage from 0 to 100.")
->inject('response')
->action(function (string $name, Response $response) use ($orchestrationPool) {
$systemCores = System::getCPUCores();
$systemUsage = System::getCPUUtilisation() / $systemCores / 100;
$systemUsage = System::getCPUUtilisation() / $systemCores;
$functionsUsage = [];
try {
@@ -662,7 +662,7 @@ App::get('/v1/health')
$containerUsages = $orchestration->getStats(filters: [ 'label' => 'openruntimes-executor=' . $name ], cycles: 3);
foreach ($containerUsages as $containerUsage) {
$functionsUsage[$containerUsage['name']] = $containerUsage['cpu'];
$functionsUsage[$containerUsage['name']] = $containerUsage['cpu'] * 100;
}
} catch (\Exception $err) {
// TODO: @Meldiron Handle better
+7 -5
View File
@@ -2,8 +2,7 @@
require_once __DIR__ . '/../vendor/autoload.php';
use FunctionsProxy\Adapter\Random;
use FunctionsProxy\Adapter\RoundRobin;
use FunctionsProxy\Adapter\UsageBased;
use Swoole\Coroutine\Http\Client;
use Utopia\Logger\Log;
use Utopia\Logger\Logger;
@@ -41,7 +40,7 @@ $redisPool = new RedisPool(
64
);
$adapter = new Random($redisPool);
$adapter = new UsageBased($redisPool); // TODO: @Meldiron allow env variable to switch; log what is picked
function markOffline(Cache $cache, string $executorId, string $error, bool $forceShowError = false)
{
@@ -218,7 +217,10 @@ $run = function (SwooleRequest $request, SwooleResponse $response) use ($adapter
throw new Exception('Missing proxy key');
}
$executor = $adapter->getNextExecutor();
$body = \json_decode($request->getContent(), true);
$runtimeId = $body['runtimeId'] ?? null;
$executor = $adapter->getNextExecutor($runtimeId);
Console::success("Executing on " . $executor['hostname']);
@@ -247,7 +249,7 @@ $run = function (SwooleRequest $request, SwooleResponse $response) use ($adapter
$http->on('start', function () use ($redisPool) {
// Keep updating executors state
Timer::tick(30000, function (int $timerId) use ($redisPool) {
Timer::tick(15000, function (int $timerId) use ($redisPool) {
fetchExecutorsState($redisPool, false);
});
});
Generated
+18 -13
View File
@@ -2400,12 +2400,12 @@
"source": {
"type": "git",
"url": "https://github.com/utopia-php/orchestration.git",
"reference": "e3434f1d5b7f7fada785281901350ead2ce10465"
"reference": "5fb741ea8152167966ec51e17e78cfbc691d1a1f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/orchestration/zipball/e3434f1d5b7f7fada785281901350ead2ce10465",
"reference": "e3434f1d5b7f7fada785281901350ead2ce10465",
"url": "https://api.github.com/repos/utopia-php/orchestration/zipball/5fb741ea8152167966ec51e17e78cfbc691d1a1f",
"reference": "5fb741ea8152167966ec51e17e78cfbc691d1a1f",
"shasum": ""
},
"require": {
@@ -2447,7 +2447,7 @@
"issues": "https://github.com/utopia-php/orchestration/issues",
"source": "https://github.com/utopia-php/orchestration/tree/feat-usage-stats"
},
"time": "2022-10-14T07:14:57+00:00"
"time": "2022-10-14T19:36:45+00:00"
},
{
"name": "utopia-php/preloader",
@@ -3420,25 +3420,30 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.6.1",
"version": "1.6.2",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "77a32518733312af16a44300404e945338981de3"
"reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3",
"reference": "77a32518733312af16a44300404e945338981de3",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
"reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"php": "^7.4 || ^8.0",
"phpdocumentor/reflection-common": "^2.0"
},
"require-dev": {
"ext-tokenizer": "*",
"psalm/phar": "^4.8"
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-phpunit": "^1.1",
"phpunit/phpunit": "^9.5",
"rector/rector": "^0.13.9",
"vimeo/psalm": "^4.25"
},
"type": "library",
"extra": {
@@ -3464,9 +3469,9 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2"
},
"time": "2022-03-15T21:29:03+00:00"
"time": "2022-10-14T12:47:21+00:00"
},
{
"name": "phpspec/prophecy",
@@ -5401,5 +5406,5 @@
"platform-overrides": {
"php": "8.0"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.2.0"
}
+1 -1
View File
@@ -60,5 +60,5 @@ abstract class Adapter
return $responseExecutors;
}
abstract public function getNextExecutor(): array;
abstract public function getNextExecutor(?string $contaierId): array;
}
+1 -3
View File
@@ -6,13 +6,11 @@ use FunctionsProxy\Adapter;
class Random extends Adapter
{
public function getNextExecutor(): array
public function getNextExecutor(?string $contaierId): array
{
$executors = $this->getExecutors();
$executor = $executors[\array_rand($executors)] ?? null;
\var_dump($executor);
return $executor ?? null;
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ class RoundRobin extends Adapter
{
private $currentIndex = 0; // TODO: @Meldiron Put into utopia app resource or/and utopia registry
public function getNextExecutor(): array
public function getNextExecutor(?string $contaierId): array
{
$executors = $this->getExecutors();
$executor = $executors[$this->currentIndex] ?? null;
+60 -48
View File
@@ -6,62 +6,74 @@ use FunctionsProxy\Adapter;
class UsageBased extends Adapter
{
public function getNextExecutor($contaierId): array
public function getNextExecutor(?string $contaierId): array
{
$executors = $this->getExecutors();
/*
appwrite-functions-proxy | array(3) {
appwrite-functions-proxy | ["id"]=>
appwrite-functions-proxy | string(4) "exc2"
appwrite-functions-proxy | ["hostname"]=>
appwrite-functions-proxy | string(18) "appwrite-executor2"
appwrite-functions-proxy | ["state"]=>
appwrite-functions-proxy | array(2) {
appwrite-functions-proxy | ["status"]=>
appwrite-functions-proxy | string(6) "online"
appwrite-functions-proxy | ["health"]=>
appwrite-functions-proxy | array(3) {
appwrite-functions-proxy | ["status"]=>
appwrite-functions-proxy | string(4) "pass"
appwrite-functions-proxy | ["hostUsage"]=>
appwrite-functions-proxy | float(0.348125)
appwrite-functions-proxy | ["functionsUsage"]=>
appwrite-functions-proxy | array(0) {
// container1: 0.5
// container1: 0.2
appwrite-functions-proxy | }
appwrite-functions-proxy | }
appwrite-functions-proxy | }
appwrite-functions-proxy | }
*/
// Remove offline and unknown-status executors
$executors = \array_filter($executors, fn($executor) => (($executor['state'] ?? [])['status']) ?? 'offline' === 'online');
// low, mid, high
// Ideal executor is one that is already running a contianer for this function deployment
$idealExecutors = [];
// 10% on host, 90% on chontainer
// 90% on host, 10% on chontainer
// TODO: @Meldiron Proper functionality
// TODO: @Meldiron Use this adapter and test it
/*
// If 2+ running, do this to sort:
$hostUsage = 20;
$containerUsage = 40;
$hostWeight = 0.33;
$contiainerWeight = 0.66;
$totalUsage = $hostUsage * $hostWeight + $containerUsage * $contiainerWeight;
// If all existing are above 80%, we should start on new executor ideally
if(host < 0.8 & container < 0.8) {
// For whatever reason we don't know container yet. We consider all executors ideal
if(!(isset($contaierId))) {
$idealExecutors = \array_map(fn($executor) => $executor['id'], $executors);
} else {
foreach($executors as $executor) {
$executorId = $executor['id'] ?? '';
$executorState = $executor['state']['health'] ?? [];
$hostUsage = intval($executorState['hostUsage'] ?? 100);
$containerUsage = intval(($executorState['functionsUsage'] ?? [])[$executorId . '-' . $contaierId] ?? 100);
// If host or contianer usage above 80, executor is not ideal
if($hostUsage < 80 && $containerUsage < 80) {
$idealExecutors[] = $executorId;
}
}
}
*/
$executor = $executors[\array_rand($executors)] ?? null;
if(\count($idealExecutors) <= 0) {
// If no ideal, let's consider all of them ideal. Since there is no prefference.
$idealExecutors = \array_map(fn($executor) => $executor['id'], $executors);
}
// Sort containers based on usage
$sortedExecutors = [];
$hostWeight = 0.3;
$containerWeight = 0.7;
foreach($idealExecutors as $executorId) {
$executorIndex = \array_search($executorId, \array_map(fn($executor) => $executor['id'], $executors));
$executor = $executors[$executorIndex];
if(!isset($executor)) {
continue;
}
$executorState = $executor['state']['health'] ?? [];
$hostUsage = intval($executorState['hostUsage'] ?? 10);
$containerUsage = intval(($executorState['functionsUsage'] ?? [])[$executorId . '-' . $contaierId] ?? 100);
$usageIndex = ($hostUsage * $hostWeight) + ($containerUsage * $containerWeight);
$sortedExecutors[$executorId] = $usageIndex;
}
\asort($sortedExecutors);
// Pick the least used executor
$idealExecutorId = $idealExecutors[0] ?? null;
// Null if no executor found
if($idealExecutorId === null) {
return null;
}
$executorIndex = \array_search($idealExecutorId, \array_map(fn($executor) => $executor['id'], $executors));
$executor = $executors[$executorIndex];
// Null if can't match executor to ID
return $executor ?? null;
}
}