From 05067302d2d61f49d92095e48e8ed31178e1805a Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 27 Mar 2025 08:25:48 +0000 Subject: [PATCH 01/10] Fix: merge the working of StatsUsage and StatsUsageDump --- src/Appwrite/Platform/Workers/StatsUsage.php | 214 ++++++++++++++++++- 1 file changed, 207 insertions(+), 7 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index a755f723a0..6439fa5fbc 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -2,17 +2,21 @@ namespace Appwrite\Platform\Workers; -use Appwrite\Event\StatsUsageDump; use Exception; +use Throwable; use Utopia\CLI\Console; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Platform\Action; use Utopia\Queue\Message; +use Utopia\Registry\Registry; use Utopia\System\System; class StatsUsage extends Action { + /** + * In memory per project metrics calculation + */ private array $stats = []; private int $lastTriggeredTime = 0; private int $keys = 0; @@ -20,6 +24,77 @@ class StatsUsage extends Action private const BATCH_SIZE_DEVELOPMENT = 1; private const BATCH_SIZE_PRODUCTION = 10_000; + /** + * Stats for batch write separated per project + * @var array + */ + private array $projects = []; + + /** + * Array of stat documents to batch write to logsDB + * @var array + */ + private array $statDocuments = []; + + protected Registry $register; + + /** + * Metrics to skip writing to logsDB + * As these metrics are calculated separately + * by logs DB + * @var array + */ + protected array $skipBaseMetrics = [ + METRIC_DATABASES => true, + METRIC_BUCKETS => true, + METRIC_USERS => true, + METRIC_FUNCTIONS => true, + METRIC_TEAMS => true, + METRIC_MESSAGES => true, + METRIC_MAU => true, + METRIC_WEBHOOKS => true, + METRIC_PLATFORMS => true, + METRIC_PROVIDERS => true, + METRIC_TOPICS => true, + METRIC_KEYS => true, + METRIC_FILES => true, + METRIC_FILES_STORAGE => true, + METRIC_DEPLOYMENTS_STORAGE => true, + METRIC_BUILDS_STORAGE => true, + METRIC_DEPLOYMENTS => true, + METRIC_BUILDS => true, + METRIC_COLLECTIONS => true, + METRIC_DOCUMENTS => true, + METRIC_DATABASES_STORAGE => true, + ]; + + /** + * Skip metrics associated with parent IDs + * these need to be checked individually with `str_ends_with` + */ + protected array $skipParentIdMetrics = [ + '.files', + '.files.storage', + '.collections', + '.documents', + '.deployments', + '.deployments.storage', + '.builds', + '.builds.storage', + '.databases.storage' + ]; + + /** + * @var callable + */ + protected mixed $getLogsDB; + + protected array $periods = [ + '1h' => 'Y-m-d H:00', + '1d' => 'Y-m-d 00:00', + 'inf' => '0000-00-00 00:00' + ]; + public static function getName(): string { return 'stats-usage'; @@ -41,7 +116,8 @@ class StatsUsage extends Action ->desc('Stats usage worker') ->inject('message') ->inject('getProjectDB') - ->inject('queueForStatsUsageDump') + ->inject('getLogsDB') + ->inject('register') ->callback([$this, 'action']); $this->lastTriggeredTime = time(); @@ -50,13 +126,16 @@ class StatsUsage extends Action /** * @param Message $message * @param callable $getProjectDB - * @param StatsUsageDump $queueForStatsUsageDump + * @param callable $getLogsDB + * @param Registry $register * @return void * @throws \Utopia\Database\Exception * @throws Exception */ - public function action(Message $message, callable $getProjectDB, StatsUsageDump $queueForStatsUsageDump): void + public function action(Message $message, callable $getProjectDB, callable $getLogsDB, Registry $register): void { + $this->getLogsDB = $getLogsDB; + $this->register = $register; $payload = $message->getPayload() ?? []; if (empty($payload)) { throw new Exception('Missing payload'); @@ -98,9 +177,7 @@ class StatsUsage extends Action ) { Console::warning('[' . DateTime::now() . '] Aggregated ' . $this->keys . ' keys'); - $queueForStatsUsageDump - ->setStats($this->stats) - ->trigger(); + $this->commitToDB($getProjectDB); $this->stats = []; $this->keys = 0; @@ -250,4 +327,127 @@ class StatsUsage extends Action console::error("[reducer] " . " {DateTime::now()} " . " {$project->getInternalId()} " . " {$e->getMessage()}"); } } + + public function commitToDb(callable $getProjectDB): void + { + + foreach ($this->stats as $stats) { + $project = new Document($stats['project'] ?? []); + $numberOfKeys = !empty($stats['keys']) ? count($stats['keys']) : 0; + $receivedAt = $stats['receivedAt'] ?? null; + if ($numberOfKeys === 0) { + continue; + } + + console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); + + try { + foreach ($stats['keys'] ?? [] as $key => $value) { + if ($value == 0) { + continue; + } + + if (str_contains($key, METRIC_DATABASES_STORAGE)) { + // skip database storage calc as it's wrong and we plan to get this from StatsResources + continue; + } + + foreach ($this->periods as $period => $format) { + $time = null; + + if ($period !== 'inf') { + $time = !empty($receivedAt) ? (new \DateTime($receivedAt))->format($format) : date($format, time()); + } + $id = \md5("{$time}_{$period}_{$key}"); + + $document = new Document([ + '$id' => $id, + 'period' => $period, + 'time' => $time, + 'metric' => $key, + 'value' => $value, + 'region' => System::getEnv('_APP_REGION', 'default'), + ]); + + + $this->projects[$project->getInternalId()]['project'] = new Document([ + '$id' => $project->getId(), + '$internalId' => $project->getInternalId(), + 'database' => $project->getAttribute('database'), + ]); + $this->projects[$project->getInternalId()]['stats'][] = $document; + + $this->prepareForLogsDB($project, $document); + } + } + } catch (Exception $e) { + console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); + } + } + + foreach ($this->projects as $internalId => $projectStats) { + if (empty($internalId)) { + continue; + } + try { + /** @var \Utopia\Database\Database $dbForProject */ + $dbForProject = $getProjectDB($projectStats['project']); + Console::log('Processing batch with ' . count($projectStats['stats']) . ' stats'); + $dbForProject->createOrUpdateDocumentsWithIncrease('stats', 'value', $projectStats['stats']); + Console::success('Batch successfully written to DB'); + + unset($this->projects[$internalId]); + } catch (Throwable $e) { + Console::error('Error processing stats: ' . $e->getMessage()); + } + } + + $this->writeToLogsDB(); + + } + + protected function prepareForLogsDB(Document $project, Document $stat) + { + if (System::getEnv('_APP_STATS_USAGE_DUAL_WRITING', 'disabled') === 'disabled') { + return; + } + if (array_key_exists($stat->getAttribute('metric'), $this->skipBaseMetrics)) { + return; + } + foreach ($this->skipParentIdMetrics as $skipMetric) { + if (str_ends_with($stat->getAttribute('metric'), $skipMetric)) { + return; + } + } + $documentClone = new Document($stat->getArrayCopy()); + $documentClone->setAttribute('$tenant', (int) $project->getInternalId()); + $this->statDocuments[] = $documentClone; + } + + protected function writeToLogsDB(): void + { + if (System::getEnv('_APP_STATS_USAGE_DUAL_WRITING', 'disabled') === 'disabled') { + Console::log('Dual Writing is disabled. Skipping...'); + return; + } + + /** @var \Utopia\Database\Database $dbForLogs*/ + $dbForLogs = call_user_func($this->getLogsDB); + $dbForLogs + ->setTenant(null) + ->setTenantPerDocument(true); + + try { + Console::log('Processing batch with ' . count($this->statDocuments) . ' stats'); + $dbForLogs->createOrUpdateDocumentsWithIncrease( + 'stats', + 'value', + $this->statDocuments + ); + Console::success('Usage logs pushed to Logs DB'); + } catch (Throwable $th) { + Console::error($th->getMessage()); + } + $this->register->get('pools')->get('logs')->reclaim(); + } } From cfb1f4fcdb6930fda09d15aa7429de34829022af Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 27 Mar 2025 08:29:12 +0000 Subject: [PATCH 02/10] fix: remove stats usage dump --- app/controllers/api/health.php | 32 -- app/worker.php | 5 - src/Appwrite/Event/Event.php | 11 - src/Appwrite/Event/StatsUsageDump.php | 44 --- src/Appwrite/Platform/Services/Workers.php | 2 - .../Platform/Workers/StatsUsageDump.php | 278 ------------------ .../Health/HealthCustomServerTest.php | 24 -- 7 files changed, 396 deletions(-) delete mode 100644 src/Appwrite/Event/StatsUsageDump.php delete mode 100644 src/Appwrite/Platform/Workers/StatsUsageDump.php diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index e5336067c8..204ba0ad0a 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -756,38 +756,6 @@ App::get('/v1/health/queue/stats-usage') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }); -App::get('/v1/health/queue/stats-usage-dump') - ->desc('Get usage dump queue') - ->groups(['api', 'health']) - ->label('scope', 'health.read') - ->label('sdk', new Method( - auth: [AuthType::KEY], - namespace: 'health', - name: 'getQueueStatsUsageDump', - description: '/docs/references/health/get-queue-stats-usage-dump.md', - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_HEALTH_QUEUE, - ) - ], - contentType: ContentType::JSON - )) - ->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true) - ->inject('publisher') - ->inject('response') - ->action(function (int|string $threshold, Publisher $publisher, Response $response) { - $threshold = \intval($threshold); - - $size = $publisher->getQueueSize(new Queue(Event::STATS_USAGE_DUMP_QUEUE_NAME)); - - if ($size >= $threshold) { - throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); - } - - $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }); - App::get('/v1/health/storage/local') ->desc('Get local storage') ->groups(['api', 'health']) diff --git a/app/worker.php b/app/worker.php index 90496c0430..e061a6a6d6 100644 --- a/app/worker.php +++ b/app/worker.php @@ -15,7 +15,6 @@ use Appwrite\Event\Messaging; use Appwrite\Event\Migration; use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; -use Appwrite\Event\StatsUsageDump; use Appwrite\Event\Webhook; use Appwrite\Platform\Appwrite; use Swoole\Runtime; @@ -278,10 +277,6 @@ Server::setResource('queueForStatsUsage', function (Publisher $publisher) { return new StatsUsage($publisher); }, ['publisher']); -Server::setResource('queueForStatsUsageDump', function (Publisher $publisher) { - return new StatsUsageDump($publisher); -}, ['publisher']); - Server::setResource('queueForDatabase', function (Publisher $publisher) { return new EventDatabase($publisher); }, ['publisher']); diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 0edffdf4dc..14b1fd356a 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -24,23 +24,12 @@ class Event public const FUNCTIONS_QUEUE_NAME = 'v1-functions'; public const FUNCTIONS_CLASS_NAME = 'FunctionsV1'; - /** remove */ - public const USAGE_QUEUE_NAME = 'v1-usage'; - public const USAGE_CLASS_NAME = 'UsageV1'; - - public const USAGE_DUMP_QUEUE_NAME = 'v1-usage-dump'; - public const USAGE_DUMP_CLASS_NAME = 'UsageDumpV1'; - /** /remove */ - public const STATS_RESOURCES_QUEUE_NAME = 'v1-stats-resources'; public const STATS_RESOURCES_CLASS_NAME = 'StatsResourcesV1'; public const STATS_USAGE_QUEUE_NAME = 'v1-stats-usage'; public const STATS_USAGE_CLASS_NAME = 'StatsUsageV1'; - public const STATS_USAGE_DUMP_QUEUE_NAME = 'v1-stats-usage-dump'; - public const STATS_USAGE_DUMP_CLASS_NAME = 'StatsUsageDumpV1'; - public const WEBHOOK_QUEUE_NAME = 'v1-webhooks'; public const WEBHOOK_CLASS_NAME = 'WebhooksV1'; diff --git a/src/Appwrite/Event/StatsUsageDump.php b/src/Appwrite/Event/StatsUsageDump.php deleted file mode 100644 index 0573a88040..0000000000 --- a/src/Appwrite/Event/StatsUsageDump.php +++ /dev/null @@ -1,44 +0,0 @@ -setQueue(Event::STATS_USAGE_DUMP_QUEUE_NAME) - ->setClass(Event::STATS_USAGE_DUMP_CLASS_NAME); - } - - /** - * Add Stats. - * - * @param array $stats - * @return self - */ - public function setStats(array $stats): self - { - $this->stats = $stats; - - return $this; - } - - /** - * Prepare the payload for the usage dump event. - * - * @return array - */ - protected function preparePayload(): array - { - return [ - 'stats' => $this->stats, - ]; - } -} diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index 4f4095aca4..eb544c140e 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -13,7 +13,6 @@ use Appwrite\Platform\Workers\Messaging; use Appwrite\Platform\Workers\Migrations; use Appwrite\Platform\Workers\StatsResources; use Appwrite\Platform\Workers\StatsUsage; -use Appwrite\Platform\Workers\StatsUsageDump; use Appwrite\Platform\Workers\Webhooks; use Utopia\Platform\Service; @@ -32,7 +31,6 @@ class Workers extends Service ->addAction(Mails::getName(), new Mails()) ->addAction(Messaging::getName(), new Messaging()) ->addAction(Webhooks::getName(), new Webhooks()) - ->addAction(StatsUsageDump::getName(), new StatsUsageDump()) ->addAction(StatsUsage::getName(), new StatsUsage()) ->addAction(Migrations::getName(), new Migrations()) ->addAction(StatsResources::getName(), new StatsResources()) diff --git a/src/Appwrite/Platform/Workers/StatsUsageDump.php b/src/Appwrite/Platform/Workers/StatsUsageDump.php deleted file mode 100644 index 2832e97000..0000000000 --- a/src/Appwrite/Platform/Workers/StatsUsageDump.php +++ /dev/null @@ -1,278 +0,0 @@ - true, - METRIC_BUCKETS => true, - METRIC_USERS => true, - METRIC_FUNCTIONS => true, - METRIC_TEAMS => true, - METRIC_MESSAGES => true, - METRIC_MAU => true, - METRIC_WEBHOOKS => true, - METRIC_PLATFORMS => true, - METRIC_PROVIDERS => true, - METRIC_TOPICS => true, - METRIC_KEYS => true, - METRIC_FILES => true, - METRIC_FILES_STORAGE => true, - METRIC_DEPLOYMENTS_STORAGE => true, - METRIC_BUILDS_STORAGE => true, - METRIC_DEPLOYMENTS => true, - METRIC_BUILDS => true, - METRIC_COLLECTIONS => true, - METRIC_DOCUMENTS => true, - METRIC_DATABASES_STORAGE => true, - ]; - - /** - * Skip metrics associated with parent IDs - * these need to be checked individually with `str_ends_with` - */ - protected array $skipParentIdMetrics = [ - '.files', - '.files.storage', - '.collections', - '.documents', - '.deployments', - '.deployments.storage', - '.builds', - '.builds.storage', - '.databases.storage' - ]; - - /** - * @var callable - */ - protected mixed $getLogsDB; - - protected array $periods = [ - '1h' => 'Y-m-d H:00', - '1d' => 'Y-m-d 00:00', - 'inf' => '0000-00-00 00:00' - ]; - - public static function getName(): string - { - return 'stats-usage-dump'; - } - - /** - * @throws \Exception - */ - public function __construct() - { - $this - ->inject('message') - ->inject('getProjectDB') - ->inject('getLogsDB') - ->inject('register') - ->callback([$this, 'action']); - } - - /** - * @param Message $message - * @param callable $getProjectDB - * @param callable $getLogsDB - * @param Registry $register - * @return void - * @throws Exception - * @throws \Throwable - * @throws \Utopia\Database\Exception - */ - public function action(Message $message, callable $getProjectDB, callable $getLogsDB, Registry $register): void - { - $this->getLogsDB = $getLogsDB; - $this->register = $register; - $payload = $message->getPayload() ?? []; - if (empty($payload)) { - throw new Exception('Missing payload'); - } - - foreach ($payload['stats'] ?? [] as $stats) { - $project = new Document($stats['project'] ?? []); - $numberOfKeys = !empty($stats['keys']) ? count($stats['keys']) : 0; - $receivedAt = $stats['receivedAt'] ?? null; - if ($numberOfKeys === 0) { - continue; - } - - console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); - - try { - foreach ($stats['keys'] ?? [] as $key => $value) { - if ($value == 0) { - continue; - } - - if (str_contains($key, METRIC_DATABASES_STORAGE)) { - // skip database storage calc as it's wrong and we plan to get this from StatsResources - continue; - } - - foreach ($this->periods as $period => $format) { - $time = null; - - if ($period !== 'inf') { - $time = !empty($receivedAt) ? (new \DateTime($receivedAt))->format($format) : date($format, time()); - } - $id = \md5("{$time}_{$period}_{$key}"); - - $document = new Document([ - '$id' => $id, - 'period' => $period, - 'time' => $time, - 'metric' => $key, - 'value' => $value, - 'region' => System::getEnv('_APP_REGION', 'default'), - ]); - - - $this->projects[$project->getInternalId()]['project'] = new Document([ - '$id' => $project->getId(), - '$internalId' => $project->getInternalId(), - 'database' => $project->getAttribute('database'), - ]); - $this->projects[$project->getInternalId()]['stats'][] = $document; - - $this->prepareForLogsDB($project, $document); - } - } - } catch (\Exception $e) { - console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); - } - } - - $batchSize = $this->getBatchSize(); - $shouldProcessBatch = \count($this->projects) >= $batchSize; - if (!$shouldProcessBatch && \count($this->projects) > 0) { - $shouldProcessBatch = (\time() - $this->lastDispatchTime) >= self::BATCH_AGGREGATION_INTERVAL; - } - - if ($shouldProcessBatch || App::isDevelopment()) { - foreach ($this->projects as $internalId => $projectStats) { - if (empty($internalId)) { - continue; - } - try { - /** @var \Utopia\Database\Database $dbForProject */ - $dbForProject = $getProjectDB($projectStats['project']); - Console::log('Processing batch with ' . count($projectStats['stats']) . ' stats'); - $dbForProject->createOrUpdateDocumentsWithIncrease('stats', 'value', $projectStats['stats']); - Console::success('Batch successfully written to DB'); - - unset($this->projects[$internalId]); - } catch (Throwable $e) { - Console::error('Error processing stats: ' . $e->getMessage()); - } - } - $this->lastDispatchTime = time(); - } - $this->writeToLogsDB(); - - } - - protected function prepareForLogsDB(Document $project, Document $stat) - { - if (System::getEnv('_APP_STATS_USAGE_DUAL_WRITING', 'disabled') === 'disabled') { - return; - } - if (array_key_exists($stat->getAttribute('metric'), $this->skipBaseMetrics)) { - return; - } - foreach ($this->skipParentIdMetrics as $skipMetric) { - if (str_ends_with($stat->getAttribute('metric'), $skipMetric)) { - return; - } - } - $documentClone = new Document($stat->getArrayCopy()); - $documentClone->setAttribute('$tenant', (int) $project->getInternalId()); - $this->statDocuments[] = $documentClone; - } - - protected function writeToLogsDB(): void - { - if (System::getEnv('_APP_STATS_USAGE_DUAL_WRITING', 'disabled') === 'disabled') { - Console::log('Dual Writing is disabled. Skipping...'); - return; - } - - $batchSize = $this->getBatchSize(); - $shouldProcessBatch = \count($this->statDocuments) >= $batchSize; - if (!$shouldProcessBatch && \count($this->statDocuments) > 0) { - $shouldProcessBatch = (\time() - $this->lastDispatchTimeLogsDB) >= self::BATCH_AGGREGATION_INTERVAL; - } - - if (!$shouldProcessBatch) { - return; - } - - /** @var \Utopia\Database\Database $dbForLogs*/ - $dbForLogs = call_user_func($this->getLogsDB); - $dbForLogs - ->setTenant(null) - ->setTenantPerDocument(true); - - try { - Console::log('Processing batch with ' . count($this->statDocuments) . ' stats'); - $dbForLogs->createOrUpdateDocumentsWithIncrease( - 'stats', - 'value', - $this->statDocuments - ); - Console::success('Usage logs pushed to Logs DB'); - } catch (Throwable $th) { - Console::error($th->getMessage()); - } finally { - $this->lastDispatchTimeLogsDB = time(); - } - - $this->register->get('pools')->get('logs')->reclaim(); - } -} diff --git a/tests/e2e/Services/Health/HealthCustomServerTest.php b/tests/e2e/Services/Health/HealthCustomServerTest.php index 04b1408cd0..4b7062dc22 100644 --- a/tests/e2e/Services/Health/HealthCustomServerTest.php +++ b/tests/e2e/Services/Health/HealthCustomServerTest.php @@ -541,28 +541,4 @@ class HealthCustomServerTest extends Scope ], $this->getHeaders()), []); $this->assertEquals(503, $response['headers']['status-code']); } - - public function testStatsUsageDumpSuccess() - { - /** - * Test for SUCCESS - */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/stats-usage-dump', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertIsInt($response['body']['size']); - $this->assertLessThan(100, $response['body']['size']); - - /** - * Test for FAILURE - */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/stats-usage-dump?threshold=0', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - $this->assertEquals(503, $response['headers']['status-code']); - } } From f4d01f4c37ef2ffdea8a8a8705c8e3ab2ce4d404 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 27 Mar 2025 08:36:52 +0000 Subject: [PATCH 03/10] fix: remove stats usage dump worker from docker --- Dockerfile | 1 - app/views/install/compose.phtml | 28 ---------------------------- bin/worker-stats-usage-dump | 3 --- docker-compose.yml | 32 -------------------------------- 4 files changed, 64 deletions(-) delete mode 100644 bin/worker-stats-usage-dump diff --git a/Dockerfile b/Dockerfile index 88d5ed030b..2f1ea6f279 100755 --- a/Dockerfile +++ b/Dockerfile @@ -86,7 +86,6 @@ RUN chmod +x /usr/local/bin/doctor && \ chmod +x /usr/local/bin/worker-migrations && \ chmod +x /usr/local/bin/worker-webhooks && \ chmod +x /usr/local/bin/worker-stats-usage && \ - chmod +x /usr/local/bin/worker-stats-usage-dump && \ chmod +x /usr/local/bin/stats-resources && \ chmod +x /usr/local/bin/worker-stats-resources diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 7dfe14fcef..96236ecd48 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -744,34 +744,6 @@ $image = $this->getParam('image', ''); - _APP_LOGGING_CONFIG - _APP_USAGE_AGGREGATION_INTERVAL - appwrite-worker-stats-usage-dump: - image: /: - entrypoint: worker-stats-usage-dump - <<: *x-logging - container_name: appwrite-worker-stats-usage-dump - restart: unless-stopped - networks: - - appwrite - depends_on: - - redis - - mariadb - environment: - - _APP_ENV - - _APP_WORKER_PER_CORE - - _APP_OPENSSL_KEY_V1 - - _APP_DB_HOST - - _APP_DB_PORT - - _APP_DB_SCHEMA - - _APP_DB_USER - - _APP_DB_PASS - - _APP_REDIS_HOST - - _APP_REDIS_PORT - - _APP_REDIS_USER - - _APP_REDIS_PASS - - _APP_USAGE_STATS - - _APP_LOGGING_CONFIG - - _APP_USAGE_AGGREGATION_INTERVAL - appwrite-task-scheduler-functions: image: /: entrypoint: schedule-functions diff --git a/bin/worker-stats-usage-dump b/bin/worker-stats-usage-dump deleted file mode 100644 index 98e3c2cac7..0000000000 --- a/bin/worker-stats-usage-dump +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -php /usr/src/code/app/worker.php stats-usage-dump $@ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 05ddba967a..7d89ec0783 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -823,38 +823,6 @@ services: - _APP_USAGE_AGGREGATION_INTERVAL - _APP_DATABASE_SHARED_TABLES - appwrite-worker-stats-usage-dump: - entrypoint: worker-stats-usage-dump - <<: *x-logging - container_name: appwrite-worker-stats-usage-dump - image: appwrite-dev - networks: - - appwrite - volumes: - - ./app:/usr/src/code/app - - ./src:/usr/src/code/src - depends_on: - - redis - - mariadb - environment: - - _APP_ENV - - _APP_WORKER_PER_CORE - - _APP_OPENSSL_KEY_V1 - - _APP_DB_HOST - - _APP_DB_PORT - - _APP_DB_SCHEMA - - _APP_DB_USER - - _APP_DB_PASS - - _APP_REDIS_HOST - - _APP_REDIS_PORT - - _APP_REDIS_USER - - _APP_REDIS_PASS - - _APP_USAGE_STATS - - _APP_LOGGING_CONFIG - - _APP_USAGE_AGGREGATION_INTERVAL - - _APP_DATABASE_SHARED_TABLES - - _APP_STATS_USAGE_DUAL_WRITING_DBS - appwrite-task-scheduler-functions: entrypoint: schedule-functions <<: *x-logging From 1389bab3d1a3d9d769c67b65f2bf2cf7b5f96551 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 30 Mar 2025 05:56:14 +0000 Subject: [PATCH 04/10] remove db storage check --- src/Appwrite/Platform/Workers/StatsUsage.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index 6439fa5fbc..a0c16449b6 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -347,11 +347,6 @@ class StatsUsage extends Action continue; } - if (str_contains($key, METRIC_DATABASES_STORAGE)) { - // skip database storage calc as it's wrong and we plan to get this from StatsResources - continue; - } - foreach ($this->periods as $period => $format) { $time = null; From d5ad714f5a211fc617c9751cd6975afeb883c06c Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 30 Mar 2025 06:09:03 +0000 Subject: [PATCH 05/10] fix missing queue name --- app/controllers/api/health.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 204ba0ad0a..81a77733ff 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -922,7 +922,6 @@ App::get('/v1/health/queue/failed/:name') Event::FUNCTIONS_QUEUE_NAME, Event::STATS_RESOURCES_QUEUE_NAME, Event::STATS_USAGE_QUEUE_NAME, - Event::STATS_USAGE_DUMP_QUEUE_NAME, Event::WEBHOOK_QUEUE_NAME, Event::CERTIFICATES_QUEUE_NAME, Event::BUILDS_QUEUE_NAME, From ec21effd2c466c4b06a96ec2a678adfaa97353a9 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 31 Mar 2025 03:55:06 +0000 Subject: [PATCH 06/10] Fix type error --- src/Appwrite/Platform/Workers/StatsUsage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index a0c16449b6..ffd2a32648 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -332,7 +332,7 @@ class StatsUsage extends Action { foreach ($this->stats as $stats) { - $project = new Document($stats['project'] ?? []); + $project = $stats['project'] ?? new Document([]); $numberOfKeys = !empty($stats['keys']) ? count($stats['keys']) : 0; $receivedAt = $stats['receivedAt'] ?? null; if ($numberOfKeys === 0) { From e14ae7ea4acc6f915d790ddda587c69b634ae5fc Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 31 Mar 2025 03:59:37 +0000 Subject: [PATCH 07/10] fix --- src/Appwrite/Platform/Workers/StatsUsage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index ffd2a32648..71e1e1a9dd 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -323,7 +323,7 @@ class StatsUsage extends Action default: break; } - } catch (\Throwable $e) { + } catch (Throwable $e) { console::error("[reducer] " . " {DateTime::now()} " . " {$project->getInternalId()} " . " {$e->getMessage()}"); } } From fd5a90075896a046e244d910fa844d0f3f34dd3a Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 13 Apr 2025 03:46:59 +0000 Subject: [PATCH 08/10] update database --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 8db4706bb5..6fcbb16235 100644 --- a/composer.lock +++ b/composer.lock @@ -3497,16 +3497,16 @@ }, { "name": "utopia-php/database", - "version": "0.64.1", + "version": "0.64.2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "6530a8a6d3c1fe92d0f9a92f0f05eda698d92e0b" + "reference": "dc9c4a68c93e8bea2dfaa76d1ba308be539998bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/6530a8a6d3c1fe92d0f9a92f0f05eda698d92e0b", - "reference": "6530a8a6d3c1fe92d0f9a92f0f05eda698d92e0b", + "url": "https://api.github.com/repos/utopia-php/database/zipball/dc9c4a68c93e8bea2dfaa76d1ba308be539998bd", + "reference": "dc9c4a68c93e8bea2dfaa76d1ba308be539998bd", "shasum": "" }, "require": { @@ -3547,9 +3547,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.64.1" + "source": "https://github.com/utopia-php/database/tree/0.64.2" }, - "time": "2025-04-02T00:35:29+00:00" + "time": "2025-04-09T07:53:05+00:00" }, { "name": "utopia-php/domains", From a46c5d10299462a03d70fe917ce59a31260df43a Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 15 Apr 2025 08:54:04 +0545 Subject: [PATCH 09/10] Update src/Appwrite/Platform/Workers/StatsUsage.php Co-authored-by: Jake Barnby --- src/Appwrite/Platform/Workers/StatsUsage.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index 71e1e1a9dd..80aea081ad 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -330,7 +330,6 @@ class StatsUsage extends Action public function commitToDb(callable $getProjectDB): void { - foreach ($this->stats as $stats) { $project = $stats['project'] ?? new Document([]); $numberOfKeys = !empty($stats['keys']) ? count($stats['keys']) : 0; From 93f86e57ea53fb124186706ee263a44fa36877c4 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 15 Apr 2025 03:15:24 +0000 Subject: [PATCH 10/10] use clone --- src/Appwrite/Platform/Workers/StatsUsage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index 80aea081ad..3e99ea972d 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -413,7 +413,7 @@ class StatsUsage extends Action return; } } - $documentClone = new Document($stat->getArrayCopy()); + $documentClone = clone $stat; $documentClone->setAttribute('$tenant', (int) $project->getInternalId()); $this->statDocuments[] = $documentClone; }