2023-10-25 08:38:44 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace Appwrite\Platform\Workers;
|
|
|
|
|
|
2025-01-27 04:33:41 +00:00
|
|
|
use Appwrite\Event\Usage as UsageEvent;
|
2024-02-21 12:29:28 +00:00
|
|
|
use Appwrite\Event\UsageDump;
|
2025-01-27 04:33:41 +00:00
|
|
|
use Appwrite\Platform\Action;
|
2024-03-06 17:34:21 +00:00
|
|
|
use Exception;
|
2025-01-27 04:33:41 +00:00
|
|
|
use Throwable;
|
2023-10-25 08:38:44 +00:00
|
|
|
use Utopia\CLI\Console;
|
2025-01-27 04:33:41 +00:00
|
|
|
use Utopia\Database\Database;
|
2024-01-18 16:52:20 +00:00
|
|
|
use Utopia\Database\DateTime;
|
2023-10-25 08:38:44 +00:00
|
|
|
use Utopia\Database\Document;
|
2025-01-27 04:33:41 +00:00
|
|
|
use Utopia\Database\Query;
|
2023-10-25 08:38:44 +00:00
|
|
|
use Utopia\Queue\Message;
|
2024-04-01 11:02:47 +00:00
|
|
|
use Utopia\System\System;
|
2023-10-25 08:38:44 +00:00
|
|
|
|
|
|
|
|
class Usage extends Action
|
|
|
|
|
{
|
2025-01-27 06:11:58 +00:00
|
|
|
/**
|
|
|
|
|
* Date format for different periods
|
|
|
|
|
*/
|
|
|
|
|
protected array $periods = [
|
|
|
|
|
'1h' => 'Y-m-d H:00',
|
|
|
|
|
'1d' => 'Y-m-d 00:00',
|
|
|
|
|
'inf' => '0000-00-00 00:00'
|
|
|
|
|
];
|
2025-01-27 04:33:41 +00:00
|
|
|
/**
|
|
|
|
|
* Log Error Callback
|
|
|
|
|
*
|
|
|
|
|
* @var callable
|
|
|
|
|
*/
|
|
|
|
|
protected mixed $logError;
|
2024-01-28 09:28:59 +00:00
|
|
|
private array $stats = [];
|
|
|
|
|
private int $lastTriggeredTime = 0;
|
2024-12-09 15:42:05 +00:00
|
|
|
private int $aggregationInterval = 20;
|
2024-01-28 09:28:59 +00:00
|
|
|
private int $keys = 0;
|
|
|
|
|
private const INFINITY_PERIOD = '_inf_';
|
2024-12-10 13:18:21 +00:00
|
|
|
private const KEYS_THRESHOLD = 20000;
|
2024-01-28 09:28:59 +00:00
|
|
|
|
2023-10-25 08:38:44 +00:00
|
|
|
public static function getName(): string
|
|
|
|
|
{
|
|
|
|
|
return 'usage';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function __construct()
|
|
|
|
|
{
|
|
|
|
|
$this
|
2025-01-23 11:09:08 +00:00
|
|
|
->desc('Usage worker')
|
|
|
|
|
->inject('message')
|
|
|
|
|
->inject('project')
|
|
|
|
|
->inject('getProjectDB')
|
|
|
|
|
->inject('queueForUsageDump')
|
2025-01-27 04:33:41 +00:00
|
|
|
->inject('getLogsDB')
|
|
|
|
|
->inject('dbForPlatform')
|
|
|
|
|
->inject('logError')
|
2025-01-23 11:09:08 +00:00
|
|
|
->callback([$this, 'action']);
|
2024-01-28 09:28:59 +00:00
|
|
|
|
2024-12-09 15:42:05 +00:00
|
|
|
$this->aggregationInterval = (int) System::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '20');
|
2024-01-28 09:28:59 +00:00
|
|
|
$this->lastTriggeredTime = time();
|
2023-10-25 08:38:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param Message $message
|
2025-01-16 06:05:22 +00:00
|
|
|
* @param Document $project
|
2023-12-24 18:38:15 +00:00
|
|
|
* @param callable $getProjectDB
|
2024-01-28 09:28:59 +00:00
|
|
|
* @param UsageDump $queueForUsageDump
|
2023-10-25 08:38:44 +00:00
|
|
|
* @return void
|
|
|
|
|
* @throws \Utopia\Database\Exception
|
2023-12-24 18:38:15 +00:00
|
|
|
* @throws Exception
|
2023-10-25 08:38:44 +00:00
|
|
|
*/
|
2025-01-27 04:33:41 +00:00
|
|
|
public function action(Message $message, Document $project, callable $getProjectDB, UsageDump $queueForUsageDump, callable $getLogsDB, Database $dbForPlatform, callable $logError): void
|
2023-10-25 08:38:44 +00:00
|
|
|
{
|
2025-01-27 04:33:41 +00:00
|
|
|
$this->logError = $logError;
|
|
|
|
|
|
2023-10-25 08:38:44 +00:00
|
|
|
$payload = $message->getPayload() ?? [];
|
|
|
|
|
if (empty($payload)) {
|
|
|
|
|
throw new Exception('Missing payload');
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 18:20:47 +00:00
|
|
|
if (empty($project->getAttribute('database'))) {
|
2024-12-12 15:46:04 +00:00
|
|
|
var_dump($payload);
|
2024-12-12 19:20:55 +00:00
|
|
|
return;
|
2024-12-12 15:46:04 +00:00
|
|
|
}
|
|
|
|
|
|
2025-01-27 04:33:41 +00:00
|
|
|
$type = $payload['type'] ?? UsageEvent::TYPE_USAGE_DUMP;
|
|
|
|
|
if ($type === UsageEvent::TYPE_USAGE_COUNT) {
|
|
|
|
|
$this->countForProject($dbForPlatform, $getLogsDB, $getProjectDB, $project);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 08:38:44 +00:00
|
|
|
$projectId = $project->getInternalId();
|
|
|
|
|
foreach ($payload['reduce'] ?? [] as $document) {
|
|
|
|
|
if (empty($document)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->reduce(
|
2023-12-24 18:38:15 +00:00
|
|
|
project: $project,
|
2023-10-25 08:38:44 +00:00
|
|
|
document: new Document($document),
|
|
|
|
|
metrics: $payload['metrics'],
|
2023-12-24 18:38:15 +00:00
|
|
|
getProjectDB: $getProjectDB
|
2023-10-25 08:38:44 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 18:20:47 +00:00
|
|
|
|
2024-12-09 15:42:05 +00:00
|
|
|
$this->stats[$projectId]['project'] = [
|
2024-12-12 10:30:36 +00:00
|
|
|
'$id' => $project->getId(),
|
2024-12-09 15:42:05 +00:00
|
|
|
'$internalId' => $project->getInternalId(),
|
|
|
|
|
'database' => $project->getAttribute('database'),
|
|
|
|
|
];
|
2024-02-14 21:05:01 +00:00
|
|
|
$this->stats[$projectId]['receivedAt'] = DateTime::now();
|
2023-10-25 08:38:44 +00:00
|
|
|
foreach ($payload['metrics'] ?? [] as $metric) {
|
2024-03-06 17:34:21 +00:00
|
|
|
$this->keys++;
|
2024-01-28 09:28:59 +00:00
|
|
|
if (!isset($this->stats[$projectId]['keys'][$metric['key']])) {
|
|
|
|
|
$this->stats[$projectId]['keys'][$metric['key']] = $metric['value'];
|
2023-10-25 08:38:44 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2024-01-28 09:28:59 +00:00
|
|
|
|
|
|
|
|
$this->stats[$projectId]['keys'][$metric['key']] += $metric['value'];
|
2023-10-25 08:38:44 +00:00
|
|
|
}
|
2024-02-09 11:22:48 +00:00
|
|
|
|
2024-02-21 12:29:28 +00:00
|
|
|
// If keys crossed threshold or X time passed since the last send and there are some keys in the array ($this->stats)
|
2024-01-28 09:28:59 +00:00
|
|
|
if (
|
|
|
|
|
$this->keys >= self::KEYS_THRESHOLD ||
|
2024-12-09 15:42:05 +00:00
|
|
|
(time() - $this->lastTriggeredTime > $this->aggregationInterval && $this->keys > 0)
|
2024-01-28 09:28:59 +00:00
|
|
|
) {
|
2024-02-21 12:29:28 +00:00
|
|
|
Console::warning('[' . DateTime::now() . '] Aggregated ' . $this->keys . ' keys');
|
2024-02-08 19:08:29 +00:00
|
|
|
|
2024-01-28 09:28:59 +00:00
|
|
|
$queueForUsageDump
|
2024-01-30 11:24:57 +00:00
|
|
|
->setStats($this->stats)
|
2024-01-28 09:28:59 +00:00
|
|
|
->trigger();
|
|
|
|
|
|
2024-01-30 11:24:57 +00:00
|
|
|
$this->stats = [];
|
2024-01-28 09:28:59 +00:00
|
|
|
$this->keys = 0;
|
|
|
|
|
$this->lastTriggeredTime = time();
|
2023-10-25 08:38:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-06 17:34:21 +00: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.
|
|
|
|
|
* @param Document $project
|
|
|
|
|
* @param Document $document
|
|
|
|
|
* @param array $metrics
|
|
|
|
|
* @param callable $getProjectDB
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
2023-12-24 18:38:15 +00:00
|
|
|
private function reduce(Document $project, Document $document, array &$metrics, callable $getProjectDB): void
|
2023-10-25 08:38:44 +00:00
|
|
|
{
|
2023-12-24 18:38:15 +00:00
|
|
|
$dbForProject = $getProjectDB($project);
|
2023-10-25 08:38:44 +00:00
|
|
|
|
2023-12-24 18:38:15 +00:00
|
|
|
try {
|
2023-10-25 08:38:44 +00:00
|
|
|
switch (true) {
|
|
|
|
|
case $document->getCollection() === 'users': // users
|
|
|
|
|
$sessions = count($document->getAttribute(METRIC_SESSIONS, 0));
|
|
|
|
|
if (!empty($sessions)) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_SESSIONS,
|
|
|
|
|
'value' => ($sessions * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case $document->getCollection() === 'databases': // databases
|
2024-03-05 09:36:23 +00:00
|
|
|
$collections = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS)));
|
|
|
|
|
$documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS)));
|
2023-10-25 08:38:44 +00:00
|
|
|
if (!empty($collections['value'])) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_COLLECTIONS,
|
|
|
|
|
'value' => ($collections['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($documents['value'])) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_DOCUMENTS,
|
|
|
|
|
'value' => ($documents['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections
|
|
|
|
|
$parts = explode('_', $document->getCollection());
|
|
|
|
|
$databaseInternalId = $parts[1] ?? 0;
|
2024-03-05 09:36:23 +00:00
|
|
|
$documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS)));
|
2023-10-25 08:38:44 +00:00
|
|
|
|
|
|
|
|
if (!empty($documents['value'])) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_DOCUMENTS,
|
|
|
|
|
'value' => ($documents['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_DOCUMENTS),
|
|
|
|
|
'value' => ($documents['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case $document->getCollection() === 'buckets':
|
2024-03-05 09:36:23 +00:00
|
|
|
$files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES)));
|
|
|
|
|
$storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE)));
|
2023-10-25 08:38:44 +00:00
|
|
|
|
|
|
|
|
if (!empty($files['value'])) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_FILES,
|
|
|
|
|
'value' => ($files['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($storage['value'])) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_FILES_STORAGE,
|
|
|
|
|
'value' => ($storage['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case $document->getCollection() === 'functions':
|
2024-03-05 09:36:23 +00:00
|
|
|
$deployments = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS)));
|
|
|
|
|
$deploymentsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE)));
|
|
|
|
|
$builds = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS)));
|
2024-05-28 12:59:28 +00:00
|
|
|
$buildsSuccess = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_SUCCESS)));
|
|
|
|
|
$buildsFailed = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_FAILED)));
|
2024-03-05 09:36:23 +00:00
|
|
|
$buildsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE)));
|
|
|
|
|
$buildsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE)));
|
2024-05-28 12:59:28 +00:00
|
|
|
$buildsComputeSuccess = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS)));
|
|
|
|
|
$buildsComputeFailed = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED)));
|
2024-03-05 09:36:23 +00:00
|
|
|
$executions = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS)));
|
|
|
|
|
$executionsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE)));
|
2023-10-25 08:38:44 +00:00
|
|
|
|
|
|
|
|
if (!empty($deployments['value'])) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_DEPLOYMENTS,
|
|
|
|
|
'value' => ($deployments['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($deploymentsStorage['value'])) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_DEPLOYMENTS_STORAGE,
|
|
|
|
|
'value' => ($deploymentsStorage['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($builds['value'])) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_BUILDS,
|
|
|
|
|
'value' => ($builds['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 12:59:28 +00:00
|
|
|
if (!empty($buildsSuccess['value'])) {
|
|
|
|
|
$metrics[] = [
|
|
|
|
|
'key' => METRIC_BUILDS_SUCCESS,
|
|
|
|
|
'value' => ($buildsSuccess['value'] * -1),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($buildsFailed['value'])) {
|
|
|
|
|
$metrics[] = [
|
|
|
|
|
'key' => METRIC_BUILDS_FAILED,
|
|
|
|
|
'value' => ($buildsFailed['value'] * -1),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 08:38:44 +00:00
|
|
|
if (!empty($buildsStorage['value'])) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_BUILDS_STORAGE,
|
|
|
|
|
'value' => ($buildsStorage['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($buildsCompute['value'])) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_BUILDS_COMPUTE,
|
|
|
|
|
'value' => ($buildsCompute['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 12:59:28 +00:00
|
|
|
if (!empty($buildsComputeSuccess['value'])) {
|
|
|
|
|
$metrics[] = [
|
|
|
|
|
'key' => METRIC_BUILDS_COMPUTE_SUCCESS,
|
|
|
|
|
'value' => ($buildsComputeSuccess['value'] * -1),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($buildsComputeFailed['value'])) {
|
|
|
|
|
$metrics[] = [
|
|
|
|
|
'key' => METRIC_BUILDS_COMPUTE_FAILED,
|
|
|
|
|
'value' => ($buildsComputeFailed['value'] * -1),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 08:38:44 +00:00
|
|
|
if (!empty($executions['value'])) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_EXECUTIONS,
|
|
|
|
|
'value' => ($executions['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($executionsCompute['value'])) {
|
|
|
|
|
$metrics[] = [
|
2024-03-06 17:34:21 +00:00
|
|
|
'key' => METRIC_EXECUTIONS_COMPUTE,
|
|
|
|
|
'value' => ($executionsCompute['value'] * -1),
|
2023-10-25 08:38:44 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-02-08 01:17:54 +00:00
|
|
|
} catch (\Throwable $e) {
|
2023-12-24 18:38:15 +00:00
|
|
|
console::error("[reducer] " . " {DateTime::now()} " . " {$project->getInternalId()} " . " {$e->getMessage()}");
|
2023-10-25 08:38:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-01-27 04:33:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
protected function countForProject(Database $dbForPlatform, callable $getLogsDB, callable $getProjectDB, Document $project): void
|
|
|
|
|
{
|
|
|
|
|
Console::info('Begining count for: ' . $project->getId());
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
/** @var \Utopia\Database\Database $dbForLogs */
|
|
|
|
|
$dbForLogs = call_user_func($getLogsDB, $project);
|
|
|
|
|
/** @var \Utopia\Database\Database $dbForProject */
|
|
|
|
|
$dbForProject = call_user_func($getProjectDB, $project);
|
|
|
|
|
|
|
|
|
|
$region = $project->getAttribute('region');
|
|
|
|
|
|
|
|
|
|
$platforms = $dbForPlatform->count('platforms', [
|
|
|
|
|
Query::equal('projectInternalId', [$project->getInternalId()])
|
|
|
|
|
]);
|
|
|
|
|
$webhooks = $dbForPlatform->count('webhooks', [
|
|
|
|
|
Query::equal('projectInternalId', [$project->getInternalId()])
|
|
|
|
|
]);
|
|
|
|
|
$keys = $dbForPlatform->count('keys', [
|
|
|
|
|
Query::equal('projectInternalId', [$project->getInternalId()])
|
|
|
|
|
]);
|
|
|
|
|
$databases = $dbForProject->count('databases');
|
|
|
|
|
$buckets = $dbForProject->count('buckets');
|
|
|
|
|
$users = $dbForProject->count('users');
|
|
|
|
|
|
|
|
|
|
$last30Days = (new \DateTime())->sub(\DateInterval::createFromDateString('30 days'))->format('Y-m-d 00:00:00');
|
|
|
|
|
$usersActive = $dbForProject->count('users', [
|
|
|
|
|
Query::greaterThanEqual('accessedAt', $last30Days)
|
|
|
|
|
]);
|
|
|
|
|
$teams = $dbForProject->count('teams');
|
|
|
|
|
$functions = $dbForProject->count('functions');
|
|
|
|
|
$messages = $dbForProject->count('messages');
|
|
|
|
|
$providers = $dbForProject->count('providers');
|
|
|
|
|
$topics = $dbForProject->count('topics');
|
|
|
|
|
|
|
|
|
|
$metrics = [
|
|
|
|
|
METRIC_DATABASES => $databases,
|
|
|
|
|
METRIC_BUCKETS => $buckets,
|
|
|
|
|
METRIC_USERS => $users,
|
|
|
|
|
METRIC_FUNCTIONS => $functions,
|
|
|
|
|
METRIC_TEAMS => $teams,
|
|
|
|
|
METRIC_MESSAGES => $messages,
|
|
|
|
|
METRIC_USERS_ACTIVE => $usersActive,
|
|
|
|
|
METRIC_WEBHOOKS => $webhooks,
|
|
|
|
|
METRIC_PLATFORMS => $platforms,
|
|
|
|
|
METRIC_PROVIDERS => $providers,
|
|
|
|
|
METRIC_TOPICS => $topics,
|
|
|
|
|
METRIC_KEYS => $keys,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
foreach ($metrics as $metric => $value) {
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, $metric, $value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$this->countForBuckets($dbForProject, $dbForLogs, $region);
|
|
|
|
|
} catch (Throwable $th) {
|
|
|
|
|
call_user_func_array($this->logError, [$th, "usageCount", "count_for_buckets_{$project->getId()}"]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$this->countForDatabase($dbForProject, $dbForLogs, $region);
|
|
|
|
|
} catch (Throwable $th) {
|
|
|
|
|
call_user_func_array($this->logError, [$th, "usageCount", "count_for_database_{$project->getId()}"]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$this->countForFunctions($dbForProject, $dbForLogs, $region);
|
|
|
|
|
} catch (Throwable $th) {
|
|
|
|
|
call_user_func_array($this->logError, [$th, "usageCount", "count_for_functions_{$project->getId()}"]);
|
|
|
|
|
}
|
|
|
|
|
} catch (Throwable $th) {
|
|
|
|
|
call_user_func_array($this->logError, [$th, "usageCount", "count_for_project_{$project->getId()}"]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Console::info('End of count for: ' . $project->getId());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function countForBuckets(Database $dbForProject, Database $dbForLogs, string $region)
|
|
|
|
|
{
|
|
|
|
|
$totalFiles = 0;
|
|
|
|
|
$totalStorage = 0;
|
|
|
|
|
$this->foreachDocument($dbForProject, 'buckets', [], function ($bucket) use ($dbForProject, $dbForLogs, $region, &$totalFiles, &$totalStorage) {
|
|
|
|
|
$files = $dbForProject->count('bucket_' . $bucket->getInternalId());
|
|
|
|
|
|
|
|
|
|
$metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES);
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, $metric, $files);
|
|
|
|
|
|
|
|
|
|
$storage = $dbForProject->sum('bucket_' . $bucket->getInternalId(), 'sizeActual');
|
|
|
|
|
$metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE);
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, $metric, $storage);
|
|
|
|
|
|
|
|
|
|
$totalStorage += $storage;
|
|
|
|
|
$totalFiles += $files;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, METRIC_FILES, $totalFiles);
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, METRIC_FILES_STORAGE, $totalStorage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function countForDatabase(Database $dbForProject, Database $dbForLogs, string $region)
|
|
|
|
|
{
|
|
|
|
|
$totalCollections = 0;
|
|
|
|
|
$totalDocuments = 0;
|
|
|
|
|
|
|
|
|
|
$this->foreachDocument($dbForProject, 'databases', [], function ($database) use ($dbForProject, $dbForLogs, $region, &$totalCollections, &$totalDocuments) {
|
|
|
|
|
$collections = $dbForProject->count('database_' . $database->getInternalId());
|
|
|
|
|
|
|
|
|
|
$metric = str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS);
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, $metric, $collections);
|
|
|
|
|
|
|
|
|
|
$documents = $this->countForCollections($dbForProject, $dbForLogs, $database, $region);
|
|
|
|
|
|
|
|
|
|
$totalDocuments += $documents;
|
|
|
|
|
$totalCollections += $collections;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, METRIC_COLLECTIONS, $totalCollections);
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, METRIC_DOCUMENTS, $totalDocuments);
|
|
|
|
|
}
|
|
|
|
|
protected function countForCollections(Database $dbForProject, Database $dbForLogs, Document $database, string $region): int
|
|
|
|
|
{
|
|
|
|
|
$databaseDocuments = 0;
|
|
|
|
|
$this->foreachDocument($dbForProject, 'database_' . $database->getInternalId(), [], function ($collection) use ($dbForProject, $dbForLogs, $database, $region, &$totalCollections, &$databaseDocuments) {
|
|
|
|
|
$documents = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
|
|
|
|
|
|
|
|
|
|
$metric = str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS);
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, $metric, $documents);
|
|
|
|
|
|
|
|
|
|
$databaseDocuments += $documents;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$metric = str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_DOCUMENTS);
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, $metric, $databaseDocuments);
|
|
|
|
|
|
|
|
|
|
return $databaseDocuments;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function countForFunctions(Database $dbForProject, Database $dbForLogs, string $region)
|
|
|
|
|
{
|
|
|
|
|
$deploymentsStorage = $dbForProject->sum('deployments', 'size');
|
|
|
|
|
$buildsStorage = $dbForProject->sum('builds', 'size');
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, METRIC_DEPLOYMENTS_STORAGE, $deploymentsStorage);
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, METRIC_BUILDS_STORAGE, $buildsStorage);
|
|
|
|
|
|
|
|
|
|
$deployments = $dbForProject->count('deployments');
|
|
|
|
|
$builds = $dbForProject->count('builds');
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, METRIC_DEPLOYMENTS, $deployments);
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, METRIC_BUILDS, $builds);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->foreachDocument($dbForProject, 'functions', [], function (Document $function) use ($dbForProject, $dbForLogs, $region) {
|
|
|
|
|
$functionDeploymentsStorage = $dbForProject->sum('deployments', 'size', [
|
|
|
|
|
Query::equal('resourceInternalId', [$function->getInternalId()]),
|
|
|
|
|
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]),
|
|
|
|
|
]);
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), $functionDeploymentsStorage);
|
|
|
|
|
|
|
|
|
|
$functionDeployments = $dbForProject->count('deployments', [
|
|
|
|
|
Query::equal('resourceInternalId', [$function->getInternalId()]),
|
|
|
|
|
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]),
|
|
|
|
|
]);
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $functionDeployments);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* As deployments and builds have 1-1 relationship,
|
|
|
|
|
* the count for one should match the other
|
|
|
|
|
*/
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS), $functionDeployments);
|
|
|
|
|
|
|
|
|
|
$functionBuildsStorage = 0;
|
|
|
|
|
|
|
|
|
|
$this->foreachDocument($dbForProject, 'deployments', [
|
|
|
|
|
Query::equal('resourceInternalId', [$function->getInternalId()]),
|
|
|
|
|
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]),
|
|
|
|
|
], function (Document $deployment) use ($dbForProject, &$functionBuildsStorage): void {
|
|
|
|
|
$build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''));
|
|
|
|
|
$functionBuildsStorage += $build->getAttribute('size', 0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$this->createOrUpdateMetric($dbForLogs, $region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), $functionBuildsStorage);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function createOrUpdateMetric(Database $dbForLogs, string $region, string $metric, int $value)
|
2025-01-27 06:15:59 +00:00
|
|
|
{
|
2025-01-27 06:11:58 +00:00
|
|
|
foreach ($this->periods as $period => $format) {
|
|
|
|
|
$time = 'inf' === $period ? null : \date($format, \time());
|
|
|
|
|
$id = \md5("{$time}_{$period}_{$metric}");
|
2025-01-27 07:22:50 +00:00
|
|
|
$current = $dbForLogs->getDocument('usage', $id);
|
2025-01-27 06:11:58 +00:00
|
|
|
if ($current->isEmpty()) {
|
2025-01-27 07:22:50 +00:00
|
|
|
$dbForLogs->createDocument('usage', new Document([
|
2025-01-27 06:11:58 +00:00
|
|
|
'$id' => $id,
|
|
|
|
|
'metric' => $metric,
|
|
|
|
|
'period' => $period,
|
|
|
|
|
'region' => $region,
|
|
|
|
|
'value' => $value,
|
|
|
|
|
]));
|
|
|
|
|
Console::success('Usage logs created for metric: ' . $metric . ' period:'. $period);
|
|
|
|
|
} else {
|
2025-01-27 07:22:50 +00:00
|
|
|
$dbForLogs->updateDocument('usage', $id, $current->setAttribute('value', $value));
|
2025-01-27 06:11:58 +00:00
|
|
|
Console::success('Usage logs updated for metric: ' . $metric . ' period:'. $period);
|
|
|
|
|
}
|
2025-01-27 04:33:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-10-25 08:38:44 +00:00
|
|
|
}
|