2022-12-05 06:06:23 +13:00
|
|
|
<?php
|
|
|
|
|
|
|
|
require_once __DIR__ . '/../worker.php';
|
|
|
|
|
|
|
|
use Swoole\Timer;
|
2022-12-07 03:17:56 +13:00
|
|
|
use Utopia\App;
|
2022-12-29 05:27:26 +13:00
|
|
|
use Utopia\Cache\Cache;
|
2022-12-07 00:36:17 +13:00
|
|
|
use Utopia\Database\Database;
|
2022-12-13 20:35:05 +13:00
|
|
|
use Utopia\Database\DateTime;
|
2022-12-07 00:36:17 +13:00
|
|
|
use Utopia\Database\Document;
|
2022-12-13 20:35:05 +13:00
|
|
|
use Utopia\Database\Exception\Duplicate;
|
2022-12-07 00:36:17 +13:00
|
|
|
use Utopia\Database\Validator\Authorization;
|
2022-12-05 06:06:23 +13:00
|
|
|
use Utopia\Queue\Message;
|
2022-12-07 00:36:17 +13:00
|
|
|
use Utopia\CLI\Console;
|
2022-12-29 05:27:26 +13:00
|
|
|
use Utopia\Queue\Server;
|
|
|
|
use Utopia\Registry\Registry;
|
2022-12-05 06:06:23 +13:00
|
|
|
|
2022-12-09 02:55:18 +13:00
|
|
|
Authorization::disable();
|
|
|
|
Authorization::setDefaultStatus(false);
|
|
|
|
|
2022-12-07 00:36:17 +13:00
|
|
|
$stats = [];
|
|
|
|
|
2022-12-09 08:06:55 +13:00
|
|
|
$periods['1h'] = 'Y-m-d H:00';
|
|
|
|
$periods['1d'] = 'Y-m-d 00:00';
|
2022-12-16 06:00:05 +13:00
|
|
|
//$periods['1m'] = 'Y-m-1 00:00';
|
2022-12-09 08:06:55 +13:00
|
|
|
$periods['inf'] = '0000-00-00 00:00';
|
2022-12-07 00:36:17 +13:00
|
|
|
|
2023-05-08 22:45:53 +12:00
|
|
|
const INFINITY_PERIOD = '_inf_';
|
2023-02-01 07:27:34 +13:00
|
|
|
|
2022-12-29 05:27:26 +13:00
|
|
|
/**
|
2023-05-08 19:32:29 +12:00
|
|
|
* On Documents that tied by relations like functions>deployments>build || documents>collection>database || buckets>files.
|
|
|
|
* When we remove a parent document we need to deduct his children aggregation from the project scope.
|
2022-12-29 05:27:26 +13:00
|
|
|
*/
|
|
|
|
Server::setResource('reduce', function (Cache $cache, Registry $register, $pools) {
|
|
|
|
return function ($database, $projectInternalId, Document $document, array &$metrics) use ($pools, $cache, $register): void {
|
|
|
|
try {
|
|
|
|
$dbForProject = new Database(
|
|
|
|
$pools
|
|
|
|
->get($database)
|
|
|
|
->pop()
|
|
|
|
->getResource(),
|
|
|
|
$cache
|
|
|
|
);
|
|
|
|
|
|
|
|
$dbForProject->setNamespace('_' . $projectInternalId);
|
|
|
|
|
|
|
|
switch (true) {
|
2022-12-30 00:32:00 +13:00
|
|
|
case $document->getCollection() === 'users': // users
|
2023-02-06 09:07:46 +13:00
|
|
|
$sessions = count($document->getAttribute(METRIC_SESSIONS, 0));
|
2022-12-29 05:27:26 +13:00
|
|
|
if (!empty($sessions)) {
|
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_SESSIONS,
|
2022-12-29 05:27:26 +13:00
|
|
|
'value' => ($sessions * -1),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case $document->getCollection() === 'databases': // databases
|
2023-05-08 22:45:53 +12:00
|
|
|
$collections = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS)));
|
|
|
|
$documents = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS)));
|
2022-12-29 05:27:26 +13:00
|
|
|
if (!empty($collections['value'])) {
|
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_COLLECTIONS,
|
2022-12-29 05:27:26 +13:00
|
|
|
'value' => ($collections['value'] * -1),
|
|
|
|
];
|
2022-12-30 00:32:00 +13:00
|
|
|
}
|
2023-01-13 06:41:14 +13:00
|
|
|
|
2022-12-30 00:32:00 +13:00
|
|
|
if (!empty($documents['value'])) {
|
2022-12-29 05:27:26 +13:00
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_DOCUMENTS,
|
2022-12-29 05:27:26 +13:00
|
|
|
'value' => ($documents['value'] * -1),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections
|
|
|
|
$parts = explode('_', $document->getCollection());
|
2023-02-07 03:03:45 +13:00
|
|
|
$databaseInternalId = $parts[1] ?? 0;
|
2023-05-08 22:45:53 +12:00
|
|
|
$documents = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS)));
|
2022-12-29 07:06:57 +13:00
|
|
|
|
2022-12-29 05:27:26 +13:00
|
|
|
if (!empty($documents['value'])) {
|
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_DOCUMENTS,
|
2022-12-29 05:27:26 +13:00
|
|
|
'value' => ($documents['value'] * -1),
|
|
|
|
];
|
2022-12-29 07:06:57 +13:00
|
|
|
$metrics[] = [
|
2023-02-07 03:03:45 +13:00
|
|
|
'key' => str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_DOCUMENTS),
|
2022-12-29 07:06:57 +13:00
|
|
|
'value' => ($documents['value'] * -1),
|
|
|
|
];
|
2022-12-29 05:27:26 +13:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case $document->getCollection() === 'buckets':
|
2023-05-08 22:45:53 +12:00
|
|
|
$files = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES)));
|
|
|
|
$storage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE)));
|
2022-12-29 05:27:26 +13:00
|
|
|
|
|
|
|
if (!empty($files['value'])) {
|
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_FILES,
|
2022-12-29 05:27:26 +13:00
|
|
|
'value' => ($files['value'] * -1),
|
|
|
|
];
|
2023-01-13 06:41:14 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($storage['value'])) {
|
2022-12-29 05:27:26 +13:00
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_FILES_STORAGE,
|
2022-12-29 05:27:26 +13:00
|
|
|
'value' => ($storage['value'] * -1),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case $document->getCollection() === 'functions':
|
2023-05-08 22:45:53 +12:00
|
|
|
$deployments = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS)));
|
|
|
|
$deploymentsStorage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE)));
|
|
|
|
$builds = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS)));
|
|
|
|
$buildsStorage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE)));
|
|
|
|
$buildsCompute = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE)));
|
|
|
|
$executions = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS)));
|
|
|
|
$executionsCompute = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE)));
|
2022-12-29 05:27:26 +13:00
|
|
|
|
|
|
|
if (!empty($deployments['value'])) {
|
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_DEPLOYMENTS,
|
2022-12-29 05:27:26 +13:00
|
|
|
'value' => ($deployments['value'] * -1),
|
|
|
|
];
|
2023-01-13 06:41:14 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($deploymentsStorage['value'])) {
|
2022-12-29 05:27:26 +13:00
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_DEPLOYMENTS_STORAGE,
|
2022-12-29 05:27:26 +13:00
|
|
|
'value' => ($deploymentsStorage['value'] * -1),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($builds['value'])) {
|
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_BUILDS,
|
2022-12-29 05:27:26 +13:00
|
|
|
'value' => ($builds['value'] * -1),
|
|
|
|
];
|
2023-01-13 06:41:14 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($buildsStorage['value'])) {
|
2022-12-29 05:27:26 +13:00
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_BUILDS_STORAGE,
|
2022-12-29 05:27:26 +13:00
|
|
|
'value' => ($buildsStorage['value'] * -1),
|
|
|
|
];
|
2023-01-13 06:41:14 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($buildsCompute['value'])) {
|
2022-12-29 05:27:26 +13:00
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_BUILDS_COMPUTE,
|
2022-12-29 05:27:26 +13:00
|
|
|
'value' => ($buildsCompute['value'] * -1),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($executions['value'])) {
|
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_EXECUTIONS,
|
2023-03-23 05:32:44 +13:00
|
|
|
'value' => ($executions['value'] * -1),
|
2022-12-29 05:27:26 +13:00
|
|
|
];
|
2023-01-13 06:41:14 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($executionsCompute['value'])) {
|
2022-12-29 05:27:26 +13:00
|
|
|
$metrics[] = [
|
2023-02-06 09:07:46 +13:00
|
|
|
'key' => METRIC_EXECUTIONS_COMPUTE,
|
2022-12-29 05:27:26 +13:00
|
|
|
'value' => ($executionsCompute['value'] * -1),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} catch (\Exception $e) {
|
2022-12-30 00:32:00 +13:00
|
|
|
console::error("[reducer] " . " {DateTime::now()} " . " {$projectInternalId} " . " {$e->getMessage()}");
|
2022-12-29 05:27:26 +13:00
|
|
|
} finally {
|
|
|
|
$pools->reclaim();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}, ['cache', 'register', 'pools']);
|
|
|
|
|
|
|
|
|
2022-12-05 06:06:23 +13:00
|
|
|
$server->job()
|
|
|
|
->inject('message')
|
2022-12-29 05:27:26 +13:00
|
|
|
->inject('reduce')
|
|
|
|
|
|
|
|
->action(function (Message $message, callable $reduce) use (&$stats) {
|
2022-12-22 08:03:09 +13:00
|
|
|
|
2022-12-05 06:06:23 +13:00
|
|
|
$payload = $message->getPayload() ?? [];
|
2022-12-07 00:36:17 +13:00
|
|
|
$project = new Document($payload['project'] ?? []);
|
2022-12-22 08:03:09 +13:00
|
|
|
$projectId = $project->getInternalId();
|
2022-12-29 05:27:26 +13:00
|
|
|
foreach ($payload['reduce'] ?? [] as $document) {
|
2022-12-30 00:32:00 +13:00
|
|
|
if (empty($document)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-12-29 05:27:26 +13:00
|
|
|
$reduce(
|
|
|
|
database: $project->getAttribute('database'),
|
|
|
|
projectInternalId: $project->getInternalId(),
|
2022-12-30 00:32:00 +13:00
|
|
|
document: new Document($document),
|
2022-12-29 05:27:26 +13:00
|
|
|
metrics: $payload['metrics'],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-12-22 08:03:09 +13:00
|
|
|
$stats[$projectId]['database'] = $project->getAttribute('database');
|
2022-12-05 06:06:23 +13:00
|
|
|
foreach ($payload['metrics'] ?? [] as $metric) {
|
2022-12-22 08:03:09 +13:00
|
|
|
if (!isset($stats[$projectId]['keys'][$metric['key']])) {
|
|
|
|
$stats[$projectId]['keys'][$metric['key']] = $metric['value'];
|
2022-12-05 06:06:23 +13:00
|
|
|
continue;
|
|
|
|
}
|
2022-12-22 08:03:09 +13:00
|
|
|
$stats[$projectId]['keys'][$metric['key']] += $metric['value'];
|
2022-12-05 06:06:23 +13:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
$server
|
|
|
|
->workerStart()
|
2022-12-07 00:36:17 +13:00
|
|
|
->inject('register')
|
|
|
|
->inject('cache')
|
|
|
|
->inject('pools')
|
|
|
|
->action(function ($register, $cache, $pools) use ($periods, &$stats) {
|
2022-12-22 08:03:09 +13:00
|
|
|
Timer::tick(30000, function () use ($register, $cache, $pools, $periods, &$stats) {
|
2022-12-14 20:34:04 +13:00
|
|
|
|
2022-12-22 08:03:09 +13:00
|
|
|
$offset = count($stats);
|
|
|
|
$projects = array_slice($stats, 0, $offset, true);
|
|
|
|
array_splice($stats, 0, $offset);
|
2022-12-14 20:34:04 +13:00
|
|
|
|
2022-12-22 08:03:09 +13:00
|
|
|
foreach ($projects as $projectInternalId => $project) {
|
|
|
|
try {
|
|
|
|
$dbForProject = new Database(
|
|
|
|
$pools
|
2022-12-29 05:27:26 +13:00
|
|
|
->get($project['database'])
|
|
|
|
->pop()
|
|
|
|
->getResource(),
|
2022-12-22 08:03:09 +13:00
|
|
|
$cache
|
|
|
|
);
|
|
|
|
|
|
|
|
$dbForProject->setNamespace('_' . $projectInternalId);
|
|
|
|
|
2023-02-06 09:07:46 +13:00
|
|
|
foreach ($project['keys'] ?? [] as $key => $value) {
|
2022-12-22 08:03:09 +13:00
|
|
|
if ($value == 0) {
|
|
|
|
continue;
|
2022-12-07 00:36:17 +13:00
|
|
|
}
|
2022-12-13 20:35:05 +13:00
|
|
|
|
2022-12-22 08:03:09 +13:00
|
|
|
foreach ($periods as $period => $format) {
|
|
|
|
$time = 'inf' === $period ? null : date($format, time());
|
|
|
|
$id = \md5("{$time}_{$period}_{$key}");
|
|
|
|
|
|
|
|
try {
|
|
|
|
$dbForProject->createDocument('stats', new Document([
|
|
|
|
'$id' => $id,
|
|
|
|
'period' => $period,
|
|
|
|
'time' => $time,
|
|
|
|
'metric' => $key,
|
|
|
|
'value' => $value,
|
|
|
|
'region' => App::getEnv('_APP_REGION', 'default'),
|
|
|
|
]));
|
|
|
|
} catch (Duplicate $th) {
|
|
|
|
if ($value < 0) {
|
|
|
|
$dbForProject->decreaseDocumentAttribute(
|
|
|
|
'stats',
|
|
|
|
$id,
|
|
|
|
'value',
|
|
|
|
abs($value)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$dbForProject->increaseDocumentAttribute(
|
|
|
|
'stats',
|
|
|
|
$id,
|
|
|
|
'value',
|
|
|
|
$value
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-12-07 00:36:17 +13:00
|
|
|
}
|
2023-02-06 09:07:46 +13:00
|
|
|
if (!empty($project['keys'])) {
|
|
|
|
$dbForProject->createDocument('statsLogger', new Document([
|
|
|
|
'time' => DateTime::now(),
|
|
|
|
'metrics' => $project['keys'],
|
|
|
|
]));
|
|
|
|
}
|
2022-12-22 08:03:09 +13:00
|
|
|
} catch (\Exception $e) {
|
2023-07-13 01:02:42 +12:00
|
|
|
$now = DateTime::now();
|
|
|
|
console::error("[Error] " . " Time: {$now} " . " projectInternalId: {$projectInternalId}" . " File: {$e->getFile()}" . " Line: {$e->getLine()} " . " message: {$e->getMessage()}");
|
2022-12-22 08:03:09 +13:00
|
|
|
} finally {
|
|
|
|
$pools->reclaim();
|
2022-12-12 22:37:10 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2022-12-05 06:06:23 +13:00
|
|
|
});
|
2022-12-13 20:35:05 +13:00
|
|
|
$server->start();
|