From 33566f2052210dd551d7be69670011208ed3db27 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 25 Mar 2026 14:27:06 +1300 Subject: [PATCH] (perf): Parallelize usage endpoint queries with Promise::map --- app/controllers/api/project.php | 20 +++++++++++----- app/controllers/api/users.php | 23 ++++++++++++------- .../Http/Databases/Collections/Usage/Get.php | 21 +++++++++++------ .../Databases/Http/Databases/Usage/Get.php | 21 +++++++++++------ .../Databases/Http/Databases/Usage/XList.php | 21 +++++++++++------ .../Modules/Functions/Http/Usage/Get.php | 21 +++++++++++------ .../Modules/Functions/Http/Usage/XList.php | 21 +++++++++++------ .../Platform/Modules/Sites/Http/Usage/Get.php | 21 +++++++++++------ .../Modules/Sites/Http/Usage/XList.php | 21 +++++++++++------ .../Modules/Storage/Http/Usage/Get.php | 21 +++++++++++------ .../Modules/Storage/Http/Usage/XList.php | 21 +++++++++++------ 11 files changed, 155 insertions(+), 77 deletions(-) diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index d24519e3fb..7620cd620e 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -6,6 +6,7 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; +use Utopia\Async\Promise; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; @@ -104,20 +105,19 @@ Http::get('/v1/project/usage') }; $authorization->skip(function () use ($dbForProject, $dbForLogs, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) { + $tasks = []; + foreach ($metrics['total'] as $metric) { $db = ($metric === METRIC_FILES_IMAGES_TRANSFORMED) ? $dbForLogs : $dbForProject; - - $result = $db->findOne('stats', [ + $tasks['total_' . $metric] = fn () => $db->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); - $total[$metric] = $result['value'] ?? 0; } foreach ($metrics['period'] as $metric) { $db = ($metric === METRIC_FILES_IMAGES_TRANSFORMED) ? $dbForLogs : $dbForProject; - - $results = $db->find('stats', [ + $tasks['period_' . $metric] = fn () => $db->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::greaterThanEqual('time', $firstDay), @@ -125,9 +125,17 @@ Http::get('/v1/project/usage') Query::limit($limit), Query::orderDesc('time'), ]); + } + $results = Promise::map($tasks)->await(); + + foreach ($metrics['total'] as $metric) { + $total[$metric] = $results['total_' . $metric]['value'] ?? 0; + } + + foreach ($metrics['period'] as $metric) { $stats[$metric] = []; - foreach ($results as $result) { + foreach ($results['period_' . $metric] as $result) { $stats[$metric][$result->getAttribute('time')] = [ 'value' => $result->getAttribute('value'), ]; diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 9d04018b10..9a390c7756 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -29,6 +29,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Users; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; +use Utopia\Async\Promise; use Utopia\Audit\Audit; use Utopia\Auth\Hash; use Utopia\Auth\Hashes\Argon2; @@ -2709,23 +2710,29 @@ Http::get('/v1/users/usage') ]; $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $count => $metric) { - $result = $dbForProject->findOne('stats', [ + $limit = $days['limit']; + $period = $days['period']; + + $tasks = []; + foreach ($metrics as $metric) { + $tasks[$metric . '_total'] = fn () => $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ + $tasks[$metric . '_data'] = fn () => $dbForProject->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::limit($limit), Query::orderDesc('time'), ]); + } + + $results = Promise::map($tasks)->await(); + + foreach ($metrics as $metric) { + $stats[$metric]['total'] = $results[$metric . '_total']['value'] ?? 0; $stats[$metric]['data'] = []; - foreach ($results as $result) { + foreach ($results[$metric . '_data'] as $result) { $stats[$metric]['data'][$result->getAttribute('time')] = [ 'value' => $result->getAttribute('value'), ]; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php index de20d058c4..40b3413667 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php @@ -10,6 +10,7 @@ use Appwrite\SDK\Deprecated; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; +use Utopia\Async\Promise; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -85,23 +86,29 @@ class Get extends Action ]; $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $limit = $days['limit']; + $period = $days['period']; + + $tasks = []; foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ + $tasks[$metric . '_total'] = fn () => $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ + $tasks[$metric . '_data'] = fn () => $dbForProject->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::limit($limit), Query::orderDesc('time'), ]); + } + + $results = Promise::map($tasks)->await(); + + foreach ($metrics as $metric) { + $stats[$metric]['total'] = $results[$metric . '_total']['value'] ?? 0; $stats[$metric]['data'] = []; - foreach ($results as $result) { + foreach ($results[$metric . '_data'] as $result) { $stats[$metric]['data'][$result->getAttribute('time')] = [ 'value' => $result->getAttribute('value'), ]; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php index 6f90e77e2b..04e2401ee2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php @@ -9,6 +9,7 @@ use Appwrite\SDK\Deprecated; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; +use Utopia\Async\Promise; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -83,23 +84,29 @@ class Get extends Action ]; $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $limit = $days['limit']; + $period = $days['period']; + + $tasks = []; foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ + $tasks[$metric . '_total'] = fn () => $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ + $tasks[$metric . '_data'] = fn () => $dbForProject->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::limit($limit), Query::orderDesc('time'), ]); + } + + $results = Promise::map($tasks)->await(); + + foreach ($metrics as $metric) { + $stats[$metric]['total'] = $results[$metric . '_total']['value'] ?? 0; $stats[$metric]['data'] = []; - foreach ($results as $result) { + foreach ($results[$metric . '_data'] as $result) { $stats[$metric]['data'][$result->getAttribute('time')] = [ 'value' => $result->getAttribute('value'), ]; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php index db5ad21358..f26e8ebc32 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php @@ -8,6 +8,7 @@ use Appwrite\SDK\Deprecated; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; +use Utopia\Async\Promise; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -76,23 +77,29 @@ class XList extends Action ]; $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $limit = $days['limit']; + $period = $days['period']; + + $tasks = []; foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ + $tasks[$metric . '_total'] = fn () => $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ + $tasks[$metric . '_data'] = fn () => $dbForProject->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::limit($limit), Query::orderDesc('time'), ]); + } + + $results = Promise::map($tasks)->await(); + + foreach ($metrics as $metric) { + $stats[$metric]['total'] = $results[$metric . '_total']['value'] ?? 0; $stats[$metric]['data'] = []; - foreach ($results as $result) { + foreach ($results[$metric . '_data'] as $result) { $stats[$metric]['data'][$result->getAttribute('time')] = [ 'value' => $result->getAttribute('value'), ]; diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Usage/Get.php b/src/Appwrite/Platform/Modules/Functions/Http/Usage/Get.php index 19476329bf..f3a9373c32 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Usage/Get.php @@ -8,6 +8,7 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; +use Utopia\Async\Promise; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -85,23 +86,29 @@ class Get extends Base ]; $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $limit = $days['limit']; + $period = $days['period']; + + $tasks = []; foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ + $tasks[$metric . '_total'] = fn () => $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ + $tasks[$metric . '_data'] = fn () => $dbForProject->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::limit($limit), Query::orderDesc('time'), ]); + } + + $results = Promise::map($tasks)->await(); + + foreach ($metrics as $metric) { + $stats[$metric]['total'] = $results[$metric . '_total']['value'] ?? 0; $stats[$metric]['data'] = []; - foreach ($results as $result) { + foreach ($results[$metric . '_data'] as $result) { $stats[$metric]['data'][$result->getAttribute('time')] = [ 'value' => $result->getAttribute('value'), ]; diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Usage/XList.php b/src/Appwrite/Platform/Modules/Functions/Http/Usage/XList.php index 38a95d4469..0b4f743302 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Usage/XList.php @@ -7,6 +7,7 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; +use Utopia\Async\Promise; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -77,23 +78,29 @@ class XList extends Base ]; $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $limit = $days['limit']; + $period = $days['period']; + + $tasks = []; foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ + $tasks[$metric . '_total'] = fn () => $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ + $tasks[$metric . '_data'] = fn () => $dbForProject->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::limit($limit), Query::orderDesc('time'), ]); + } + + $results = Promise::map($tasks)->await(); + + foreach ($metrics as $metric) { + $stats[$metric]['total'] = $results[$metric . '_total']['value'] ?? 0; $stats[$metric]['data'] = []; - foreach ($results as $result) { + foreach ($results[$metric . '_data'] as $result) { $stats[$metric]['data'][$result->getAttribute('time')] = [ 'value' => $result->getAttribute('value'), ]; diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php b/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php index a6768462d1..e9f198cb3c 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php @@ -8,6 +8,7 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; +use Utopia\Async\Promise; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -94,23 +95,29 @@ class Get extends Base ]; $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $limit = $days['limit']; + $period = $days['period']; + + $tasks = []; foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ + $tasks[$metric . '_total'] = fn () => $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ + $tasks[$metric . '_data'] = fn () => $dbForProject->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::limit($limit), Query::orderDesc('time'), ]); + } + + $results = Promise::map($tasks)->await(); + + foreach ($metrics as $metric) { + $stats[$metric]['total'] = $results[$metric . '_total']['value'] ?? 0; $stats[$metric]['data'] = []; - foreach ($results as $result) { + foreach ($results[$metric . '_data'] as $result) { $stats[$metric]['data'][$result->getAttribute('time')] = [ 'value' => $result->getAttribute('value'), ]; diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Usage/XList.php b/src/Appwrite/Platform/Modules/Sites/Http/Usage/XList.php index a90cb0cab9..347e27bdd9 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Usage/XList.php @@ -7,6 +7,7 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; +use Utopia\Async\Promise; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -80,23 +81,29 @@ class XList extends Base ]; $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $limit = $days['limit']; + $period = $days['period']; + + $tasks = []; foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ + $tasks[$metric . '_total'] = fn () => $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ + $tasks[$metric . '_data'] = fn () => $dbForProject->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::limit($limit), Query::orderDesc('time'), ]); + } + + $results = Promise::map($tasks)->await(); + + foreach ($metrics as $metric) { + $stats[$metric]['total'] = $results[$metric . '_total']['value'] ?? 0; $stats[$metric]['data'] = []; - foreach ($results as $result) { + foreach ($results[$metric . '_data'] as $result) { $stats[$metric]['data'][$result->getAttribute('time')] = [ 'value' => $result->getAttribute('value'), ]; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Usage/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Usage/Get.php index a7bda355da..28dc83f71d 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Usage/Get.php @@ -7,6 +7,7 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; +use Utopia\Async\Promise; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -77,27 +78,33 @@ class Get extends Action ]; $authorization->skip(function () use ($dbForProject, $dbForLogs, $bucket, $days, $metrics, &$stats) { + $limit = $days['limit']; + $period = $days['period']; + + $tasks = []; foreach ($metrics as $metric) { $db = ($metric === str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED)) ? $dbForLogs : $dbForProject; - $result = $db->findOne('stats', [ + $tasks[$metric . '_total'] = fn () => $db->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $db->find('stats', [ + $tasks[$metric . '_data'] = fn () => $db->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::limit($limit), Query::orderDesc('time'), ]); + } + + $results = Promise::map($tasks)->await(); + + foreach ($metrics as $metric) { + $stats[$metric]['total'] = $results[$metric . '_total']['value'] ?? 0; $stats[$metric]['data'] = []; - foreach ($results as $result) { + foreach ($results[$metric . '_data'] as $result) { $stats[$metric]['data'][$result->getAttribute('time')] = [ 'value' => $result->getAttribute('value'), ]; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Usage/XList.php b/src/Appwrite/Platform/Modules/Storage/Http/Usage/XList.php index 44fdd54e8c..06388bb773 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Usage/XList.php @@ -6,6 +6,7 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; +use Utopia\Async\Promise; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -65,23 +66,29 @@ class XList extends Action ]; $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $limit = $days['limit']; + $period = $days['period']; + + $tasks = []; foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ + $tasks[$metric . '_total'] = fn () => $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ + $tasks[$metric . '_data'] = fn () => $dbForProject->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::limit($limit), Query::orderDesc('time'), ]); + } + + $results = Promise::map($tasks)->await(); + + foreach ($metrics as $metric) { + $stats[$metric]['total'] = $results[$metric . '_total']['value'] ?? 0; $stats[$metric]['data'] = []; - foreach ($results as $result) { + foreach ($results[$metric . '_data'] as $result) { $stats[$metric]['data'][$result->getAttribute('time')] = [ 'value' => $result->getAttribute('value'), ];