appwrite/src/Appwrite/Platform/Workers/StatsResources.php

507 lines
22 KiB
PHP
Raw Normal View History

2025-01-30 04:05:51 +00:00
<?php
namespace Appwrite\Platform\Workers;
use Appwrite\Platform\Action;
use Exception;
use Throwable;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Queue\Message;
class StatsResources extends Action
{
/**
* 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'
];
/**
* @var array $documents
*
* Array of documents to batch write
*
*/
protected array $documents = [];
2025-01-30 04:05:51 +00:00
public static function getName(): string
{
return 'stats-resources';
}
2025-01-30 04:05:51 +00:00
/**
* @throws Exception
*/
public function __construct()
{
$this
->desc('Stats resources worker')
->inject('message')
->inject('project')
->inject('getProjectDB')
->inject('getLogsDB')
->inject('dbForPlatform')
->inject('logError')
2025-06-04 08:37:43 +00:00
->callback($this->action(...));
2025-01-30 04:05:51 +00:00
}
/**
* @param Message $message
* @param Document $project
* @param callable $getProjectDB
* @return void
* @throws \Utopia\Database\Exception
* @throws Exception
*/
public function action(Message $message, Document $project, callable $getProjectDB, callable $getLogsDB, Database $dbForPlatform, callable $logError): void
{
$this->logError = $logError;
$payload = $message->getPayload() ?? [];
if (empty($payload)) {
throw new Exception('Missing payload');
}
if (empty($project->getAttribute('database'))) {
return;
}
// Reset documents for each job
$this->documents = [];
$startTime = microtime(true);
2025-01-30 04:05:51 +00:00
$this->countForProject($dbForPlatform, $getLogsDB, $getProjectDB, $project);
$endTime = microtime(true);
$executionTime = $endTime - $startTime;
2025-05-26 05:42:11 +00:00
Console::info('Project: ' . $project->getId() . '(' . $project->getSequence() . ') aggregated in ' . $executionTime .' seconds');
2025-01-30 04:05:51 +00:00
}
protected function countForProject(Database $dbForPlatform, callable $getLogsDB, callable $getProjectDB, Document $project): void
{
Console::info('Begining count for: ' . $project->getId());
2025-02-09 08:52:07 +00:00
$dbForLogs = null;
$dbForProject = null;
2025-01-30 04:05:51 +00:00
try {
/** @var \Utopia\Database\Database $dbForLogs */
$dbForLogs = call_user_func($getLogsDB, $project);
/** @var \Utopia\Database\Database $dbForProject */
$dbForProject = call_user_func($getProjectDB, $project);
2025-02-09 08:52:07 +00:00
} catch (Throwable $th) {
Console::error('Unable to get database');
Console::error($th->getMessage());
return;
}
try {
2025-01-30 04:05:51 +00:00
$region = $project->getAttribute('region');
$platforms = $dbForPlatform->count('platforms', [
2025-05-26 05:42:11 +00:00
Query::equal('projectInternalId', [$project->getSequence()])
2025-01-30 04:05:51 +00:00
]);
$webhooks = $dbForPlatform->count('webhooks', [
2025-05-26 05:42:11 +00:00
Query::equal('projectInternalId', [$project->getSequence()])
2025-01-30 04:05:51 +00:00
]);
$keys = $dbForPlatform->count('keys', [
2025-05-26 05:42:11 +00:00
Query::equal('projectInternalId', [$project->getSequence()])
2025-01-30 04:05:51 +00:00
]);
2025-03-16 02:35:10 +00:00
$domains = $dbForPlatform->count('rules', [
2025-05-26 05:42:11 +00:00
Query::equal('projectInternalId', [$project->getSequence()]),
2025-03-16 02:35:10 +00:00
Query::equal('owner', ['']),
]);
2025-01-30 04:05:51 +00:00
$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');
2025-02-05 09:26:00 +00:00
$usersMAU = $dbForProject->count('users', [
2025-01-30 04:05:51 +00:00
Query::greaterThanEqual('accessedAt', $last30Days)
]);
2025-02-05 09:26:00 +00:00
$last24Hours = (new \DateTime())->sub(\DateInterval::createFromDateString('24 hours'))->format('Y-m-d h:m:00');
$usersDAU = $dbForProject->count('users', [
Query::greaterThanEqual('accessedAt', $last24Hours)
]);
$last7Days = (new \DateTime())->sub(\DateInterval::createFromDateString('7 days'))->format('Y-m-d 00:00:00');
$usersWAU = $dbForProject->count('users', [
Query::greaterThanEqual('accessedAt', $last7Days)
]);
2025-01-30 04:05:51 +00:00
$teams = $dbForProject->count('teams');
$functions = $dbForProject->count('functions');
2025-02-13 04:54:29 +00:00
2025-01-30 04:05:51 +00:00
$messages = $dbForProject->count('messages');
$providers = $dbForProject->count('providers');
$topics = $dbForProject->count('topics');
2025-02-13 04:54:29 +00:00
$targets = $dbForProject->count('targets');
$emailTargets = $dbForProject->count('targets', [
Query::equal('providerType', [MESSAGE_TYPE_EMAIL])
]);
$pushTargets = $dbForProject->count('targets', [
Query::equal('providerType', [MESSAGE_TYPE_PUSH])
]);
$smsTargets = $dbForProject->count('targets', [
Query::equal('providerType', [MESSAGE_TYPE_SMS])
]);
2025-01-30 04:05:51 +00:00
$metrics = [
METRIC_DATABASES => $databases,
METRIC_BUCKETS => $buckets,
METRIC_USERS => $users,
METRIC_FUNCTIONS => $functions,
METRIC_TEAMS => $teams,
METRIC_MESSAGES => $messages,
2025-02-05 09:26:00 +00:00
METRIC_MAU => $usersMAU,
METRIC_DAU => $usersDAU,
METRIC_WAU => $usersWAU,
2025-01-30 04:05:51 +00:00
METRIC_WEBHOOKS => $webhooks,
METRIC_PLATFORMS => $platforms,
METRIC_PROVIDERS => $providers,
METRIC_TOPICS => $topics,
METRIC_KEYS => $keys,
2025-03-16 02:35:10 +00:00
METRIC_DOMAINS => $domains,
2025-02-13 04:54:29 +00:00
METRIC_TARGETS => $targets,
2025-02-13 05:24:00 +00:00
str_replace('{providerType}', MESSAGE_TYPE_EMAIL, METRIC_PROVIDER_TYPE_TARGETS) => $emailTargets,
str_replace('{providerType}', MESSAGE_TYPE_PUSH, METRIC_PROVIDER_TYPE_TARGETS) => $pushTargets,
str_replace('{providerType}', MESSAGE_TYPE_SMS, METRIC_PROVIDER_TYPE_TARGETS) => $smsTargets,
2025-01-30 04:05:51 +00:00
];
foreach ($metrics as $metric => $value) {
$this->createStatsDocuments($region, $metric, $value);
2025-01-30 04:05:51 +00:00
}
try {
$this->countForBuckets($dbForProject, $dbForLogs, $region);
} catch (Throwable $th) {
call_user_func_array($this->logError, [$th, "StatsResources", "count_for_buckets_{$project->getId()}"]);
}
2025-02-11 16:40:17 +00:00
try {
$this->countImageTransformations($dbForProject, $dbForLogs, $region);
} catch (Throwable $th) {
call_user_func_array($this->logError, [$th, "StatsResources", "count_for_buckets_{$project->getId()}"]);
}
2025-01-30 04:05:51 +00:00
try {
$this->countForDatabase($dbForProject, $region);
2025-01-30 04:05:51 +00:00
} catch (Throwable $th) {
call_user_func_array($this->logError, [$th, "StatsResources", "count_for_database_{$project->getId()}"]);
}
try {
2025-04-01 05:07:44 +00:00
$this->countForSitesAndFunctions($dbForProject, $region);
2025-01-30 04:05:51 +00:00
} catch (Throwable $th) {
call_user_func_array($this->logError, [$th, "StatsResources", "count_for_functions_{$project->getId()}"]);
}
2025-02-09 08:52:07 +00:00
$this->writeDocuments($dbForLogs, $project);
2025-01-30 04:05:51 +00:00
} catch (Throwable $th) {
call_user_func_array($this->logError, [$th, "StatsResources", "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) {
2025-05-26 05:42:11 +00:00
$files = $dbForProject->count('bucket_' . $bucket->getSequence());
2025-01-30 04:05:51 +00:00
2025-05-26 05:42:11 +00:00
$metric = str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES);
$this->createStatsDocuments($region, $metric, $files);
2025-01-30 04:05:51 +00:00
2025-05-26 05:42:11 +00:00
$storage = $dbForProject->sum('bucket_' . $bucket->getSequence(), 'sizeActual');
$metric = str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_STORAGE);
$this->createStatsDocuments($region, $metric, $storage);
2025-01-30 04:05:51 +00:00
$totalStorage += $storage;
$totalFiles += $files;
});
$this->createStatsDocuments($region, METRIC_FILES, $totalFiles);
$this->createStatsDocuments($region, METRIC_FILES_STORAGE, $totalStorage);
}
/**
* Need separate function to count per period data
*/
protected function countImageTransformations(Database $dbForProject, Database $dbForLogs, string $region)
{
$totalImageTransformations = 0;
2025-02-16 11:31:46 +00:00
$last30Days = (new \DateTime())->sub(\DateInterval::createFromDateString('30 days'))->format('Y-m-d 00:00:00');
$this->foreachDocument($dbForProject, 'buckets', [], function ($bucket) use ($dbForProject, $last30Days, $region, &$totalImageTransformations) {
2025-05-26 05:42:11 +00:00
$imageTransformations = $dbForProject->count('bucket_' . $bucket->getSequence(), [
2025-02-16 11:31:46 +00:00
Query::greaterThanEqual('transformedAt', $last30Days),
]);
2025-05-26 05:42:11 +00:00
$metric = str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED);
2025-02-16 11:31:46 +00:00
$this->createStatsDocuments($region, $metric, $imageTransformations);
$totalImageTransformations += $imageTransformations;
});
2025-02-16 11:31:46 +00:00
$this->createStatsDocuments($region, METRIC_FILES_IMAGES_TRANSFORMED, $totalImageTransformations);
2025-01-30 04:05:51 +00:00
}
protected function countForDatabase(Database $dbForProject, string $region)
2025-01-30 04:05:51 +00:00
{
$totalCollections = 0;
$totalDocuments = 0;
$totalDatabaseStorage = 0;
$this->foreachDocument($dbForProject, 'databases', [], function ($database) use ($dbForProject, $region, &$totalCollections, &$totalDocuments, &$totalDatabaseStorage) {
2025-05-26 05:42:11 +00:00
$collections = $dbForProject->count('database_' . $database->getSequence());
2025-01-30 04:05:51 +00:00
2025-05-26 05:42:11 +00:00
$metric = str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_COLLECTIONS);
$this->createStatsDocuments($region, $metric, $collections);
2025-01-30 04:05:51 +00:00
[$documents, $storage] = $this->countForCollections($dbForProject, $database, $region);
2025-01-30 04:05:51 +00:00
$totalDatabaseStorage += $storage;
2025-01-30 04:05:51 +00:00
$totalDocuments += $documents;
$totalCollections += $collections;
});
$this->createStatsDocuments($region, METRIC_COLLECTIONS, $totalCollections);
$this->createStatsDocuments($region, METRIC_DOCUMENTS, $totalDocuments);
$this->createStatsDocuments($region, METRIC_DATABASES_STORAGE, $totalDatabaseStorage);
2025-01-30 04:05:51 +00:00
}
protected function countForCollections(Database $dbForProject, Document $database, string $region): array
2025-01-30 04:05:51 +00:00
{
$databaseDocuments = 0;
$databaseStorage = 0;
2025-05-26 05:42:11 +00:00
$this->foreachDocument($dbForProject, 'database_' . $database->getSequence(), [], function ($collection) use ($dbForProject, $database, $region, &$databaseStorage, &$databaseDocuments) {
$documents = $dbForProject->count('database_' . $database->getSequence() . '_collection_' . $collection->getSequence());
$metric = str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getSequence(), $collection->getSequence()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS);
$this->createStatsDocuments($region, $metric, $documents);
2025-01-30 04:05:51 +00:00
$databaseDocuments += $documents;
2025-05-26 05:42:11 +00:00
$collectionStorage = $dbForProject->getSizeOfCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence());
$metric = str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getSequence(), $collection->getSequence()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE);
$this->createStatsDocuments($region, $metric, $collectionStorage);
$databaseStorage += $collectionStorage;
2025-01-30 04:05:51 +00:00
});
2025-05-26 05:42:11 +00:00
$metric = str_replace(['{databaseInternalId}'], [$database->getSequence()], METRIC_DATABASE_ID_DOCUMENTS);
$this->createStatsDocuments($region, $metric, $databaseDocuments);
2025-01-30 04:05:51 +00:00
2025-05-26 05:42:11 +00:00
$metric = str_replace(['{databaseInternalId}'], [$database->getSequence()], METRIC_DATABASE_ID_STORAGE);
$this->createStatsDocuments($region, $metric, $databaseStorage);
return [$databaseDocuments, $databaseStorage];
2025-01-30 04:05:51 +00:00
}
2025-04-01 05:02:54 +00:00
protected function countForSitesAndFunctions(Database $dbForProject, string $region): void
2025-01-30 04:05:51 +00:00
{
2025-04-01 07:31:53 +00:00
$deploymentsStorage = $dbForProject->sum('deployments', 'sourceSize');
$buildsStorage = $dbForProject->sum('deployments', 'buildSize');
$this->createStatsDocuments($region, METRIC_DEPLOYMENTS_STORAGE, $deploymentsStorage);
$this->createStatsDocuments($region, METRIC_BUILDS_STORAGE, $buildsStorage);
2025-01-30 04:05:51 +00:00
$deployments = $dbForProject->count('deployments');
$this->createStatsDocuments($region, METRIC_DEPLOYMENTS, $deployments);
$this->createStatsDocuments($region, METRIC_BUILDS, $deployments);
2025-01-30 04:05:51 +00:00
2025-04-01 05:02:54 +00:00
$this->countForFunctions($dbForProject, $region);
$this->countForSites($dbForProject, $region);
}
protected function countForFunctions(Database $dbForProject, string $region)
{
2025-01-30 04:05:51 +00:00
2025-04-01 07:31:53 +00:00
$deploymentsStorage = $dbForProject->sum('deployments', 'sourceSize', [
2025-04-01 05:02:54 +00:00
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS])
]);
$buildsStorage = $dbForProject->sum('deployments', 'buildSize', [
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS])
]);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_FUNCTIONS, METRIC_RESOURCE_TYPE_DEPLOYMENTS_STORAGE), $deploymentsStorage);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_FUNCTIONS, METRIC_RESOURCE_TYPE_BUILDS_STORAGE), $buildsStorage);
$deployments = $dbForProject->count('deployments', [
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS])
]);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_FUNCTIONS, METRIC_RESOURCE_TYPE_DEPLOYMENTS), $deployments);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_FUNCTIONS, METRIC_RESOURCE_TYPE_BUILDS), $deployments);
2025-11-09 08:18:37 +00:00
// Count runtimes
$runtimes = [];
2025-11-09 08:18:37 +00:00
$this->foreachDocument($dbForProject, 'functions', [], function (Document $function) use ($dbForProject, $region, &$runtimes) {
2025-04-01 08:07:47 +00:00
$functionDeploymentsStorage = $dbForProject->sum('deployments', 'sourceSize', [
2025-05-26 05:42:11 +00:00
Query::equal('resourceInternalId', [$function->getSequence()]),
2025-01-30 04:05:51 +00:00
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]),
]);
2025-05-26 05:42:11 +00:00
$this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), $functionDeploymentsStorage);
2025-01-30 04:05:51 +00:00
$functionDeployments = $dbForProject->count('deployments', [
2025-05-26 05:42:11 +00:00
Query::equal('resourceInternalId', [$function->getSequence()]),
2025-01-30 04:05:51 +00:00
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]),
]);
2025-05-26 05:42:11 +00:00
$this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $functionDeployments);
2025-01-30 04:05:51 +00:00
/**
* As deployments and builds have 1-1 relationship,
* the count for one should match the other
*/
2025-05-26 05:42:11 +00:00
$this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS), $functionDeployments);
2025-01-30 04:05:51 +00:00
$functionBuildsStorage = 0;
$this->foreachDocument($dbForProject, 'deployments', [
2025-05-26 05:42:11 +00:00
Query::equal('resourceInternalId', [$function->getSequence()]),
2025-01-30 04:05:51 +00:00
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]),
], function (Document $deployment) use (&$functionBuildsStorage): void {
$functionBuildsStorage += $deployment->getAttribute('buildSize', 0);
2025-01-30 04:05:51 +00:00
});
2025-05-26 05:42:11 +00:00
$this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), $functionBuildsStorage);
// Runtimes count
$runtime = $function->getAttribute('runtime');
if (!empty($runtime)) {
$runtimes[$runtime] = ($runtimes[$runtime] ?? 0) + 1;
}
2025-01-30 04:05:51 +00:00
});
// Write runtimes counts
foreach ($runtimes as $runtime => $count) {
$this->createStatsDocuments($region, str_replace('{runtime}', $runtime, METRIC_FUNCTIONS_RUNTIME), $count);
}
2025-01-30 04:05:51 +00:00
}
2025-04-01 05:02:54 +00:00
protected function countForSites(Database $dbForProject, string $region)
{
2025-04-01 08:07:47 +00:00
$deploymentsStorage = $dbForProject->sum('deployments', 'sourceSize', [
2025-04-01 05:02:54 +00:00
Query::equal('resourceType', [RESOURCE_TYPE_SITES])
]);
$buildsStorage = $dbForProject->sum('deployments', 'buildSize', [
Query::equal('resourceType', [RESOURCE_TYPE_SITES])
]);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_DEPLOYMENTS_STORAGE), $deploymentsStorage);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_BUILDS_STORAGE), $buildsStorage);
$deployments = $dbForProject->count('deployments', [
Query::equal('resourceType', [RESOURCE_TYPE_SITES])
]);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_DEPLOYMENTS), $deployments);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_BUILDS), $deployments);
// Count frameworks
$frameworks = [];
2025-11-09 08:18:37 +00:00
$this->foreachDocument($dbForProject, 'sites', [], function (Document $site) use ($dbForProject, $region, &$frameworks) {
2025-04-01 08:07:47 +00:00
$siteDeploymentsStorage = $dbForProject->sum('deployments', 'sourceSize', [
Query::equal('resourceInternalId', [$site->getSequence()]),
2025-04-01 05:02:54 +00:00
Query::equal('resourceType', [RESOURCE_TYPE_SITES]),
]);
$this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), $siteDeploymentsStorage);
2025-04-01 05:02:54 +00:00
$siteDeployments = $dbForProject->count('deployments', [
Query::equal('resourceInternalId', [$site->getSequence()]),
2025-04-01 05:02:54 +00:00
Query::equal('resourceType', [RESOURCE_TYPE_SITES]),
]);
$this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $siteDeployments);
2025-04-01 05:02:54 +00:00
/**
* As deployments and builds have 1-1 relationship,
* the count for one should match the other
*/
$this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS), $siteDeployments);
2025-04-01 05:02:54 +00:00
2025-04-01 08:07:47 +00:00
$siteBuildsStorage = $dbForProject->sum('deployments', 'buildSize', [
Query::equal('resourceInternalId', [$site->getSequence()]),
2025-04-01 05:02:54 +00:00
Query::equal('resourceType', [RESOURCE_TYPE_SITES]),
2025-04-01 08:07:47 +00:00
]);
2025-04-01 05:02:54 +00:00
$this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), $siteBuildsStorage);
// Frameworks count
$framework = $site->getAttribute('framework');
if (!empty($framework)) {
$frameworks[$framework] = ($frameworks[$framework] ?? 0) + 1;
}
2025-04-01 05:02:54 +00:00
});
// Write frameworks counts
foreach ($frameworks as $framework => $count) {
$this->createStatsDocuments($region, str_replace('{framework}', $framework, METRIC_SITES_FRAMEWORK), $count);
}
2025-04-01 05:02:54 +00:00
}
2025-02-16 11:31:46 +00:00
protected function createStatsDocuments(string $region, string $metric, int $value)
2025-01-30 04:05:51 +00:00
{
2025-02-16 11:31:46 +00:00
foreach ($this->periods as $period => $format) {
$time = 'inf' === $period ? null : \date($format, \time());
$id = \md5("{$time}_{$period}_{$metric}");
2025-02-16 11:31:46 +00:00
$this->documents[] = new Document([
'$id' => $id,
'metric' => $metric,
'period' => $period,
'region' => $region,
'value' => $value,
2025-02-13 06:43:49 +00:00
'time' => $time,
]);
2025-01-30 04:05:51 +00:00
}
}
protected function writeDocuments(Database $dbForLogs, Document $project): void
{
2025-09-10 09:24:24 +00:00
$message = 'Stats writeDocuments project: ' . $project->getId() . '(' . $project->getSequence() . ')';
2025-09-10 15:04:35 +00:00
/**
* sort by unique index key reduce locks/deadlocks
*/
usort($this->documents, function ($a, $b) {
// Metric DESC
2025-09-10 14:23:33 +00:00
$cmp = strcmp($b['metric'], $a['metric']);
2025-09-11 06:41:17 +00:00
if ($cmp !== 0) {
return $cmp;
}
2025-09-10 14:23:33 +00:00
2025-09-10 15:04:35 +00:00
// Period ASC
2025-09-10 14:23:33 +00:00
$cmp = strcmp($a['period'], $b['period']);
2025-09-11 06:41:17 +00:00
if ($cmp !== 0) {
return $cmp;
}
2025-09-10 14:23:33 +00:00
2025-09-10 15:04:35 +00:00
// Time ASC, NULLs first
2025-09-11 06:41:17 +00:00
if ($a['time'] === null) {
return ($b['time'] === null) ? 0 : -1;
}
if ($b['time'] === null) {
return 1;
}
2025-09-10 14:48:58 +00:00
2025-09-10 14:43:37 +00:00
return strcmp($a['time'], $b['time']);
2025-09-10 14:23:33 +00:00
});
2025-09-10 09:24:24 +00:00
try {
2025-10-04 09:32:01 +00:00
$dbForLogs->upsertDocuments(
2025-09-10 09:24:24 +00:00
'stats',
2025-09-10 14:23:33 +00:00
$this->documents,
2025-09-10 09:24:24 +00:00
);
Console::success($message . ' | Documents: ' . count($this->documents));
} catch (\Throwable $e) {
Console::error('Error: ' . $message . ' | Exception: ' . $e->getMessage());
throw $e;
}
}
2025-01-30 04:05:51 +00:00
}