mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch 'feat-db-pools-master' into feat-db-pools-sync
This commit is contained in:
@@ -51,6 +51,7 @@ class Exception extends \Exception
|
||||
public const GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found';
|
||||
public const GENERAL_SERVER_ERROR = 'general_server_error';
|
||||
public const GENERAL_PROTOCOL_UNSUPPORTED = 'general_protocol_unsupported';
|
||||
public const GENERAL_CODES_DISABLED = 'general_codes_disabled';
|
||||
|
||||
/** Users */
|
||||
public const USER_COUNT_EXCEEDED = 'user_count_exceeded';
|
||||
@@ -61,7 +62,7 @@ class Exception extends \Exception
|
||||
public const USER_PASSWORD_RESET_REQUIRED = 'user_password_reset_required';
|
||||
public const USER_EMAIL_NOT_WHITELISTED = 'user_email_not_whitelisted';
|
||||
public const USER_IP_NOT_WHITELISTED = 'user_ip_not_whitelisted';
|
||||
public const USER_CODE_INVALID = 'user_code_invalid';
|
||||
public const USER_INVALID_CODE = 'user_invalid_code';
|
||||
public const USER_INVALID_CREDENTIALS = 'user_invalid_credentials';
|
||||
public const USER_ANONYMOUS_CONSOLE_PROHIBITED = 'user_anonymous_console_prohibited';
|
||||
public const USER_SESSION_ALREADY_EXISTS = 'user_session_already_exists';
|
||||
@@ -184,6 +185,7 @@ class Exception extends \Exception
|
||||
public const GRAPHQL_TOO_MANY_QUERIES = 'graphql_too_many_queries';
|
||||
|
||||
protected $type = '';
|
||||
protected $errors = [];
|
||||
|
||||
public function __construct(string $type = Exception::GENERAL_UNKNOWN, string $message = null, int $code = null, \Throwable $previous = null)
|
||||
{
|
||||
|
||||
@@ -80,17 +80,8 @@ class V17 extends Migration
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'mimeType' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
break;
|
||||
case 'builds':
|
||||
try {
|
||||
/**
|
||||
* Create 'size' attribute
|
||||
*/
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'size');
|
||||
$this->projectDB->deleteCachedCollection($id);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'size' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,8 @@ use Appwrite\Platform\Tasks\PatchCreateMissingSchedules;
|
||||
use Appwrite\Platform\Tasks\SDKs;
|
||||
use Appwrite\Platform\Tasks\Specs;
|
||||
use Appwrite\Platform\Tasks\SSL;
|
||||
use Appwrite\Platform\Tasks\Hamster;
|
||||
use Appwrite\Platform\Tasks\Usage;
|
||||
use Appwrite\Platform\Tasks\Vars;
|
||||
use Appwrite\Platform\Tasks\Version;
|
||||
use Appwrite\Platform\Tasks\VolumeSync;
|
||||
@@ -25,6 +27,7 @@ class Tasks extends Service
|
||||
->addAction(Version::getName(), new Version())
|
||||
->addAction(Vars::getName(), new Vars())
|
||||
->addAction(SSL::getName(), new SSL())
|
||||
->addAction(Hamster::getName(), new Hamster())
|
||||
->addAction(Doctor::getName(), new Doctor())
|
||||
->addAction(Install::getName(), new Install())
|
||||
->addAction(Maintenance::getName(), new Maintenance())
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Exception;
|
||||
use Utopia\App;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use League\Csv\Writer;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Registry\Registry;
|
||||
|
||||
class Hamster extends Action
|
||||
{
|
||||
private array $columns = [
|
||||
'Project ID',
|
||||
'Project Name',
|
||||
'Functions',
|
||||
'Deployments',
|
||||
'Members',
|
||||
'Domains',
|
||||
'Files',
|
||||
'Buckets',
|
||||
'Databases',
|
||||
'Documents',
|
||||
'Collections',
|
||||
'Storage',
|
||||
'Requests',
|
||||
'Bandwidth',
|
||||
'Users',
|
||||
'Sessions',
|
||||
'Executions'
|
||||
];
|
||||
|
||||
private array $usageStats = [
|
||||
'Files' => 'files.$all.count.total',
|
||||
'Buckets' => 'buckets.$all.count.total',
|
||||
'Databases' => 'databases.$all.count.total',
|
||||
'Documents' => 'documents.$all.count.total',
|
||||
'Collections' => 'collections.$all.count.total',
|
||||
'Storage' => 'project.$all.storage.size',
|
||||
'Requests' => 'project.$all.network.requests',
|
||||
'Bandwidth' => 'project.$all.network.bandwidth',
|
||||
'Users' => 'users.$all.count.total',
|
||||
'Sessions' => 'sessions.$all.requests.create',
|
||||
'Executions' => 'executions.$all.compute.total',
|
||||
];
|
||||
|
||||
protected string $directory = '/usr/local';
|
||||
protected string $path;
|
||||
|
||||
protected string $date;
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'hamster';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->desc('Get stats for projects')
|
||||
->inject('register')
|
||||
->inject('pools')
|
||||
->inject('cache')
|
||||
->inject('dbForConsole')
|
||||
->callback(function (Registry $register, Group $pools, Cache $cache, Database $dbForConsole) {
|
||||
$this->action($register, $pools, $cache, $dbForConsole);
|
||||
});
|
||||
}
|
||||
|
||||
private function getStats(Database $dbForConsole, Database $dbForProject, Document $project): array
|
||||
{
|
||||
$stats = [];
|
||||
|
||||
/** Get Project ID */
|
||||
$stats['Project ID'] = $project->getId();
|
||||
|
||||
/** Get Project Name */
|
||||
$stats['Project Name'] = $project->getAttribute('name');
|
||||
|
||||
/** Get Total Functions */
|
||||
$stats['Functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT);
|
||||
|
||||
/** Get Total Deployments */
|
||||
$stats['Deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT);
|
||||
|
||||
/** Get Total Members */
|
||||
$teamInternalId = $project->getAttribute('teamInternalId', null);
|
||||
if ($teamInternalId) {
|
||||
$stats['Members'] = $dbForConsole->count('memberships', [
|
||||
Query::equal('teamInternalId', [$teamInternalId])
|
||||
], APP_LIMIT_COUNT);
|
||||
} else {
|
||||
$stats['Members'] = 0;
|
||||
}
|
||||
|
||||
/** Get Domains */
|
||||
$stats['Domains'] = $dbForProject->count('domains', [], APP_LIMIT_COUNT);
|
||||
|
||||
/** Get Usage stats */
|
||||
$range = '90d';
|
||||
$periods = [
|
||||
'90d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
];
|
||||
|
||||
$metrics = array_values($this->usageStats);
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
// Calculate aggregate of each metric
|
||||
$stats[$metric] = array_sum(array_column($stats[$metric], 'value'));
|
||||
}
|
||||
});
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
public function action(Registry $register, Group $pools, Cache $cache, Database $dbForConsole): void
|
||||
{
|
||||
|
||||
Console::title('Cloud Hamster V1');
|
||||
Console::success(APP_NAME . ' cloud hamster process v1 has started');
|
||||
|
||||
$interval = (int) App::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default)
|
||||
|
||||
Console::loop(function () use ($register, $pools, $cache, $dbForConsole, $interval) {
|
||||
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Getting Cloud Usage Stats every {$interval} seconds");
|
||||
$loopStart = microtime(true);
|
||||
|
||||
/* Initialise new Utopia app */
|
||||
$app = new App('UTC');
|
||||
$console = $app->getResource('console');
|
||||
|
||||
/** CSV stuff */
|
||||
$this->date = date('Y-m-d');
|
||||
$this->path = "{$this->directory}/stats_{$this->date}.csv";
|
||||
$csv = Writer::createFromPath($this->path, 'w');
|
||||
$csv->insertOne($this->columns);
|
||||
|
||||
/** Database connections */
|
||||
$totalProjects = $dbForConsole->count('projects') + 1;
|
||||
Console::success("Found a total of: {$totalProjects} projects");
|
||||
|
||||
$projects = [$console];
|
||||
$count = 0;
|
||||
$limit = 30;
|
||||
$sum = 30;
|
||||
$offset = 0;
|
||||
while (!empty($projects)) {
|
||||
foreach ($projects as $project) {
|
||||
/**
|
||||
* Skip user projects with id 'console'
|
||||
*/
|
||||
if ($project->getId() === 'console') {
|
||||
continue;
|
||||
}
|
||||
|
||||
Console::info("Getting stats for {$project->getId()}");
|
||||
|
||||
try {
|
||||
$db = $project->getAttribute('database');
|
||||
$adapter = $pools
|
||||
->get($db)
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$dbForProject = new Database($adapter, $cache);
|
||||
$dbForProject->setDefaultDatabase('appwrite');
|
||||
$dbForProject->setNamespace('_' . $project->getInternalId());
|
||||
|
||||
$statsPerProject = $this->getStats($dbForConsole, $dbForProject, $project);
|
||||
$csv->insertOne(array_values($statsPerProject));
|
||||
} catch (\Throwable $th) {
|
||||
throw $th;
|
||||
Console::error('Failed to update project ("' . $project->getId() . '") version with error: ' . $th->getMessage());
|
||||
} finally {
|
||||
$pools
|
||||
->get($db)
|
||||
->reclaim();
|
||||
}
|
||||
}
|
||||
|
||||
$sum = \count($projects);
|
||||
|
||||
$projects = $dbForConsole->find('projects', [
|
||||
Query::limit($limit),
|
||||
Query::offset($offset),
|
||||
]);
|
||||
|
||||
$offset = $offset + $limit;
|
||||
$count = $count + $sum;
|
||||
|
||||
Console::log('Iterated through ' . $count . '/' . $totalProjects . ' projects...');
|
||||
}
|
||||
|
||||
$this->sendEmail($register);
|
||||
|
||||
$pools
|
||||
->get('console')
|
||||
->reclaim();
|
||||
|
||||
$loopTook = microtime(true) - $loopStart;
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Cloud Stats took {$loopTook} seconds");
|
||||
}, $interval);
|
||||
}
|
||||
|
||||
private function sendEmail(Registry $register)
|
||||
{
|
||||
/** @var \PHPMailer\PHPMailer\PHPMailer $mail */
|
||||
$mail = $register->get('smtp');
|
||||
|
||||
$mail->clearAddresses();
|
||||
$mail->clearAllRecipients();
|
||||
$mail->clearReplyTos();
|
||||
$mail->clearAttachments();
|
||||
$mail->clearBCCs();
|
||||
$mail->clearCCs();
|
||||
|
||||
try {
|
||||
/** Addresses */
|
||||
$mail->setFrom(App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), 'Appwrite Cloud Hamster');
|
||||
$recipients = explode(',', App::getEnv('_APP_HAMSTER_RECIPIENTS', ''));
|
||||
foreach ($recipients as $recipient) {
|
||||
$mail->addAddress($recipient);
|
||||
}
|
||||
|
||||
/** Attachments */
|
||||
$mail->addAttachment($this->path);
|
||||
|
||||
/** Content */
|
||||
$mail->Subject = "Cloud Report for {$this->date}";
|
||||
$mail->Body = "Please find the daily cloud report atttached";
|
||||
|
||||
$mail->send();
|
||||
Console::success('Email has been sent!');
|
||||
} catch (Exception $e) {
|
||||
Console::error("Message could not be sent. Mailer Error: {$mail->ErrorInfo}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,18 +51,18 @@ class Build extends Model
|
||||
'default' => '',
|
||||
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||
])
|
||||
->addRule('endTime', [
|
||||
'type' => self::TYPE_DATETIME,
|
||||
'description' => 'The time the build was finished in ISO 8601 format.',
|
||||
'default' => '',
|
||||
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||
])
|
||||
->addRule('duration', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'The build duration in seconds.',
|
||||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('size', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'The code size in bytes.',
|
||||
'default' => 0,
|
||||
'example' => 128,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user