Compare commits

...
26 changed files with 767 additions and 550 deletions
+3 -4
View File
@@ -24,6 +24,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Targets;
use Appwrite\Utopia\Database\Validator\Queries\Topics;
use Appwrite\Utopia\Response;
use MaxMind\Db\Reader;
use Utopia\Async\Promise;
use Utopia\Audit\Audit;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
@@ -55,8 +56,6 @@ use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
use function Swoole\Coroutine\batch;
Http::post('/v1/messaging/providers/mailgun')
->desc('Create Mailgun provider')
->groups(['api', 'messaging'])
@@ -2918,7 +2917,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers')
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject, $authorization) {
$subscribers = Promise::map(\array_map(function (Document $subscriber) use ($dbForProject, $authorization) {
return function () use ($subscriber, $dbForProject, $authorization) {
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
@@ -2927,7 +2926,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers')
->setAttribute('target', $target)
->setAttribute('userName', $user->getAttribute('name'));
};
}, $subscribers));
}, $subscribers))->await();
$response
->dynamic(new Document([
+14 -6
View File
@@ -4,6 +4,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\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
@@ -126,20 +127,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),
@@ -147,9 +147,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'),
];
+15 -8
View File
@@ -28,6 +28,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;
@@ -2750,23 +2751,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'),
];
+7
View File
@@ -60,6 +60,7 @@
"utopia-php/compression": "0.1.*",
"utopia-php/config": "1.*",
"utopia-php/console": "0.1.*",
"utopia-php/async": "@dev",
"utopia-php/database": "5.*",
"utopia-php/agents": "1.*",
"utopia-php/detector": "0.2.*",
@@ -111,6 +112,12 @@
"provide": {
"ext-phpiredis": "*"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/utopia-php/async.git"
}
],
"config": {
"platform": {
},
Generated
+188 -2
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f9225f2b580de0ccb796b2fb8c881384",
"content-hash": "98a9cffeea945bf19942a259a64fdb7b",
"packages": [
{
"name": "adhocore/jwt",
@@ -1634,6 +1634,71 @@
},
"time": "2026-01-21T04:14:03+00:00"
},
{
"name": "opis/closure",
"version": "4.5.0",
"source": {
"type": "git",
"url": "https://github.com/opis/closure.git",
"reference": "b97e42b95bb72d87507f5e2d137ceb239aea8d6b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opis/closure/zipball/b97e42b95bb72d87507f5e2d137ceb239aea8d6b",
"reference": "b97e42b95bb72d87507f5e2d137ceb239aea8d6b",
"shasum": ""
},
"require": {
"php": "^8.0"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.x-dev"
}
},
"autoload": {
"files": [
"src/functions.php"
],
"psr-4": {
"Opis\\Closure\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marius Sarca",
"email": "marius.sarca@gmail.com"
},
{
"name": "Sorin Sarca",
"email": "sarca_sorin@hotmail.com"
}
],
"description": "A library that can be used to serialize closures (anonymous functions) and arbitrary data.",
"homepage": "https://opis.io/closure",
"keywords": [
"anonymous classes",
"anonymous functions",
"closure",
"function",
"serializable",
"serialization",
"serialize"
],
"support": {
"issues": "https://github.com/opis/closure/issues",
"source": "https://github.com/opis/closure/tree/4.5.0"
},
"time": "2026-03-05T13:32:42+00:00"
},
{
"name": "paragonie/constant_time_encoding",
"version": "v3.1.3",
@@ -3500,6 +3565,125 @@
},
"time": "2026-02-09T12:46:39+00:00"
},
{
"name": "utopia-php/async",
"version": "dev-main",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/async.git",
"reference": "7a0c6957b41731a5c999382ad26a0b2fdbd19812"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/async/zipball/7a0c6957b41731a5c999382ad26a0b2fdbd19812",
"reference": "7a0c6957b41731a5c999382ad26a0b2fdbd19812",
"shasum": ""
},
"require": {
"opis/closure": "4.*",
"php": ">=8.1"
},
"require-dev": {
"amphp/amp": "3.*",
"amphp/parallel": "2.*",
"amphp/process": "^2.0",
"laravel/pint": "1.*",
"phpstan/phpstan": "2.*",
"phpunit/phpunit": "11.5.45",
"react/child-process": "0.*",
"react/event-loop": "1.*",
"swoole/ide-helper": "*"
},
"suggest": {
"amphp/amp": "Required for Amp promise adapter",
"amphp/parallel": "Required for Amp parallel adapter",
"ext-ev": "Required for ReactPHP event loop (recommended for best performance)",
"ext-parallel": "Required for parallel adapter (requires PHP ZTS build)",
"ext-sockets": "Required for Swoole Process adapter",
"ext-swoole": "Required for Swoole Thread and Process adapters (recommended for best performance)",
"react/child-process": "Required for ReactPHP parallel adapter",
"react/event-loop": "Required for ReactPHP promise and parallel adapters"
},
"default-branch": true,
"type": "library",
"autoload": {
"psr-4": {
"Utopia\\Async\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Utopia\\Tests\\": "tests/"
}
},
"scripts": {
"test-unit": [
"vendor/bin/phpunit tests/Unit --exclude-group no-swoole"
],
"test-promise-sync": [
"vendor/bin/phpunit tests/E2e/Promise/SyncTest.php"
],
"test-promise-swoole": [
"vendor/bin/phpunit tests/E2e/Promise/Swoole"
],
"test-promise-amp": [
"vendor/bin/phpunit tests/E2e/Promise/Amp"
],
"test-promise-react": [
"vendor/bin/phpunit tests/E2e/Promise/React"
],
"test-parallel-sync": [
"vendor/bin/phpunit tests/E2e/Parallel/Sync"
],
"test-parallel-swoole-thread": [
"vendor/bin/phpunit tests/E2e/Parallel/Swoole/ThreadTest.php"
],
"test-parallel-swoole-process": [
"vendor/bin/phpunit tests/E2e/Parallel/Swoole/ProcessTest.php"
],
"test-parallel-amp": [
"vendor/bin/phpunit tests/E2e/Parallel/Amp"
],
"test-parallel-react": [
"vendor/bin/phpunit tests/E2e/Parallel/React"
],
"test-parallel-ext": [
"php -n -d extension=parallel.so -d extension=sockets.so vendor/bin/phpunit tests/E2e/Parallel/Parallel"
],
"test-e2e": [
"vendor/bin/phpunit tests/E2e --exclude-group ext-parallel"
],
"test": [
"@test-unit",
"@test-e2e",
"@test-parallel-ext"
],
"lint": [
"vendor/bin/pint"
],
"format": [
"php -d memory_limit=4G vendor/bin/pint"
],
"check": [
"vendor/bin/phpstan analyse src tests --level=max --memory-limit=4G"
]
},
"license": [
"MIT"
],
"authors": [
{
"name": "Appwrite Team",
"email": "team@appwrite.io"
}
],
"description": "High-performance concurrent + parallel library with Promise and Parallel execution support for PHP.",
"support": {
"source": "https://github.com/utopia-php/async/tree/main",
"issues": "https://github.com/utopia-php/async/issues"
},
"time": "2026-01-09T06:16:09+00:00"
},
{
"name": "utopia-php/audit",
"version": "2.2.1",
@@ -8435,7 +8619,9 @@
],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": {},
"stability-flags": {
"utopia-php/async": 20
},
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
+2 -1
View File
@@ -183,7 +183,8 @@ class TransactionState
if (!isset($state[$collectionId])) {
return $baseCount;
}
$committedDocs = $dbForDatabases->find($collectionId, $queries);
$committedDocs = $dbForDatabases->find($collectionId, \array_merge($queries, [Query::select(['$id'])]));
$committedDocIds = [];
foreach ($committedDocs as $doc) {
$committedDocIds[$doc->getId()] = true;
@@ -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;
@@ -92,23 +93,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'),
];
@@ -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;
@@ -117,23 +118,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'),
];
@@ -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;
@@ -106,23 +107,29 @@ class XList extends Action
$metrics = $this->getMetrics();
$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'),
];
@@ -580,7 +580,7 @@ class Databases extends Action
Query::equal('databaseInternalId', [$databaseInternalId]),
Query::equal('type', [Database::VAR_RELATIONSHIP]),
Query::notEqual('collectionInternalId', $collectionInternalId),
Query::contains('options', ['"relatedCollection":"'. $collectionId .'"']),
Query::containsAny('options', ['"relatedCollection":"'. $collectionId .'"']),
],
$dbForProject,
function ($attribute) use ($dbForProject, $databaseInternalId) {
@@ -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'),
];
@@ -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'),
];
@@ -7,6 +7,7 @@ use Appwrite\Event\Realtime;
use Appwrite\Permission;
use Appwrite\Role;
use Exception;
use Utopia\Async\Promise;
use Utopia\Compression\Compression;
use Utopia\Config\Config;
use Utopia\Console;
@@ -20,8 +21,6 @@ use Utopia\Queue\Message;
use Utopia\Storage\Device;
use Utopia\System\System;
use function Swoole\Coroutine\batch;
class Screenshots extends Action
{
public static function getName(): string
@@ -154,7 +153,7 @@ class Screenshots extends Action
]);
$screenshotError = null;
$screenshots = batch(\array_map(function ($key) use ($configs, $apiKey, $site, $client, &$screenshotError) {
$screenshots = Promise::map(\array_map(function ($key) use ($configs, $apiKey, $site, $client, &$screenshotError) {
return function () use ($key, $configs, $apiKey, $site, $client, &$screenshotError) {
try {
$config = $configs[$key];
@@ -189,7 +188,7 @@ class Screenshots extends Action
return;
}
};
}, \array_keys($configs)));
}, \array_keys($configs)))->await();
if (!\is_null($screenshotError)) {
throw new \Exception($screenshotError);
@@ -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'),
];
@@ -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'),
];
@@ -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'),
];
@@ -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'),
];
@@ -92,7 +92,7 @@ class Delete extends Action
$ownersCount = $dbForProject->count(
collection: 'memberships',
queries: [
Query::contains('roles', ['owner']),
Query::containsAny('roles', ['owner']),
Query::equal('teamInternalId', [$team->getSequence()])
],
max: 2
@@ -92,7 +92,7 @@ class Update extends Action
$ownersCount = $dbForProject->count(
collection: 'memberships',
queries: [
Query::contains('roles', ['owner']),
Query::containsAny('roles', ['owner']),
Query::equal('teamInternalId', [$team->getSequence()])
],
max: 2
@@ -9,6 +9,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Swoole\Coroutine\WaitGroup;
use Utopia\Async\Promise;
use Utopia\Config\Adapters\Dotenv as ConfigDotenv;
use Utopia\Config\Config;
use Utopia\Config\Exceptions\Parse;
@@ -57,8 +58,6 @@ use Utopia\Validator\WhiteList;
use Utopia\VCS\Adapter\Git\GitHub;
use Utopia\VCS\Exception\FileNotFound;
use function Swoole\Coroutine\batch;
class XList extends Action
{
use HTTP;
@@ -153,7 +152,7 @@ class XList extends Action
return $repo;
}, $repos);
$repos = batch(\array_map(function ($repo) use ($type, $github) {
$repos = Promise::map(\array_map(function ($repo) use ($type, $github) {
return function () use ($repo, $type, $github) {
$files = $github->listRepositoryContents($repo['organization'], $repo['name'], '');
$files = \array_column($files, 'name');
@@ -307,7 +306,7 @@ class XList extends Action
return $repo;
};
}, $repos));
}, $repos))->await();
$repos = \array_map(function ($repo) {
return new Document($repo);
+1 -1
View File
@@ -148,7 +148,7 @@ class Interval extends Action
$staleExecutions = $dbForProject->find('executions', [
Query::equal('status', ['processing']),
Query::lessThan('$createdAt', $staleThreshold),
Query::createdBefore($staleThreshold),
Query::limit(100),
]);
+197 -238
View File
@@ -10,6 +10,7 @@ use Appwrite\Extend\Exception;
use Executor\Executor;
use Throwable;
use Utopia\Abuse\Adapters\TimeLimit\Database as AbuseDatabase;
use Utopia\Async\Promise;
use Utopia\Audit\Adapter\SQL;
use Utopia\Audit\Audit;
use Utopia\Cache\Adapter\Filesystem;
@@ -32,8 +33,6 @@ use Utopia\Queue\Message;
use Utopia\Storage\Device;
use Utopia\System\System;
use function Swoole\Coroutine\batch;
class Deletes extends Action
{
protected array $selects = ['$sequence', '$id', '$collection', '$permissions', '$updatedAt'];
@@ -692,7 +691,7 @@ class Deletes extends Action
$callback($dbForDatabases);
};
batch(array_map(
Promise::map(array_map(
fn ($databaseDoc) => fn () => $this->cleanDatabase(
$databaseDoc,
$executionActionPerDatabase,
@@ -700,62 +699,58 @@ class Deletes extends Action
$projectCollectionIds
),
$databasesToClean
));
))->await();
// Delete Platforms
$this->deleteByGroup('platforms', [
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform);
// Delete project and function rules
$this->deleteByGroup('rules', [
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform, function (Document $document) use ($dbForPlatform, $certificates) {
$this->deleteRule($dbForPlatform, $document, $certificates);
});
// Delete Keys
$this->deleteByGroup('keys', [
Query::equal('resourceType', ['projects']),
Query::equal('resourceInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform);
// Delete Webhooks
$this->deleteByGroup('webhooks', [
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform);
// Delete VCS Installations
$this->deleteByGroup('installations', [
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform);
// Delete VCS Repositories
$this->deleteByGroup('repositories', [
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform);
// Delete VCS comments
$this->deleteByGroup('vcsComments', [
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform);
// Delete Schedules
$this->deleteByGroup('schedules', [
Query::equal('projectId', [$projectId]),
Query::orderAsc()
], $dbForPlatform);
// Delete platform-level resources concurrently
Promise::map([
// Delete Platforms
fn () => $this->deleteByGroup('platforms', [
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform),
// Delete project and function rules
fn () => $this->deleteByGroup('rules', [
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform, function (Document $document) use ($dbForPlatform, $certificates) {
$this->deleteRule($dbForPlatform, $document, $certificates);
}),
// Delete Keys
fn () => $this->deleteByGroup('keys', [
Query::equal('resourceType', ['projects']),
Query::equal('resourceInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform),
// Delete Webhooks
fn () => $this->deleteByGroup('webhooks', [
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform),
// Delete VCS Installations
fn () => $this->deleteByGroup('installations', [
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform),
// Delete VCS Repositories
fn () => $this->deleteByGroup('repositories', [
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform),
// Delete VCS comments
fn () => $this->deleteByGroup('vcsComments', [
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform),
// Delete Schedules
fn () => $this->deleteByGroup('schedules', [
Query::equal('projectId', [$projectId]),
Query::orderAsc()
], $dbForPlatform),
])->await();
// Delete metadata table
if ($projectTables) {
batch(array_map(
Promise::map(array_map(
fn ($databaseDoc) => fn () =>
$executionActionPerDatabase(
$databaseDoc,
@@ -763,7 +758,7 @@ class Deletes extends Action
$dbForDatabases->deleteCollection(Database::METADATA)
),
$databasesToClean
));
))->await();
} elseif ($sharedTablesV1) {
$this->deleteByGroup(
Database::METADATA,
@@ -830,48 +825,46 @@ class Deletes extends Action
$userInternalId = $document->getSequence();
$dbForProject = $getProjectDB($project);
// Delete all sessions of this user from the sessions table and update the sessions field of the user record
$this->deleteByGroup('sessions', [
Query::equal('userInternalId', [$userInternalId]),
Query::orderAsc()
], $dbForProject);
if ($project->getId() === 'console') {
// Delete Keys
$this->deleteByGroup('keys', [
Query::equal('resourceInternalId', [$userInternalId]),
Query::equal('resourceType', ['users']),
Query::orderAsc()
], $dbForProject);
}
$dbForProject->purgeCachedDocument('users', $userId);
// Delete Memberships and decrement team membership counts
$this->deleteByGroup('memberships', [
Query::equal('userInternalId', [$userInternalId]),
Query::orderAsc()
], $dbForProject, function (Document $document) use ($dbForProject) {
if ($document->getAttribute('confirm')) { // Count only confirmed members
$teamId = $document->getAttribute('teamId');
$team = $dbForProject->getDocument('teams', $teamId);
if (!$team->isEmpty()) {
$dbForProject->decreaseDocumentAttribute('teams', $teamId, 'total', 1, 0);
// Delete user-related resources concurrently
Promise::map([
// Delete all sessions of this user
fn () => $this->deleteByGroup('sessions', [
Query::equal('userInternalId', [$userInternalId]),
Query::orderAsc()
], $dbForProject),
// Delete Keys (console project only)
fn () => $project->getId() === 'console'
? $this->deleteByGroup('keys', [
Query::equal('resourceInternalId', [$userInternalId]),
Query::equal('resourceType', ['users']),
Query::orderAsc()
], $dbForProject)
: null,
// Delete Memberships and decrement team membership counts
fn () => $this->deleteByGroup('memberships', [
Query::equal('userInternalId', [$userInternalId]),
Query::orderAsc()
], $dbForProject, function (Document $document) use ($dbForProject) {
if ($document->getAttribute('confirm')) { // Count only confirmed members
$teamId = $document->getAttribute('teamId');
$team = $dbForProject->getDocument('teams', $teamId);
if (!$team->isEmpty()) {
$dbForProject->decreaseDocumentAttribute('teams', $teamId, 'total', 1, 0);
}
}
}
});
// Delete tokens
$this->deleteByGroup('tokens', [
Query::equal('userInternalId', [$userInternalId]),
Query::orderAsc()
], $dbForProject);
// Delete identities
Identities::delete($dbForProject, Query::equal('userInternalId', [$userInternalId]));
// Delete targets
Targets::delete($dbForProject, Query::equal('userInternalId', [$userInternalId]));
}),
// Delete tokens
fn () => $this->deleteByGroup('tokens', [
Query::equal('userInternalId', [$userInternalId]),
Query::orderAsc()
], $dbForProject),
// Delete identities
fn () => Identities::delete($dbForProject, Query::equal('userInternalId', [$userInternalId])),
// Delete targets
fn () => Targets::delete($dbForProject, Query::equal('userInternalId', [$userInternalId])),
])->await();
}
/**
@@ -890,7 +883,7 @@ class Deletes extends Action
// Delete Executions
$this->deleteByGroup('executions', [
Query::select([...$this->selects, '$createdAt']),
Query::lessThan('$createdAt', $datetime),
Query::createdBefore($datetime),
Query::orderDesc('$createdAt'),
Query::orderDesc(),
], $dbForProject);
@@ -941,7 +934,7 @@ class Deletes extends Action
Query::select([...$this->selects, '$createdAt']),
Query::equal('resourceInternalId', [$resourceInternalId]),
Query::equal('resourceType', [$resourceType]),
Query::lessThan('$createdAt', $cutoffTime),
Query::createdBefore($cutoffTime),
Query::orderDesc('$createdAt'),
Query::orderDesc(),
], $dbForProject);
@@ -964,10 +957,10 @@ class Deletes extends Action
};
/* perform processing in parallel */
batch([
Promise::map([
fn () => $processResource(RESOURCE_TYPE_SITES),
fn () => $processResource(RESOURCE_TYPE_FUNCTIONS),
]);
])->await();
}
}
@@ -986,7 +979,7 @@ class Deletes extends Action
// Delete Sessions
$this->deleteByGroup('sessions', [
Query::select([...$this->selects, '$createdAt']),
Query::lessThan('$createdAt', $expired),
Query::createdBefore($expired),
Query::orderDesc('$createdAt'),
Query::orderDesc(),
], $dbForProject);
@@ -1078,72 +1071,57 @@ class Deletes extends Action
$siteId = $document->getId();
$siteInternalId = $document->getSequence();
/**
* Delete rules for site
*/
Console::info("Deleting rules for site " . $siteId);
$this->deleteByGroup('rules', [
Query::equal('deploymentResourceType', ['site']),
Query::equal('deploymentResourceInternalId', [$siteInternalId]),
Query::equal('projectInternalId', [$project->getSequence()])
], $dbForPlatform, function (Document $document) use ($dbForPlatform, $certificates) {
$this->deleteRule($dbForPlatform, $document, $certificates);
});
/**
* Delete Variables
*/
Console::info("Deleting variables for site " . $siteId);
$this->deleteByGroup('variables', [
Query::equal('resourceType', ['site']),
Query::equal('resourceInternalId', [$siteInternalId])
], $dbForProject);
/**
* Delete Deployments
*/
Console::info("Deleting deployments for site " . $siteId);
// Delete site resources concurrently
Console::info("Deleting resources for site " . $siteId);
$deploymentInternalIds = [];
$deploymentIds = [];
$this->deleteByGroup('deployments', [
Query::equal('resourceInternalId', [$siteInternalId]),
Query::equal('resourceType', ['sites']),
Query::orderAsc()
], $dbForProject, function (Document $document) use ($project, $certificates, $deviceForSites, $deviceForBuilds, $deviceForFiles, $dbForPlatform, &$deploymentInternalIds) {
$deploymentInternalIds[] = $document->getSequence();
$deploymentIds[] = $document->getId();
$this->deleteBuildFiles($deviceForBuilds, $document);
$this->deleteDeploymentFiles($deviceForSites, $document);
$this->deleteDeploymentScreenshots($deviceForFiles, $dbForPlatform, $document);
});
/**
* Delete Logs
*/
Console::info("Deleting logs for site " . $siteId);
$this->deleteByGroup('executions', [
Query::select($this->selects),
Query::equal('resourceInternalId', [$siteInternalId]),
Query::equal('resourceType', ['sites']),
Query::orderAsc()
], $dbForProject);
/**
* Delete VCS Repositories and VCS Comments
*/
Console::info("Deleting VCS repositories and comments linked to site " . $siteId);
$this->deleteByGroup('repositories', [
Query::equal('projectInternalId', [$project->getSequence()]),
Query::equal('resourceInternalId', [$siteInternalId]),
Query::equal('resourceType', ['site']),
], $dbForPlatform, function (Document $document) use ($dbForPlatform) {
$providerRepositoryId = $document->getAttribute('providerRepositoryId', '');
$projectInternalId = $document->getAttribute('projectInternalId', '');
$this->deleteByGroup('vcsComments', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::equal('projectInternalId', [$projectInternalId]),
], $dbForPlatform);
});
Promise::map([
// Delete rules for site
fn () => $this->deleteByGroup('rules', [
Query::equal('deploymentResourceType', ['site']),
Query::equal('deploymentResourceInternalId', [$siteInternalId]),
Query::equal('projectInternalId', [$project->getSequence()])
], $dbForPlatform, function (Document $document) use ($dbForPlatform, $certificates) {
$this->deleteRule($dbForPlatform, $document, $certificates);
}),
// Delete Variables
fn () => $this->deleteByGroup('variables', [
Query::equal('resourceType', ['site']),
Query::equal('resourceInternalId', [$siteInternalId])
], $dbForProject),
// Delete Deployments
fn () => $this->deleteByGroup('deployments', [
Query::equal('resourceInternalId', [$siteInternalId]),
Query::equal('resourceType', ['sites']),
Query::orderAsc()
], $dbForProject, function (Document $document) use ($project, $certificates, $deviceForSites, $deviceForBuilds, $deviceForFiles, $dbForPlatform, &$deploymentInternalIds) {
$deploymentInternalIds[] = $document->getSequence();
$deploymentIds[] = $document->getId();
$this->deleteBuildFiles($deviceForBuilds, $document);
$this->deleteDeploymentFiles($deviceForSites, $document);
$this->deleteDeploymentScreenshots($deviceForFiles, $dbForPlatform, $document);
}),
// Delete Logs
fn () => $this->deleteByGroup('executions', [
Query::select($this->selects),
Query::equal('resourceInternalId', [$siteInternalId]),
Query::equal('resourceType', ['sites']),
Query::orderAsc()
], $dbForProject),
// Delete VCS Repositories and VCS Comments
fn () => $this->deleteByGroup('repositories', [
Query::equal('projectInternalId', [$project->getSequence()]),
Query::equal('resourceInternalId', [$siteInternalId]),
Query::equal('resourceType', ['site']),
], $dbForPlatform, function (Document $document) use ($dbForPlatform) {
$providerRepositoryId = $document->getAttribute('providerRepositoryId', '');
$projectInternalId = $document->getAttribute('projectInternalId', '');
$this->deleteByGroup('vcsComments', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::equal('projectInternalId', [$projectInternalId]),
], $dbForPlatform);
}),
])->await();
}
/**
@@ -1163,81 +1141,62 @@ class Deletes extends Action
$functionId = $document->getId();
$functionInternalId = $document->getSequence();
/**
* Delete rules
*/
Console::info("Deleting rules for function " . $functionId);
$this->deleteByGroup('rules', [
Query::equal('deploymentResourceType', ['function']),
Query::equal('deploymentResourceInternalId', [$functionInternalId]),
Query::equal('projectInternalId', [$project->getSequence()]),
Query::orderAsc()
], $dbForPlatform, function (Document $document) use ($project, $dbForPlatform, $certificates) {
$this->deleteRule($dbForPlatform, $document, $certificates);
});
/**
* Delete Variables
*/
Console::info("Deleting variables for function " . $functionId);
$this->deleteByGroup('variables', [
Query::equal('resourceInternalId', [$functionInternalId]),
Query::equal('resourceType', ['function']),
Query::orderAsc()
], $dbForProject);
/**
* Delete Deployments
*/
Console::info("Deleting deployments for function " . $functionId);
$deploymentInternalIds = [];
$this->deleteByGroup('deployments', [
Query::equal('resourceInternalId', [$functionInternalId]),
Query::equal('resourceType', ['functions']),
Query::orderAsc()
], $dbForProject, function (Document $document) use ($dbForPlatform, $project, $certificates, $deviceForFunctions, $deviceForBuilds, &$deploymentInternalIds) {
$deploymentInternalIds[] = $document->getSequence();
$this->deleteDeploymentFiles($deviceForFunctions, $document);
$this->deleteBuildFiles($deviceForBuilds, $document);
});
/**
* Delete Executions
*/
Console::info("Deleting executions for function " . $functionId);
$this->deleteByGroup('executions', [
Query::select($this->selects),
Query::equal('resourceInternalId', [$functionInternalId]),
Query::equal('resourceType', ['functions']),
Query::orderAsc()
], $dbForProject);
/**
* Delete VCS Repositories and VCS Comments
*/
Console::info("Deleting VCS repositories and comments linked to function " . $functionId);
$this->deleteByGroup('repositories', [
Query::equal('projectInternalId', [$project->getSequence()]),
Query::equal('resourceInternalId', [$functionInternalId]),
Query::equal('resourceType', ['function']),
Query::orderAsc()
], $dbForPlatform, function (Document $document) use ($dbForPlatform) {
$providerRepositoryId = $document->getAttribute('providerRepositoryId', '');
$projectInternalId = $document->getAttribute('projectInternalId', '');
$this->deleteByGroup('vcsComments', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform);
});
/**
* Request executor to delete all deployment containers
*/
Console::info("Requesting executor to delete all deployment containers for function " . $functionId);
// Request executor to delete all deployment containers before deleting deployments
Console::info("Deleting resources for function " . $functionId);
$this->deleteRuntimes($getProjectDB, $document, $project, $executor);
// Delete function resources concurrently
$deploymentInternalIds = [];
Promise::map([
// Delete rules
fn () => $this->deleteByGroup('rules', [
Query::equal('deploymentResourceType', ['function']),
Query::equal('deploymentResourceInternalId', [$functionInternalId]),
Query::equal('projectInternalId', [$project->getSequence()]),
Query::orderAsc()
], $dbForPlatform, function (Document $document) use ($project, $dbForPlatform, $certificates) {
$this->deleteRule($dbForPlatform, $document, $certificates);
}),
// Delete Variables
fn () => $this->deleteByGroup('variables', [
Query::equal('resourceInternalId', [$functionInternalId]),
Query::equal('resourceType', ['function']),
Query::orderAsc()
], $dbForProject),
// Delete Deployments
fn () => $this->deleteByGroup('deployments', [
Query::equal('resourceInternalId', [$functionInternalId]),
Query::equal('resourceType', ['functions']),
Query::orderAsc()
], $dbForProject, function (Document $document) use ($dbForPlatform, $project, $certificates, $deviceForFunctions, $deviceForBuilds, &$deploymentInternalIds) {
$deploymentInternalIds[] = $document->getSequence();
$this->deleteDeploymentFiles($deviceForFunctions, $document);
$this->deleteBuildFiles($deviceForBuilds, $document);
}),
// Delete Executions
fn () => $this->deleteByGroup('executions', [
Query::select($this->selects),
Query::equal('resourceInternalId', [$functionInternalId]),
Query::equal('resourceType', ['functions']),
Query::orderAsc()
], $dbForProject),
// Delete VCS Repositories and VCS Comments
fn () => $this->deleteByGroup('repositories', [
Query::equal('projectInternalId', [$project->getSequence()]),
Query::equal('resourceInternalId', [$functionInternalId]),
Query::equal('resourceType', ['function']),
Query::orderAsc()
], $dbForPlatform, function (Document $document) use ($dbForPlatform) {
$providerRepositoryId = $document->getAttribute('providerRepositoryId', '');
$projectInternalId = $document->getAttribute('projectInternalId', '');
$this->deleteByGroup('vcsComments', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::equal('projectInternalId', [$projectInternalId]),
Query::orderAsc()
], $dbForPlatform);
}),
])->await();
}
private function deleteDeploymentScreenshots(Device $deviceForFiles, Database $dbForPlatform, Document $deployment): void
+1 -1
View File
@@ -122,7 +122,7 @@ class Functions extends Action
while ($sum >= $limit) {
$functions = $dbForProject->find('functions', [
Query::select(['$id', 'events']), // Skip variables subqueries
Query::contains('events', $events),
Query::containsAny('events', $events),
Query::limit($limit),
Query::offset($offset),
Query::orderAsc('$sequence'),
+29 -33
View File
@@ -9,6 +9,7 @@ use Appwrite\Usage\Context as UsageContext;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberUtil;
use Swoole\Runtime;
use Utopia\Async\Promise;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
@@ -48,8 +49,6 @@ use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
use Utopia\System\System;
use function Swoole\Coroutine\batch;
class Messaging extends Action
{
private ?Local $localDevice = null;
@@ -147,44 +146,41 @@ class Messaging extends Action
*/
$allTargets = [];
if (\count($topicIds) > 0) {
$topics = $dbForProject->find('topics', [
// Fetch topics, users, and targets concurrently
$results = Promise::map([
'topics' => fn () => \count($topicIds) > 0 ? $dbForProject->find('topics', [
Query::equal('$id', $topicIds),
Query::limit(\count($topicIds)),
]);
foreach ($topics as $topic) {
$targets = \array_filter($topic->getAttribute('targets'), function (Document $target) use ($providerType) {
return $target->getAttribute('providerType') === $providerType;
});
\array_push($allTargets, ...$targets);
}
}
if (\count($userIds) > 0) {
$users = $dbForProject->find('users', [
]) : [],
'users' => fn () => \count($userIds) > 0 ? $dbForProject->find('users', [
Query::equal('$id', $userIds),
Query::limit(\count($userIds)),
]);
foreach ($users as $user) {
$targets = \array_filter($user->getAttribute('targets'), function (Document $target) use ($providerType) {
return $target->getAttribute('providerType') === $providerType;
});
\array_push($allTargets, ...$targets);
}
}
if (\count($targetIds) > 0) {
$targets = $dbForProject->find('targets', [
]) : [],
'targets' => fn () => \count($targetIds) > 0 ? $dbForProject->find('targets', [
Query::equal('$id', $targetIds),
Query::equal('providerType', [$providerType]),
Query::limit(\count($targetIds)),
]);
]) : [],
])->await();
foreach ($results['topics'] as $topic) {
$targets = \array_filter($topic->getAttribute('targets'), function (Document $target) use ($providerType) {
return $target->getAttribute('providerType') === $providerType;
});
\array_push($allTargets, ...$targets);
}
foreach ($results['users'] as $user) {
$targets = \array_filter($user->getAttribute('targets'), function (Document $target) use ($providerType) {
return $target->getAttribute('providerType') === $providerType;
});
\array_push($allTargets, ...$targets);
}
\array_push($allTargets, ...$results['targets']);
if (empty($allTargets)) {
$dbForProject->updateDocument('messages', $message->getId(), $message->setAttributes([
'status' => MessageStatus::FAILED,
@@ -241,7 +237,7 @@ class Messaging extends Action
/**
* @var array<array> $results
*/
$results = batch(\array_map(function ($providerId) use ($identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $project, $publisherForUsage) {
$results = Promise::map(\array_map(function ($providerId) use ($identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $project, $publisherForUsage) {
return function () use ($providerId, $identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $project, $publisherForUsage) {
if (\array_key_exists($providerId, $providers)) {
$provider = $providers[$providerId];
@@ -269,7 +265,7 @@ class Messaging extends Action
$adapter->getMaxMessagesPerRequest()
);
return batch(\array_map(function ($batch) use ($message, $provider, $adapter, $dbForProject, $deviceForFiles, $project, $publisherForUsage) {
return Promise::map(\array_map(function ($batch) use ($message, $provider, $adapter, $dbForProject, $deviceForFiles, $project, $publisherForUsage) {
return function () use ($batch, $message, $provider, $adapter, $dbForProject, $deviceForFiles, $project, $publisherForUsage) {
$deliveredTotal = 0;
$deliveryErrors = [];
@@ -333,9 +329,9 @@ class Messaging extends Action
];
}
};
}, $batches));
}, $batches))->await();
};
}, \array_keys($identifiers)));
}, \array_keys($identifiers)))->await();
$results = \array_merge(...$results);
+173 -180
View File
@@ -5,6 +5,7 @@ namespace Appwrite\Platform\Workers;
use Appwrite\Platform\Action;
use Exception;
use Throwable;
use Utopia\Async\Promise;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
@@ -93,80 +94,84 @@ class StatsResources extends Action
$region = $project->getAttribute('region');
$platforms = $dbForPlatform->count('platforms', [
Query::equal('projectInternalId', [$project->getSequence()])
]);
$webhooks = $dbForPlatform->count('webhooks', [
Query::equal('projectInternalId', [$project->getSequence()])
]);
$keys = $dbForPlatform->count('keys', [
Query::equal('resourceType', ['projects']),
Query::equal('resourceInternalId', [$project->getSequence()]),
]);
$domains = $dbForPlatform->count('rules', [
Query::equal('projectInternalId', [$project->getSequence()]),
Query::equal('owner', ['']),
]);
$databases = $dbForProject->count('databases', [Query::equal('type', [DATABASE_TYPE_LEGACY, DATABASE_TYPE_TABLESDB])]);
$documentsdb = $dbForProject->count('databases', [Query::equal('type', [DATABASE_TYPE_DOCUMENTSDB])]);
$vectorsdb = $dbForProject->count('databases', [Query::equal('type', [DATABASE_TYPE_VECTORSDB])]);
$buckets = $dbForProject->count('buckets');
$users = $dbForProject->count('users');
$last30Days = (new \DateTime())->sub(\DateInterval::createFromDateString('30 days'))->format('Y-m-d 00:00:00');
$usersMAU = $dbForProject->count('users', [
Query::greaterThanEqual('accessedAt', $last30Days)
]);
$last24Hours = (new \DateTime())->sub(\DateInterval::createFromDateString('24 hours'))->format('Y-m-d h:m:00');
$usersDAU = $dbForProject->count('users', [
Query::greaterThanEqual('accessedAt', $last24Hours)
]);
$last24Hours = (new \DateTime())->sub(\DateInterval::createFromDateString('24 hours'))->format('Y-m-d H:i:00');
$last7Days = (new \DateTime())->sub(\DateInterval::createFromDateString('7 days'))->format('Y-m-d 00:00:00');
$usersWAU = $dbForProject->count('users', [
Query::greaterThanEqual('accessedAt', $last7Days)
]);
$teams = $dbForProject->count('teams');
$functions = $dbForProject->count('functions');
$messages = $dbForProject->count('messages');
$providers = $dbForProject->count('providers');
$topics = $dbForProject->count('topics');
$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])
]);
$results = Promise::map([
'platforms' => fn () => $dbForPlatform->count('platforms', [
Query::equal('projectInternalId', [$project->getSequence()])
]),
'webhooks' => fn () => $dbForPlatform->count('webhooks', [
Query::equal('projectInternalId', [$project->getSequence()])
]),
'keys' => fn () => $dbForPlatform->count('keys', [
Query::equal('resourceType', ['projects']),
Query::equal('resourceInternalId', [$project->getSequence()]),
]),
'domains' => fn () => $dbForPlatform->count('rules', [
Query::equal('projectInternalId', [$project->getSequence()]),
Query::equal('owner', ['']),
]),
'databases' => fn () => $dbForProject->count('databases', [
Query::equal('type', [DATABASE_TYPE_LEGACY, DATABASE_TYPE_TABLESDB])
]),
'documentsdb' => fn () => $dbForProject->count('databases', [
Query::equal('type', [DATABASE_TYPE_DOCUMENTSDB])
]),
'vectorsdb' => fn () => $dbForProject->count('databases', [
Query::equal('type', [DATABASE_TYPE_VECTORSDB])
]),
'buckets' => fn () => $dbForProject->count('buckets'),
'users' => fn () => $dbForProject->count('users'),
'usersMAU' => fn () => $dbForProject->count('users', [
Query::greaterThanEqual('accessedAt', $last30Days)
]),
'usersDAU' => fn () => $dbForProject->count('users', [
Query::greaterThanEqual('accessedAt', $last24Hours)
]),
'usersWAU' => fn () => $dbForProject->count('users', [
Query::greaterThanEqual('accessedAt', $last7Days)
]),
'teams' => fn () => $dbForProject->count('teams'),
'functions' => fn () => $dbForProject->count('functions'),
'messages' => fn () => $dbForProject->count('messages'),
'providers' => fn () => $dbForProject->count('providers'),
'topics' => fn () => $dbForProject->count('topics'),
'targets' => fn () => $dbForProject->count('targets'),
'emailTargets' => fn () => $dbForProject->count('targets', [
Query::equal('providerType', [MESSAGE_TYPE_EMAIL])
]),
'pushTargets' => fn () => $dbForProject->count('targets', [
Query::equal('providerType', [MESSAGE_TYPE_PUSH])
]),
'smsTargets' => fn () => $dbForProject->count('targets', [
Query::equal('providerType', [MESSAGE_TYPE_SMS])
]),
])->await();
$metrics = [
METRIC_DATABASES => $databases,
METRIC_DATABASES_DOCUMENTSDB => $documentsdb,
METRIC_DATABASES_VECTORSDB => $vectorsdb,
METRIC_BUCKETS => $buckets,
METRIC_USERS => $users,
METRIC_FUNCTIONS => $functions,
METRIC_TEAMS => $teams,
METRIC_MESSAGES => $messages,
METRIC_MAU => $usersMAU,
METRIC_DAU => $usersDAU,
METRIC_WAU => $usersWAU,
METRIC_WEBHOOKS => $webhooks,
METRIC_PLATFORMS => $platforms,
METRIC_PROVIDERS => $providers,
METRIC_TOPICS => $topics,
METRIC_KEYS => $keys,
METRIC_DOMAINS => $domains,
METRIC_TARGETS => $targets,
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,
METRIC_DATABASES => $results['databases'],
METRIC_DATABASES_DOCUMENTSDB => $results['documentsdb'],
METRIC_DATABASES_VECTORSDB => $results['vectorsdb'],
METRIC_BUCKETS => $results['buckets'],
METRIC_USERS => $results['users'],
METRIC_FUNCTIONS => $results['functions'],
METRIC_TEAMS => $results['teams'],
METRIC_MESSAGES => $results['messages'],
METRIC_MAU => $results['usersMAU'],
METRIC_DAU => $results['usersDAU'],
METRIC_WAU => $results['usersWAU'],
METRIC_WEBHOOKS => $results['webhooks'],
METRIC_PLATFORMS => $results['platforms'],
METRIC_PROVIDERS => $results['providers'],
METRIC_TOPICS => $results['topics'],
METRIC_KEYS => $results['keys'],
METRIC_DOMAINS => $results['domains'],
METRIC_TARGETS => $results['targets'],
str_replace('{providerType}', MESSAGE_TYPE_EMAIL, METRIC_PROVIDER_TYPE_TARGETS) => $results['emailTargets'],
str_replace('{providerType}', MESSAGE_TYPE_PUSH, METRIC_PROVIDER_TYPE_TARGETS) => $results['pushTargets'],
str_replace('{providerType}', MESSAGE_TYPE_SMS, METRIC_PROVIDER_TYPE_TARGETS) => $results['smsTargets'],
];
foreach ($metrics as $metric => $value) {
@@ -210,22 +215,21 @@ class StatsResources extends Action
$totalStorage = 0;
$this->foreachDocument($dbForProject, 'buckets', [], function ($bucket) use ($dbForProject, $dbForLogs, $region, &$totalFiles, &$totalStorage) {
try {
$files = $dbForProject->count('bucket_' . $bucket->getSequence());
$bucketResults = Promise::map([
'files' => fn () => $dbForProject->count('bucket_' . $bucket->getSequence()),
'storage' => fn () => $dbForProject->sum('bucket_' . $bucket->getSequence(), 'sizeActual'),
])->await();
} catch (Throwable $th) {
call_user_func_array($this->logError, [$th, "StatsResources", "count_for_bucket_{$bucket->getSequence()}"]);
call_user_func_array($this->logError, [$th, "StatsResources", "bucket_{$bucket->getSequence()}"]);
return;
}
$files = $bucketResults['files'];
$storage = $bucketResults['storage'];
$metric = str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES);
$this->createStatsDocuments($region, $metric, $files);
try {
$storage = $dbForProject->sum('bucket_' . $bucket->getSequence(), 'sizeActual');
} catch (Throwable $th) {
call_user_func_array($this->logError, [$th, "StatsResources", "sum_for_bucket_{$bucket->getSequence()}"]);
return;
}
$metric = str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_STORAGE);
$this->createStatsDocuments($region, $metric, $storage);
@@ -341,16 +345,25 @@ class StatsResources extends Action
}
$this->foreachDocument($dbForProject, 'database_' . $database->getSequence(), [], function ($collection) use ($dbForDatabases, $database, $region, &$databaseStorage, &$databaseDocuments, $databaseIdCollectionIdDocumentsMetric, $databaseIdCollectionIdStorageMetric) {
$documents = $dbForDatabases->count('database_' . $database->getSequence() . '_collection_' . $collection->getSequence());
$collectionName = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence();
try {
$collectionResults = Promise::map([
'documents' => fn () => $dbForDatabases->count($collectionName),
'storage' => fn () => $dbForDatabases->getSizeOfCollection($collectionName),
])->await();
} catch (Throwable $th) {
call_user_func_array($this->logError, [$th, "StatsResources", "collection_{$database->getSequence()}_{$collection->getSequence()}"]);
return;
}
$metric = str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getSequence(), $collection->getSequence()], $databaseIdCollectionIdDocumentsMetric);
$this->createStatsDocuments($region, $metric, $documents);
$databaseDocuments += $documents;
$this->createStatsDocuments($region, $metric, $collectionResults['documents']);
$databaseDocuments += $collectionResults['documents'];
$collectionStorage = $dbForDatabases->getSizeOfCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence());
$metric = str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getSequence(), $collection->getSequence()], $databaseIdCollectionIdStorageMetric);
$this->createStatsDocuments($region, $metric, $collectionStorage);
$databaseStorage += $collectionStorage;
$this->createStatsDocuments($region, $metric, $collectionResults['storage']);
$databaseStorage += $collectionResults['storage'];
});
$metric = str_replace(['{databaseInternalId}'], [$database->getSequence()], $databaseIdDocumentsMetric);
@@ -364,14 +377,16 @@ class StatsResources extends Action
protected function countForSitesAndFunctions(Database $dbForProject, string $region): void
{
$deploymentsStorage = $dbForProject->sum('deployments', 'sourceSize');
$buildsStorage = $dbForProject->sum('deployments', 'buildSize');
$this->createStatsDocuments($region, METRIC_DEPLOYMENTS_STORAGE, $deploymentsStorage);
$this->createStatsDocuments($region, METRIC_BUILDS_STORAGE, $buildsStorage);
$results = Promise::map([
'deploymentsStorage' => fn () => $dbForProject->sum('deployments', 'sourceSize'),
'buildsStorage' => fn () => $dbForProject->sum('deployments', 'buildSize'),
'deployments' => fn () => $dbForProject->count('deployments'),
])->await();
$deployments = $dbForProject->count('deployments');
$this->createStatsDocuments($region, METRIC_DEPLOYMENTS, $deployments);
$this->createStatsDocuments($region, METRIC_BUILDS, $deployments);
$this->createStatsDocuments($region, METRIC_DEPLOYMENTS_STORAGE, $results['deploymentsStorage']);
$this->createStatsDocuments($region, METRIC_BUILDS_STORAGE, $results['buildsStorage']);
$this->createStatsDocuments($region, METRIC_DEPLOYMENTS, $results['deployments']);
$this->createStatsDocuments($region, METRIC_BUILDS, $results['deployments']);
$this->countForFunctions($dbForProject, $region);
$this->countForSites($dbForProject, $region);
@@ -379,125 +394,103 @@ class StatsResources extends Action
protected function countForFunctions(Database $dbForProject, string $region)
{
$results = Promise::map([
'deploymentsStorage' => fn () => $dbForProject->sum('deployments', 'sourceSize', [
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS])
]),
'buildsStorage' => fn () => $dbForProject->sum('deployments', 'buildSize', [
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS])
]),
'deployments' => fn () => $dbForProject->count('deployments', [
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS])
]),
])->await();
$deploymentsStorage = $dbForProject->sum('deployments', 'sourceSize', [
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);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_FUNCTIONS, METRIC_RESOURCE_TYPE_DEPLOYMENTS_STORAGE), $results['deploymentsStorage']);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_FUNCTIONS, METRIC_RESOURCE_TYPE_BUILDS_STORAGE), $results['buildsStorage']);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_FUNCTIONS, METRIC_RESOURCE_TYPE_DEPLOYMENTS), $results['deployments']);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_FUNCTIONS, METRIC_RESOURCE_TYPE_BUILDS), $results['deployments']);
$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);
// Count runtimes
$runtimes = [];
$this->foreachDocument($dbForProject, 'functions', [], function (Document $function) use ($dbForProject, $region, &$runtimes) {
$functionDeploymentsStorage = $dbForProject->sum('deployments', 'sourceSize', [
Query::equal('resourceInternalId', [$function->getSequence()]),
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]),
]);
$this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), $functionDeploymentsStorage);
$functionResults = Promise::map([
'deploymentsStorage' => fn () => $dbForProject->sum('deployments', 'sourceSize', [
Query::equal('resourceInternalId', [$function->getSequence()]),
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]),
]),
'deployments' => fn () => $dbForProject->count('deployments', [
Query::equal('resourceInternalId', [$function->getSequence()]),
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]),
]),
'buildsStorage' => fn () => $dbForProject->sum('deployments', 'buildSize', [
Query::equal('resourceInternalId', [$function->getSequence()]),
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]),
]),
])->await();
$functionDeployments = $dbForProject->count('deployments', [
Query::equal('resourceInternalId', [$function->getSequence()]),
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]),
]);
$this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $functionDeployments);
$this->createStatsDocuments($region, str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), $functionResults['deploymentsStorage']);
$this->createStatsDocuments($region, str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $functionResults['deployments']);
$this->createStatsDocuments($region, str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS), $functionResults['deployments']);
$this->createStatsDocuments($region, str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), $functionResults['buildsStorage']);
/**
* 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_FUNCTIONS,$function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS), $functionDeployments);
$functionBuildsStorage = 0;
$this->foreachDocument($dbForProject, 'deployments', [
Query::equal('resourceInternalId', [$function->getSequence()]),
Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]),
], function (Document $deployment) use (&$functionBuildsStorage): void {
$functionBuildsStorage += $deployment->getAttribute('buildSize', 0);
});
$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;
}
});
// Write runtimes counts
foreach ($runtimes as $runtime => $count) {
$this->createStatsDocuments($region, str_replace('{runtime}', $runtime, METRIC_FUNCTIONS_RUNTIME), $count);
}
}
protected function countForSites(Database $dbForProject, string $region)
{
$results = Promise::map([
'deploymentsStorage' => fn () => $dbForProject->sum('deployments', 'sourceSize', [
Query::equal('resourceType', [RESOURCE_TYPE_SITES])
]),
'buildsStorage' => fn () => $dbForProject->sum('deployments', 'buildSize', [
Query::equal('resourceType', [RESOURCE_TYPE_SITES])
]),
'deployments' => fn () => $dbForProject->count('deployments', [
Query::equal('resourceType', [RESOURCE_TYPE_SITES])
]),
])->await();
$deploymentsStorage = $dbForProject->sum('deployments', 'sourceSize', [
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);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_DEPLOYMENTS_STORAGE), $results['deploymentsStorage']);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_BUILDS_STORAGE), $results['buildsStorage']);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_DEPLOYMENTS), $results['deployments']);
$this->createStatsDocuments($region, str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_BUILDS), $results['deployments']);
$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 = [];
$this->foreachDocument($dbForProject, 'sites', [], function (Document $site) use ($dbForProject, $region, &$frameworks) {
$siteDeploymentsStorage = $dbForProject->sum('deployments', 'sourceSize', [
Query::equal('resourceInternalId', [$site->getSequence()]),
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);
$siteResults = Promise::map([
'deploymentsStorage' => fn () => $dbForProject->sum('deployments', 'sourceSize', [
Query::equal('resourceInternalId', [$site->getSequence()]),
Query::equal('resourceType', [RESOURCE_TYPE_SITES]),
]),
'deployments' => fn () => $dbForProject->count('deployments', [
Query::equal('resourceInternalId', [$site->getSequence()]),
Query::equal('resourceType', [RESOURCE_TYPE_SITES]),
]),
'buildsStorage' => fn () => $dbForProject->sum('deployments', 'buildSize', [
Query::equal('resourceInternalId', [$site->getSequence()]),
Query::equal('resourceType', [RESOURCE_TYPE_SITES]),
]),
])->await();
$siteDeployments = $dbForProject->count('deployments', [
Query::equal('resourceInternalId', [$site->getSequence()]),
Query::equal('resourceType', [RESOURCE_TYPE_SITES]),
]);
$this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $siteDeployments);
$this->createStatsDocuments($region, str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), $siteResults['deploymentsStorage']);
$this->createStatsDocuments($region, str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $siteResults['deployments']);
$this->createStatsDocuments($region, str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS), $siteResults['deployments']);
$this->createStatsDocuments($region, str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), $siteResults['buildsStorage']);
/**
* 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);
$siteBuildsStorage = $dbForProject->sum('deployments', 'buildSize', [
Query::equal('resourceInternalId', [$site->getSequence()]),
Query::equal('resourceType', [RESOURCE_TYPE_SITES]),
]);
$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;
}
});
// Write frameworks counts
foreach ($frameworks as $framework => $count) {
$this->createStatsDocuments($region, str_replace('{framework}', $framework, METRIC_SITES_FRAMEWORK), $count);
}
@@ -4057,7 +4057,7 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::greaterThan('$createdAt', '1976-06-12')->toString(),
Query::createdAfter('1976-06-12')->toString(),
],
]);
@@ -4068,7 +4068,7 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::lessThan('$createdAt', '1976-06-12')->toString(),
Query::createdBefore('1976-06-12')->toString(),
],
]);