Merge pull request #7707 from appwrite/revert-7629-revert-7614-chore-usage-queue-health
Revert 7629 revert 7614 chore usage queue health
This commit is contained in:
commit
e3c1379263
2
.env
2
.env
|
@ -78,7 +78,7 @@ _APP_MAINTENANCE_RETENTION_CACHE=2592000
|
||||||
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
|
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
|
||||||
_APP_MAINTENANCE_RETENTION_ABUSE=86400
|
_APP_MAINTENANCE_RETENTION_ABUSE=86400
|
||||||
_APP_MAINTENANCE_RETENTION_AUDIT=1209600
|
_APP_MAINTENANCE_RETENTION_AUDIT=1209600
|
||||||
_APP_USAGE_AGGREGATION_INTERVAL=20
|
_APP_USAGE_AGGREGATION_INTERVAL=30
|
||||||
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
|
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
|
||||||
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
|
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
|
||||||
_APP_USAGE_STATS=enabled
|
_APP_USAGE_STATS=enabled
|
||||||
|
|
|
@ -34,6 +34,28 @@ $commonCollections = [
|
||||||
'array' => false,
|
'array' => false,
|
||||||
'filters' => [],
|
'filters' => [],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'$id' => 'resourceType',
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 255,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('mimeType'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 255, // https://tools.ietf.org/html/rfc4288#section-4.2
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'$id' => 'accessedAt',
|
'$id' => 'accessedAt',
|
||||||
'type' => Database::VAR_DATETIME,
|
'type' => Database::VAR_DATETIME,
|
||||||
|
@ -1327,10 +1349,10 @@ $commonCollections = [
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
'stats_v2' => [
|
'stats' => [
|
||||||
'$collection' => ID::custom(Database::METADATA),
|
'$collection' => ID::custom(Database::METADATA),
|
||||||
'$id' => ID::custom('stats_v2'),
|
'$id' => ID::custom('stats'),
|
||||||
'name' => 'stats_v2',
|
'name' => 'stats',
|
||||||
'attributes' => [
|
'attributes' => [
|
||||||
[
|
[
|
||||||
'$id' => ID::custom('metric'),
|
'$id' => ID::custom('metric'),
|
||||||
|
|
|
@ -1791,7 +1791,7 @@ App::get('/v1/account/logs')
|
||||||
}
|
}
|
||||||
|
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
'total' => $audit->countLogsByUser($user->getId()),
|
'total' => $audit->countLogsByUser($user->getInternalId()),
|
||||||
'logs' => $output,
|
'logs' => $output,
|
||||||
]), Response::MODEL_LOG_LIST);
|
]), Response::MODEL_LOG_LIST);
|
||||||
});
|
});
|
||||||
|
|
|
@ -76,7 +76,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($gitHubSession)) {
|
if (empty($gitHubSession)) {
|
||||||
throw new Exception(Exception::GENERAL_UNKNOWN, 'GitHub session not found.');
|
throw new Exception(Exception::USER_SESSION_NOT_FOUND, 'GitHub session not found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$provider = $gitHubSession->getAttribute('provider', '');
|
$provider = $gitHubSession->getAttribute('provider', '');
|
||||||
|
|
|
@ -3590,7 +3590,7 @@ App::get('/v1/databases/usage')
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||||
foreach ($metrics as $metric) {
|
foreach ($metrics as $metric) {
|
||||||
$result = $dbForProject->findOne('stats_v2', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', ['inf'])
|
Query::equal('period', ['inf'])
|
||||||
]);
|
]);
|
||||||
|
@ -3598,7 +3598,7 @@ App::get('/v1/databases/usage')
|
||||||
$stats[$metric]['total'] = $result['value'] ?? 0;
|
$stats[$metric]['total'] = $result['value'] ?? 0;
|
||||||
$limit = $days['limit'];
|
$limit = $days['limit'];
|
||||||
$period = $days['period'];
|
$period = $days['period'];
|
||||||
$results = $dbForProject->find('stats_v2', [
|
$results = $dbForProject->find('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', [$period]),
|
Query::equal('period', [$period]),
|
||||||
Query::limit($limit),
|
Query::limit($limit),
|
||||||
|
@ -3674,7 +3674,7 @@ App::get('/v1/databases/:databaseId/usage')
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||||
foreach ($metrics as $metric) {
|
foreach ($metrics as $metric) {
|
||||||
$result = $dbForProject->findOne('stats_v2', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', ['inf'])
|
Query::equal('period', ['inf'])
|
||||||
]);
|
]);
|
||||||
|
@ -3682,7 +3682,7 @@ App::get('/v1/databases/:databaseId/usage')
|
||||||
$stats[$metric]['total'] = $result['value'] ?? 0;
|
$stats[$metric]['total'] = $result['value'] ?? 0;
|
||||||
$limit = $days['limit'];
|
$limit = $days['limit'];
|
||||||
$period = $days['period'];
|
$period = $days['period'];
|
||||||
$results = $dbForProject->find('stats_v2', [
|
$results = $dbForProject->find('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', [$period]),
|
Query::equal('period', [$period]),
|
||||||
Query::limit($limit),
|
Query::limit($limit),
|
||||||
|
@ -3760,7 +3760,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||||
foreach ($metrics as $metric) {
|
foreach ($metrics as $metric) {
|
||||||
$result = $dbForProject->findOne('stats_v2', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', ['inf'])
|
Query::equal('period', ['inf'])
|
||||||
]);
|
]);
|
||||||
|
@ -3768,7 +3768,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
|
||||||
$stats[$metric]['total'] = $result['value'] ?? 0;
|
$stats[$metric]['total'] = $result['value'] ?? 0;
|
||||||
$limit = $days['limit'];
|
$limit = $days['limit'];
|
||||||
$period = $days['period'];
|
$period = $days['period'];
|
||||||
$results = $dbForProject->find('stats_v2', [
|
$results = $dbForProject->find('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', [$period]),
|
Query::equal('period', [$period]),
|
||||||
Query::limit($limit),
|
Query::limit($limit),
|
||||||
|
|
|
@ -484,7 +484,7 @@ App::get('/v1/functions/:functionId/usage')
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||||
foreach ($metrics as $metric) {
|
foreach ($metrics as $metric) {
|
||||||
$result = $dbForProject->findOne('stats_v2', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', ['inf'])
|
Query::equal('period', ['inf'])
|
||||||
]);
|
]);
|
||||||
|
@ -492,7 +492,7 @@ App::get('/v1/functions/:functionId/usage')
|
||||||
$stats[$metric]['total'] = $result['value'] ?? 0;
|
$stats[$metric]['total'] = $result['value'] ?? 0;
|
||||||
$limit = $days['limit'];
|
$limit = $days['limit'];
|
||||||
$period = $days['period'];
|
$period = $days['period'];
|
||||||
$results = $dbForProject->find('stats_v2', [
|
$results = $dbForProject->find('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', [$period]),
|
Query::equal('period', [$period]),
|
||||||
Query::limit($limit),
|
Query::limit($limit),
|
||||||
|
@ -576,7 +576,7 @@ App::get('/v1/functions/usage')
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||||
foreach ($metrics as $metric) {
|
foreach ($metrics as $metric) {
|
||||||
$result = $dbForProject->findOne('stats_v2', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', ['inf'])
|
Query::equal('period', ['inf'])
|
||||||
]);
|
]);
|
||||||
|
@ -584,7 +584,7 @@ App::get('/v1/functions/usage')
|
||||||
$stats[$metric]['total'] = $result['value'] ?? 0;
|
$stats[$metric]['total'] = $result['value'] ?? 0;
|
||||||
$limit = $days['limit'];
|
$limit = $days['limit'];
|
||||||
$period = $days['period'];
|
$period = $days['period'];
|
||||||
$results = $dbForProject->find('stats_v2', [
|
$results = $dbForProject->find('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', [$period]),
|
Query::equal('period', [$period]),
|
||||||
Query::limit($limit),
|
Query::limit($limit),
|
||||||
|
|
|
@ -659,6 +659,60 @@ App::get('/v1/health/queue/functions')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
}, ['response']);
|
}, ['response']);
|
||||||
|
|
||||||
|
App::get('/v1/health/queue/usage')
|
||||||
|
->desc('Get usage queue')
|
||||||
|
->groups(['api', 'health'])
|
||||||
|
->label('scope', 'health.read')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||||
|
->label('sdk.namespace', 'health')
|
||||||
|
->label('sdk.method', 'getQueueUsage')
|
||||||
|
->label('sdk.description', '/docs/references/health/get-queue-usage.md')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||||
|
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||||
|
->inject('queue')
|
||||||
|
->inject('response')
|
||||||
|
->action(function (int|string $threshold, Connection $queue, Response $response) {
|
||||||
|
$threshold = \intval($threshold);
|
||||||
|
|
||||||
|
$client = new Client(Event::USAGE_QUEUE_NAME, $queue);
|
||||||
|
$size = $client->getQueueSize();
|
||||||
|
|
||||||
|
if ($size >= $threshold) {
|
||||||
|
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
|
});
|
||||||
|
|
||||||
|
App::get('/v1/health/queue/usage-dump')
|
||||||
|
->desc('Get usage dump queue')
|
||||||
|
->groups(['api', 'health'])
|
||||||
|
->label('scope', 'health.read')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||||
|
->label('sdk.namespace', 'health')
|
||||||
|
->label('sdk.method', 'getQueueUsage')
|
||||||
|
->label('sdk.description', '/docs/references/health/get-queue-usage-dump.md')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||||
|
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||||
|
->inject('queue')
|
||||||
|
->inject('response')
|
||||||
|
->action(function (int|string $threshold, Connection $queue, Response $response) {
|
||||||
|
$threshold = \intval($threshold);
|
||||||
|
|
||||||
|
$client = new Client(Event::USAGE_DUMP_QUEUE_NAME, $queue);
|
||||||
|
$size = $client->getQueueSize();
|
||||||
|
|
||||||
|
if ($size >= $threshold) {
|
||||||
|
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
|
});
|
||||||
|
|
||||||
App::get('/v1/health/storage/local')
|
App::get('/v1/health/storage/local')
|
||||||
->desc('Get local storage')
|
->desc('Get local storage')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
|
@ -796,6 +850,7 @@ App::get('/v1/health/queue/failed/:name')
|
||||||
Event::MAILS_QUEUE_NAME,
|
Event::MAILS_QUEUE_NAME,
|
||||||
Event::FUNCTIONS_QUEUE_NAME,
|
Event::FUNCTIONS_QUEUE_NAME,
|
||||||
Event::USAGE_QUEUE_NAME,
|
Event::USAGE_QUEUE_NAME,
|
||||||
|
Event::USAGE_DUMP_QUEUE_NAME,
|
||||||
Event::WEBHOOK_CLASS_NAME,
|
Event::WEBHOOK_CLASS_NAME,
|
||||||
Event::CERTIFICATES_QUEUE_NAME,
|
Event::CERTIFICATES_QUEUE_NAME,
|
||||||
Event::BUILDS_QUEUE_NAME,
|
Event::BUILDS_QUEUE_NAME,
|
||||||
|
|
|
@ -73,7 +73,7 @@ App::get('/v1/project/usage')
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, &$total, &$stats) {
|
Authorization::skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, &$total, &$stats) {
|
||||||
foreach ($metrics['total'] as $metric) {
|
foreach ($metrics['total'] as $metric) {
|
||||||
$result = $dbForProject->findOne('stats_v2', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', ['inf'])
|
Query::equal('period', ['inf'])
|
||||||
]);
|
]);
|
||||||
|
@ -81,7 +81,7 @@ App::get('/v1/project/usage')
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($metrics['period'] as $metric) {
|
foreach ($metrics['period'] as $metric) {
|
||||||
$results = $dbForProject->find('stats_v2', [
|
$results = $dbForProject->find('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', [$period]),
|
Query::equal('period', [$period]),
|
||||||
Query::greaterThanEqual('time', $firstDay),
|
Query::greaterThanEqual('time', $firstDay),
|
||||||
|
@ -116,7 +116,7 @@ App::get('/v1/project/usage')
|
||||||
$id = $function->getId();
|
$id = $function->getId();
|
||||||
$name = $function->getAttribute('name');
|
$name = $function->getAttribute('name');
|
||||||
$metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS);
|
$metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS);
|
||||||
$value = $dbForProject->findOne('stats_v2', [
|
$value = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', ['inf'])
|
Query::equal('period', ['inf'])
|
||||||
]);
|
]);
|
||||||
|
@ -132,7 +132,7 @@ App::get('/v1/project/usage')
|
||||||
$id = $bucket->getId();
|
$id = $bucket->getId();
|
||||||
$name = $bucket->getAttribute('name');
|
$name = $bucket->getAttribute('name');
|
||||||
$metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE);
|
$metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE);
|
||||||
$value = $dbForProject->findOne('stats_v2', [
|
$value = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', ['inf'])
|
Query::equal('period', ['inf'])
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -1467,6 +1467,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
if ($deviceDeleted) {
|
if ($deviceDeleted) {
|
||||||
$queueForDeletes
|
$queueForDeletes
|
||||||
->setType(DELETE_TYPE_CACHE_BY_RESOURCE)
|
->setType(DELETE_TYPE_CACHE_BY_RESOURCE)
|
||||||
|
->setResourceType('bucket/' . $bucket->getId())
|
||||||
->setResource('file/' . $fileId)
|
->setResource('file/' . $fileId)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -1524,7 +1525,7 @@ App::get('/v1/storage/usage')
|
||||||
$total = [];
|
$total = [];
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
|
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
|
||||||
foreach ($metrics as $metric) {
|
foreach ($metrics as $metric) {
|
||||||
$result = $dbForProject->findOne('stats_v2', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', ['inf'])
|
Query::equal('period', ['inf'])
|
||||||
]);
|
]);
|
||||||
|
@ -1532,7 +1533,7 @@ App::get('/v1/storage/usage')
|
||||||
$stats[$metric]['total'] = $result['value'] ?? 0;
|
$stats[$metric]['total'] = $result['value'] ?? 0;
|
||||||
$limit = $days['limit'];
|
$limit = $days['limit'];
|
||||||
$period = $days['period'];
|
$period = $days['period'];
|
||||||
$results = $dbForProject->find('stats_v2', [
|
$results = $dbForProject->find('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', [$period]),
|
Query::equal('period', [$period]),
|
||||||
Query::limit($limit),
|
Query::limit($limit),
|
||||||
|
@ -1609,7 +1610,7 @@ App::get('/v1/storage/:bucketId/usage')
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
|
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
|
||||||
foreach ($metrics as $metric) {
|
foreach ($metrics as $metric) {
|
||||||
$result = $dbForProject->findOne('stats_v2', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', ['inf'])
|
Query::equal('period', ['inf'])
|
||||||
]);
|
]);
|
||||||
|
@ -1617,7 +1618,7 @@ App::get('/v1/storage/:bucketId/usage')
|
||||||
$stats[$metric]['total'] = $result['value'] ?? 0;
|
$stats[$metric]['total'] = $result['value'] ?? 0;
|
||||||
$limit = $days['limit'];
|
$limit = $days['limit'];
|
||||||
$period = $days['period'];
|
$period = $days['period'];
|
||||||
$results = $dbForProject->find('stats_v2', [
|
$results = $dbForProject->find('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', [$period]),
|
Query::equal('period', [$period]),
|
||||||
Query::limit($limit),
|
Query::limit($limit),
|
||||||
|
|
|
@ -615,6 +615,9 @@ App::get('/v1/users/:userId/logs')
|
||||||
|
|
||||||
$output[$i] = new Document([
|
$output[$i] = new Document([
|
||||||
'event' => $log['event'],
|
'event' => $log['event'],
|
||||||
|
'userId' => ID::custom($log['data']['userId']),
|
||||||
|
'userEmail' => $log['data']['userEmail'] ?? null,
|
||||||
|
'userName' => $log['data']['userName'] ?? null,
|
||||||
'ip' => $log['ip'],
|
'ip' => $log['ip'],
|
||||||
'time' => $log['time'],
|
'time' => $log['time'],
|
||||||
'osCode' => $os['osCode'],
|
'osCode' => $os['osCode'],
|
||||||
|
@ -643,7 +646,7 @@ App::get('/v1/users/:userId/logs')
|
||||||
}
|
}
|
||||||
|
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
'total' => $audit->countLogsByUser($user->getId()),
|
'total' => $audit->countLogsByUser($user->getInternalId()),
|
||||||
'logs' => $output,
|
'logs' => $output,
|
||||||
]), Response::MODEL_LOG_LIST);
|
]), Response::MODEL_LOG_LIST);
|
||||||
});
|
});
|
||||||
|
@ -1263,7 +1266,7 @@ App::get('/v1/users/usage')
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||||
foreach ($metrics as $count => $metric) {
|
foreach ($metrics as $count => $metric) {
|
||||||
$result = $dbForProject->findOne('stats_v2', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', ['inf'])
|
Query::equal('period', ['inf'])
|
||||||
]);
|
]);
|
||||||
|
@ -1271,7 +1274,7 @@ App::get('/v1/users/usage')
|
||||||
$stats[$metric]['total'] = $result['value'] ?? 0;
|
$stats[$metric]['total'] = $result['value'] ?? 0;
|
||||||
$limit = $days['limit'];
|
$limit = $days['limit'];
|
||||||
$period = $days['period'];
|
$period = $days['period'];
|
||||||
$results = $dbForProject->find('stats_v2', [
|
$results = $dbForProject->find('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', [$period]),
|
Query::equal('period', [$period]),
|
||||||
Query::limit($limit),
|
Query::limit($limit),
|
||||||
|
|
|
@ -252,24 +252,22 @@ App::init()
|
||||||
;
|
;
|
||||||
|
|
||||||
$useCache = $route->getLabel('cache', false);
|
$useCache = $route->getLabel('cache', false);
|
||||||
|
|
||||||
if ($useCache) {
|
if ($useCache) {
|
||||||
$key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
$key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
||||||
|
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||||
$cache = new Cache(
|
$cache = new Cache(
|
||||||
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
||||||
);
|
);
|
||||||
$timestamp = 60 * 60 * 24 * 30;
|
$timestamp = 60 * 60 * 24 * 30;
|
||||||
$data = $cache->load($key, $timestamp);
|
$data = $cache->load($key, $timestamp);
|
||||||
|
|
||||||
if (!empty($data)) {
|
if (!empty($data) && !$cacheLog->isEmpty()) {
|
||||||
$data = json_decode($data, true);
|
$parts = explode('/', $cacheLog->getAttribute('resourceType'));
|
||||||
$parts = explode('/', $data['resourceType']);
|
|
||||||
$type = $parts[0] ?? null;
|
$type = $parts[0] ?? null;
|
||||||
|
|
||||||
if ($type === 'bucket') {
|
if ($type === 'bucket') {
|
||||||
$bucketId = $parts[1] ?? null;
|
$bucketId = $parts[1] ?? null;
|
||||||
|
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||||
|
@ -281,11 +279,12 @@ App::init()
|
||||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||||
$validator = new Authorization(Database::PERMISSION_READ);
|
$validator = new Authorization(Database::PERMISSION_READ);
|
||||||
$valid = $validator->isValid($bucket->getRead());
|
$valid = $validator->isValid($bucket->getRead());
|
||||||
|
|
||||||
if (!$fileSecurity && !$valid) {
|
if (!$fileSecurity && !$valid) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
$parts = explode('/', $data['resource']);
|
$parts = explode('/', $cacheLog->getAttribute('resource'));
|
||||||
$fileId = $parts[1] ?? null;
|
$fileId = $parts[1] ?? null;
|
||||||
|
|
||||||
if ($fileSecurity && !$valid) {
|
if ($fileSecurity && !$valid) {
|
||||||
|
@ -302,8 +301,8 @@ App::init()
|
||||||
$response
|
$response
|
||||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $timestamp) . ' GMT')
|
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $timestamp) . ' GMT')
|
||||||
->addHeader('X-Appwrite-Cache', 'hit')
|
->addHeader('X-Appwrite-Cache', 'hit')
|
||||||
->setContentType($data['contentType'])
|
->setContentType($cacheLog->getAttribute('mimeType'))
|
||||||
->send(base64_decode($data['payload']))
|
->send($data)
|
||||||
;
|
;
|
||||||
} else {
|
} else {
|
||||||
$response->addHeader('X-Appwrite-Cache', 'miss');
|
$response->addHeader('X-Appwrite-Cache', 'miss');
|
||||||
|
@ -534,7 +533,6 @@ App::shutdown()
|
||||||
if ($useCache) {
|
if ($useCache) {
|
||||||
$resource = $resourceType = null;
|
$resource = $resourceType = null;
|
||||||
$data = $response->getPayload();
|
$data = $response->getPayload();
|
||||||
|
|
||||||
if (!empty($data['payload'])) {
|
if (!empty($data['payload'])) {
|
||||||
$pattern = $route->getLabel('cache.resource', null);
|
$pattern = $route->getLabel('cache.resource', null);
|
||||||
if (!empty($pattern)) {
|
if (!empty($pattern)) {
|
||||||
|
@ -546,22 +544,17 @@ App::shutdown()
|
||||||
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
|
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
|
||||||
}
|
}
|
||||||
|
|
||||||
$key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
$key = md5($request->getURI() . '*' . implode('*', $request->getParams())) . '*' . APP_CACHE_BUSTER;
|
||||||
$data = json_encode([
|
$signature = md5($data['payload']);
|
||||||
'resourceType' => $resourceType,
|
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||||
'resource' => $resource,
|
|
||||||
'contentType' => $response->getContentType(),
|
|
||||||
'payload' => base64_encode($data['payload']),
|
|
||||||
]) ;
|
|
||||||
|
|
||||||
$signature = md5($data);
|
|
||||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
|
||||||
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
|
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
|
||||||
$now = DateTime::now();
|
$now = DateTime::now();
|
||||||
if ($cacheLog->isEmpty()) {
|
if ($cacheLog->isEmpty()) {
|
||||||
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
|
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
|
||||||
'$id' => $key,
|
'$id' => $key,
|
||||||
'resource' => $resource,
|
'resource' => $resource,
|
||||||
|
'resourceType' => $resourceType,
|
||||||
|
'mimeType' => $response->getContentType(),
|
||||||
'accessedAt' => $now,
|
'accessedAt' => $now,
|
||||||
'signature' => $signature,
|
'signature' => $signature,
|
||||||
])));
|
])));
|
||||||
|
@ -574,7 +567,7 @@ App::shutdown()
|
||||||
$cache = new Cache(
|
$cache = new Cache(
|
||||||
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
||||||
);
|
);
|
||||||
$cache->save($key, $data);
|
$cache->save($key, $data['payload']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -927,6 +927,7 @@ services:
|
||||||
# - appwrite
|
# - appwrite
|
||||||
# volumes:
|
# volumes:
|
||||||
# - appwrite-uploads:/storage/uploads
|
# - appwrite-uploads:/storage/uploads
|
||||||
|
|
||||||
# Dev Tools Start ------------------------------------------------------------------------------------------
|
# Dev Tools Start ------------------------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack
|
# The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack
|
||||||
|
|
1
docs/references/health/get-queue-usage-dump.md
Normal file
1
docs/references/health/get-queue-usage-dump.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Get the number of projects containing metrics that are waiting to be processed in the Appwrite internal queue server.
|
1
docs/references/health/get-queue-usage.md
Normal file
1
docs/references/health/get-queue-usage.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.
|
|
@ -10,6 +10,7 @@ class Delete extends Event
|
||||||
{
|
{
|
||||||
protected string $type = '';
|
protected string $type = '';
|
||||||
protected ?Document $document = null;
|
protected ?Document $document = null;
|
||||||
|
protected ?string $resourceType = null;
|
||||||
protected ?string $resource = null;
|
protected ?string $resource = null;
|
||||||
protected ?string $datetime = null;
|
protected ?string $datetime = null;
|
||||||
protected ?string $hourlyUsageRetentionDatetime = null;
|
protected ?string $hourlyUsageRetentionDatetime = null;
|
||||||
|
@ -107,6 +108,19 @@ class Delete extends Event
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the resource type for the delete event.
|
||||||
|
*
|
||||||
|
* @param string $resourceType
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setResourceType(string $resourceType): self
|
||||||
|
{
|
||||||
|
$this->resourceType = $resourceType;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the set document for the delete event.
|
* Returns the set document for the delete event.
|
||||||
*
|
*
|
||||||
|
@ -133,6 +147,7 @@ class Delete extends Event
|
||||||
'type' => $this->type,
|
'type' => $this->type,
|
||||||
'document' => $this->document,
|
'document' => $this->document,
|
||||||
'resource' => $this->resource,
|
'resource' => $this->resource,
|
||||||
|
'resourceType' => $this->resourceType,
|
||||||
'datetime' => $this->datetime,
|
'datetime' => $this->datetime,
|
||||||
'hourlyUsageRetentionDatetime' => $this->hourlyUsageRetentionDatetime
|
'hourlyUsageRetentionDatetime' => $this->hourlyUsageRetentionDatetime
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -270,7 +270,7 @@ class CalcTierStats extends Action
|
||||||
$limit = $periods[$range]['limit'];
|
$limit = $periods[$range]['limit'];
|
||||||
$period = $periods[$range]['period'];
|
$period = $periods[$range]['period'];
|
||||||
|
|
||||||
$requestDocs = $dbForProject->find('stats_v2', [
|
$requestDocs = $dbForProject->find('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', [$period]),
|
Query::equal('period', [$period]),
|
||||||
Query::limit($limit),
|
Query::limit($limit),
|
||||||
|
|
|
@ -167,8 +167,8 @@ class CreateInfMetric extends Action
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$id = \md5("_inf_{$metric}");
|
$id = \md5("_inf_{$metric}");
|
||||||
$dbForProject->deleteDocument('stats_v2', $id);
|
$dbForProject->deleteDocument('stats', $id);
|
||||||
$dbForProject->createDocument('stats_v2', new Document([
|
$dbForProject->createDocument('stats', new Document([
|
||||||
'$id' => $id,
|
'$id' => $id,
|
||||||
'metric' => $metric,
|
'metric' => $metric,
|
||||||
'period' => 'inf',
|
'period' => 'inf',
|
||||||
|
@ -190,7 +190,7 @@ class CreateInfMetric extends Action
|
||||||
protected function getFromMetric(database $dbForProject, string $metric): int|float
|
protected function getFromMetric(database $dbForProject, string $metric): int|float
|
||||||
{
|
{
|
||||||
|
|
||||||
return $dbForProject->sum('stats_v2', 'value', [
|
return $dbForProject->sum('stats', 'value', [
|
||||||
Query::equal('metric', [
|
Query::equal('metric', [
|
||||||
$metric,
|
$metric,
|
||||||
]),
|
]),
|
||||||
|
|
|
@ -68,18 +68,13 @@ class Deletes extends Action
|
||||||
$datetime = $payload['datetime'] ?? null;
|
$datetime = $payload['datetime'] ?? null;
|
||||||
$hourlyUsageRetentionDatetime = $payload['hourlyUsageRetentionDatetime'] ?? null;
|
$hourlyUsageRetentionDatetime = $payload['hourlyUsageRetentionDatetime'] ?? null;
|
||||||
$resource = $payload['resource'] ?? null;
|
$resource = $payload['resource'] ?? null;
|
||||||
|
$resourceType = $payload['resourceType'] ?? null;
|
||||||
$document = new Document($payload['document'] ?? []);
|
$document = new Document($payload['document'] ?? []);
|
||||||
$project = new Document($payload['project'] ?? []);
|
$project = new Document($payload['project'] ?? []);
|
||||||
|
|
||||||
switch (strval($type)) {
|
switch (strval($type)) {
|
||||||
case DELETE_TYPE_DOCUMENT:
|
case DELETE_TYPE_DOCUMENT:
|
||||||
switch ($document->getCollection()) {
|
switch ($document->getCollection()) {
|
||||||
case DELETE_TYPE_DATABASES:
|
|
||||||
$this->deleteDatabase($getProjectDB, $document, $project);
|
|
||||||
break;
|
|
||||||
case DELETE_TYPE_COLLECTIONS:
|
|
||||||
$this->deleteCollection($getProjectDB, $document, $project);
|
|
||||||
break;
|
|
||||||
case DELETE_TYPE_PROJECTS:
|
case DELETE_TYPE_PROJECTS:
|
||||||
$this->deleteProject($dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice, $document);
|
$this->deleteProject($dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice, $document);
|
||||||
break;
|
break;
|
||||||
|
@ -108,10 +103,6 @@ class Deletes extends Action
|
||||||
$this->deleteRule($dbForConsole, $document);
|
$this->deleteRule($dbForConsole, $document);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (\str_starts_with($document->getCollection(), 'database_')) {
|
|
||||||
$this->deleteCollection($getProjectDB, $document, $project);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Console::error('No lazy delete operation available for document of type: ' . $document->getCollection());
|
Console::error('No lazy delete operation available for document of type: ' . $document->getCollection());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -145,7 +136,7 @@ class Deletes extends Action
|
||||||
$this->deleteUsageStats($project, $getProjectDB, $hourlyUsageRetentionDatetime);
|
$this->deleteUsageStats($project, $getProjectDB, $hourlyUsageRetentionDatetime);
|
||||||
break;
|
break;
|
||||||
case DELETE_TYPE_CACHE_BY_RESOURCE:
|
case DELETE_TYPE_CACHE_BY_RESOURCE:
|
||||||
$this->deleteCacheByResource($project, $getProjectDB, $resource);
|
$this->deleteCacheByResource($project, $getProjectDB, $resource, $resourceType);
|
||||||
break;
|
break;
|
||||||
case DELETE_TYPE_CACHE_BY_TIMESTAMP:
|
case DELETE_TYPE_CACHE_BY_TIMESTAMP:
|
||||||
$this->deleteCacheByDate($project, $getProjectDB, $datetime);
|
$this->deleteCacheByDate($project, $getProjectDB, $datetime);
|
||||||
|
@ -203,32 +194,37 @@ class Deletes extends Action
|
||||||
* @param string $resource
|
* @param string $resource
|
||||||
* @return void
|
* @return void
|
||||||
* @throws Authorization
|
* @throws Authorization
|
||||||
|
* @param string|null $resourceType
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private function deleteCacheByResource(Document $project, callable $getProjectDB, string $resource): void
|
private function deleteCacheByResource(Document $project, callable $getProjectDB, string $resource, string $resourceType = null): void
|
||||||
{
|
{
|
||||||
$projectId = $project->getId();
|
$projectId = $project->getId();
|
||||||
$dbForProject = $getProjectDB($project);
|
$dbForProject = $getProjectDB($project);
|
||||||
$document = $dbForProject->findOne('cache', [Query::equal('resource', [$resource])]);
|
|
||||||
|
|
||||||
if ($document) {
|
$cache = new Cache(
|
||||||
$cache = new Cache(
|
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId)
|
||||||
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId)
|
);
|
||||||
);
|
|
||||||
|
|
||||||
$this->deleteById(
|
$query[] = Query::equal('resource', [$resource]);
|
||||||
$document,
|
if (!empty($resourceType)) {
|
||||||
$dbForProject,
|
$query[] = Query::equal('resourceType', [$resourceType]);
|
||||||
function ($document) use ($cache, $projectId) {
|
|
||||||
$path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId();
|
|
||||||
|
|
||||||
if ($cache->purge($document->getId())) {
|
|
||||||
Console::success('Deleting cache file: ' . $path);
|
|
||||||
} else {
|
|
||||||
Console::error('Failed to delete cache file: ' . $path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->deleteByGroup(
|
||||||
|
'cache',
|
||||||
|
$query,
|
||||||
|
$dbForProject,
|
||||||
|
function (Document $document) use ($cache, $projectId) {
|
||||||
|
$path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId();
|
||||||
|
|
||||||
|
if ($cache->purge($document->getId())) {
|
||||||
|
Console::success('Deleting cache file: ' . $path);
|
||||||
|
} else {
|
||||||
|
Console::error('Failed to delete cache file: ' . $path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -268,72 +264,6 @@ class Deletes extends Action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param callable $getProjectDB
|
|
||||||
* @param Document $document
|
|
||||||
* @param Document $project
|
|
||||||
* @return void
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function deleteDatabase(callable $getProjectDB, Document $document, Document $project): void
|
|
||||||
{
|
|
||||||
$databaseId = $document->getId();
|
|
||||||
$dbForProject = $getProjectDB($project);
|
|
||||||
|
|
||||||
$this->deleteByGroup('database_' . $document->getInternalId(), [], $dbForProject, function ($document) use ($getProjectDB, $project) {
|
|
||||||
$this->deleteCollection($getProjectDB, $document, $project);
|
|
||||||
});
|
|
||||||
|
|
||||||
$dbForProject->deleteCollection('database_' . $document->getInternalId());
|
|
||||||
$this->deleteAuditLogsByResource($getProjectDB, 'database/' . $databaseId, $project);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param callable $getProjectDB
|
|
||||||
* @param Document $document teams document
|
|
||||||
* @param Document $project
|
|
||||||
* @return void
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function deleteCollection(callable $getProjectDB, Document $document, Document $project): void
|
|
||||||
{
|
|
||||||
$collectionId = $document->getId();
|
|
||||||
$collectionInternalId = $document->getInternalId();
|
|
||||||
$databaseId = $document->getAttribute('databaseId');
|
|
||||||
$databaseInternalId = $document->getAttribute('databaseInternalId');
|
|
||||||
|
|
||||||
$dbForProject = $getProjectDB($project);
|
|
||||||
|
|
||||||
$relationships = \array_filter(
|
|
||||||
$document->getAttribute('attributes'),
|
|
||||||
fn ($attribute) => $attribute['type'] === Database::VAR_RELATIONSHIP
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($relationships as $relationship) {
|
|
||||||
if (!$relationship['twoWay']) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$relatedCollection = $dbForProject->getDocument('database_' . $databaseInternalId, $relationship['relatedCollection']);
|
|
||||||
$dbForProject->deleteDocument('attributes', $databaseInternalId . '_' . $relatedCollection->getInternalId() . '_' . $relationship['twoWayKey']);
|
|
||||||
$dbForProject->deleteCachedDocument('database_' . $databaseInternalId, $relatedCollection->getId());
|
|
||||||
$dbForProject->deleteCachedCollection('database_' . $databaseInternalId . '_collection_' . $relatedCollection->getInternalId());
|
|
||||||
}
|
|
||||||
|
|
||||||
$dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $document->getInternalId());
|
|
||||||
|
|
||||||
$this->deleteByGroup('attributes', [
|
|
||||||
Query::equal('databaseInternalId', [$databaseInternalId]),
|
|
||||||
Query::equal('collectionInternalId', [$collectionInternalId])
|
|
||||||
], $dbForProject);
|
|
||||||
|
|
||||||
$this->deleteByGroup('indexes', [
|
|
||||||
Query::equal('databaseInternalId', [$databaseInternalId]),
|
|
||||||
Query::equal('collectionInternalId', [$collectionInternalId])
|
|
||||||
], $dbForProject);
|
|
||||||
|
|
||||||
$this->deleteAuditLogsByResource($getProjectDB, 'database/' . $databaseId . '/collection/' . $collectionId, $project);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Database $dbForConsole
|
* @param Database $dbForConsole
|
||||||
* @param callable $getProjectDB
|
* @param callable $getProjectDB
|
||||||
|
@ -345,7 +275,7 @@ class Deletes extends Action
|
||||||
{
|
{
|
||||||
$dbForProject = $getProjectDB($project);
|
$dbForProject = $getProjectDB($project);
|
||||||
// Delete Usage stats
|
// Delete Usage stats
|
||||||
$this->deleteByGroup('stats_v2', [
|
$this->deleteByGroup('stats', [
|
||||||
Query::lessThan('time', $hourlyUsageRetentionDatetime),
|
Query::lessThan('time', $hourlyUsageRetentionDatetime),
|
||||||
Query::equal('period', ['1h']),
|
Query::equal('period', ['1h']),
|
||||||
], $dbForProject);
|
], $dbForProject);
|
||||||
|
|
|
@ -286,7 +286,7 @@ class Hamster extends Action
|
||||||
$limit = $periodValue['limit'];
|
$limit = $periodValue['limit'];
|
||||||
$period = $periodValue['period'];
|
$period = $periodValue['period'];
|
||||||
|
|
||||||
$requestDocs = $dbForProject->find('stats_v2', [
|
$requestDocs = $dbForProject->find('stats', [
|
||||||
Query::equal('period', [$period]),
|
Query::equal('period', [$period]),
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::limit($limit),
|
Query::limit($limit),
|
||||||
|
|
|
@ -130,8 +130,8 @@ class Usage extends Action
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case $document->getCollection() === 'databases': // databases
|
case $document->getCollection() === 'databases': // databases
|
||||||
$collections = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS)));
|
$collections = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS)));
|
||||||
$documents = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS)));
|
$documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS)));
|
||||||
if (!empty($collections['value'])) {
|
if (!empty($collections['value'])) {
|
||||||
$metrics[] = [
|
$metrics[] = [
|
||||||
'key' => METRIC_COLLECTIONS,
|
'key' => METRIC_COLLECTIONS,
|
||||||
|
@ -149,7 +149,7 @@ class Usage extends Action
|
||||||
case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections
|
case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections
|
||||||
$parts = explode('_', $document->getCollection());
|
$parts = explode('_', $document->getCollection());
|
||||||
$databaseInternalId = $parts[1] ?? 0;
|
$databaseInternalId = $parts[1] ?? 0;
|
||||||
$documents = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS)));
|
$documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS)));
|
||||||
|
|
||||||
if (!empty($documents['value'])) {
|
if (!empty($documents['value'])) {
|
||||||
$metrics[] = [
|
$metrics[] = [
|
||||||
|
@ -164,8 +164,8 @@ class Usage extends Action
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case $document->getCollection() === 'buckets':
|
case $document->getCollection() === 'buckets':
|
||||||
$files = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES)));
|
$files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES)));
|
||||||
$storage = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE)));
|
$storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE)));
|
||||||
|
|
||||||
if (!empty($files['value'])) {
|
if (!empty($files['value'])) {
|
||||||
$metrics[] = [
|
$metrics[] = [
|
||||||
|
@ -183,13 +183,13 @@ class Usage extends Action
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case $document->getCollection() === 'functions':
|
case $document->getCollection() === 'functions':
|
||||||
$deployments = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS)));
|
$deployments = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS)));
|
||||||
$deploymentsStorage = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE)));
|
$deploymentsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE)));
|
||||||
$builds = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS)));
|
$builds = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS)));
|
||||||
$buildsStorage = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE)));
|
$buildsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE)));
|
||||||
$buildsCompute = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE)));
|
$buildsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE)));
|
||||||
$executions = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS)));
|
$executions = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS)));
|
||||||
$executionsCompute = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE)));
|
$executionsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE)));
|
||||||
|
|
||||||
if (!empty($deployments['value'])) {
|
if (!empty($deployments['value'])) {
|
||||||
$metrics[] = [
|
$metrics[] = [
|
||||||
|
|
|
@ -78,7 +78,7 @@ class UsageDump extends Action
|
||||||
$id = \md5("{$time}_{$period}_{$key}");
|
$id = \md5("{$time}_{$period}_{$key}");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$dbForProject->createDocument('stats_v2', new Document([
|
$dbForProject->createDocument('stats', new Document([
|
||||||
'$id' => $id,
|
'$id' => $id,
|
||||||
'period' => $period,
|
'period' => $period,
|
||||||
'time' => $time,
|
'time' => $time,
|
||||||
|
@ -89,14 +89,14 @@ class UsageDump extends Action
|
||||||
} catch (Duplicate $th) {
|
} catch (Duplicate $th) {
|
||||||
if ($value < 0) {
|
if ($value < 0) {
|
||||||
$dbForProject->decreaseDocumentAttribute(
|
$dbForProject->decreaseDocumentAttribute(
|
||||||
'stats_v2',
|
'stats',
|
||||||
$id,
|
$id,
|
||||||
'value',
|
'value',
|
||||||
abs($value)
|
abs($value)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$dbForProject->increaseDocumentAttribute(
|
$dbForProject->increaseDocumentAttribute(
|
||||||
'stats_v2',
|
'stats',
|
||||||
$id,
|
$id,
|
||||||
'value',
|
'value',
|
||||||
$value
|
$value
|
||||||
|
|
|
@ -295,7 +295,7 @@ class OpenAPI3 extends Format
|
||||||
switch ((!empty($validator)) ? \get_class($validator) : '') {
|
switch ((!empty($validator)) ? \get_class($validator) : '') {
|
||||||
case 'Utopia\Validator\Text':
|
case 'Utopia\Validator\Text':
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
$node['schema']['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
$node['schema']['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Boolean':
|
case 'Utopia\Validator\Boolean':
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
|
@ -303,14 +303,14 @@ class OpenAPI3 extends Format
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Database\Validator\UID':
|
case 'Utopia\Database\Validator\UID':
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
$node['schema']['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
$node['schema']['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>';
|
||||||
break;
|
break;
|
||||||
case 'Appwrite\Utopia\Database\Validator\CustomId':
|
case 'Appwrite\Utopia\Database\Validator\CustomId':
|
||||||
if ($route->getLabel('sdk.methodType', '') === 'upload') {
|
if ($route->getLabel('sdk.methodType', '') === 'upload') {
|
||||||
$node['schema']['x-upload-id'] = true;
|
$node['schema']['x-upload-id'] = true;
|
||||||
}
|
}
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
$node['schema']['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
$node['schema']['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Database\Validator\DatetimeValidator':
|
case 'Utopia\Database\Validator\DatetimeValidator':
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
|
|
|
@ -297,7 +297,7 @@ class Swagger2 extends Format
|
||||||
switch ((!empty($validator)) ? \get_class($validator) : '') {
|
switch ((!empty($validator)) ? \get_class($validator) : '') {
|
||||||
case 'Utopia\Validator\Text':
|
case 'Utopia\Validator\Text':
|
||||||
$node['type'] = $validator->getType();
|
$node['type'] = $validator->getType();
|
||||||
$node['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
$node['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Boolean':
|
case 'Utopia\Validator\Boolean':
|
||||||
$node['type'] = $validator->getType();
|
$node['type'] = $validator->getType();
|
||||||
|
@ -308,11 +308,11 @@ class Swagger2 extends Format
|
||||||
$node['x-upload-id'] = true;
|
$node['x-upload-id'] = true;
|
||||||
}
|
}
|
||||||
$node['type'] = $validator->getType();
|
$node['type'] = $validator->getType();
|
||||||
$node['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
$node['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Database\Validator\UID':
|
case 'Utopia\Database\Validator\UID':
|
||||||
$node['type'] = $validator->getType();
|
$node['type'] = $validator->getType();
|
||||||
$node['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']';
|
$node['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Database\Validator\DatetimeValidator':
|
case 'Utopia\Database\Validator\DatetimeValidator':
|
||||||
$node['type'] = $validator->getType();
|
$node['type'] = $validator->getType();
|
||||||
|
|
|
@ -512,4 +512,52 @@ class HealthCustomServerTest extends Scope
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testUsageSuccess()
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/health/queue/usage', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), []);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
|
$this->assertIsInt($response['body']['size']);
|
||||||
|
$this->assertLessThan(100, $response['body']['size']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/health/queue/usage?threshold=0', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), []);
|
||||||
|
$this->assertEquals(503, $response['headers']['status-code']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUsageDumpSuccess()
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/health/queue/usage-dump', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), []);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
|
$this->assertIsInt($response['body']['size']);
|
||||||
|
$this->assertLessThan(100, $response['body']['size']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/health/queue/usage-dump?threshold=0', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), []);
|
||||||
|
$this->assertEquals(503, $response['headers']['status-code']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue