1
0
Fork 0
mirror of synced 2024-05-17 11:12:41 +12:00

added inf metric

This commit is contained in:
shimon 2023-11-08 11:09:32 +02:00
parent f64d0ab8a8
commit ad63a377ab
13 changed files with 533 additions and 229 deletions

View file

@ -99,12 +99,9 @@ RUN chmod +x /usr/local/bin/doctor && \
# Cloud Executabless
RUN chmod +x /usr/local/bin/hamster && \
chmod +x /usr/local/bin/volume-sync && \
chmod +x /usr/local/bin/patch-delete-schedule-updated-at-attribute && \
chmod +x /usr/local/bin/patch-delete-project-collections && \
chmod +x /usr/local/bin/delete-orphaned-projects && \
chmod +x /usr/local/bin/clear-card-cache && \
chmod +x /usr/local/bin/calc-users-stats
chmod +x /usr/local/bin/calc-tier-stats && \
chmod +x /usr/local/bin/create-inf-metric
# Letsencrypt Permissions
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/

View file

@ -3557,7 +3557,7 @@ App::get('/v1/databases/usage')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_DATABASES)
->param('range', '24h', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true)
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $range, Response $response, Database $dbForProject) {
@ -3571,15 +3571,14 @@ App::get('/v1/databases/usage')
METRIC_DOCUMENTS,
];
$total = [];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $count => $metric) {
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
$total[$count] = $result['value'] ?? 0;
$stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit'];
$period = $days['period'];
$results = $dbForProject->find('stats', [
@ -3588,10 +3587,10 @@ App::get('/v1/databases/usage')
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
$stats[$metric]['data'] = [];
foreach ($results as $result) {
$stats[$metric][$result->getAttribute('time')] = [
'value' => $total[$count] - $result->getAttribute('value'),
$stats[$metric]['data'][$result->getAttribute('time')] = [
'value' => $result->getAttribute('value'),
];
}
}
@ -3603,25 +3602,26 @@ App::get('/v1/databases/usage')
};
foreach ($metrics as $metric) {
$usage[$metric] = [];
$usage[$metric]['total'] = $stats[$metric]['total'];
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
$usage[$metric]['data'][] = [
'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
}
$response->dynamic(new Document([
'range' => $range,
'databasesTotal' => $total[0],
'collectionsTotal' => $total[1],
'documentsTotal' => $total[2],
'databases' => $usage[$metrics[0]],
'collections' => $usage[$metrics[1]],
'documents' => $usage[$metrics[2]],
'databasesTotal' => $usage[$metrics[0]]['total'],
'collectionsTotal' => $usage[$metrics[1]]['total'],
'documentsTotal' => $usage[$metrics[2]]['total'],
'databases' => $usage[$metrics[0]]['data'],
'collections' => $usage[$metrics[1]]['data'],
'documents' => $usage[$metrics[2]]['data'],
]), Response::MODEL_USAGE_DATABASES);
});
@ -3636,7 +3636,7 @@ App::get('/v1/databases/:databaseId/usage')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_DATABASE)
->param('databaseId', '', new UID(), 'Database ID.')
->param('range', '24h', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true)
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $databaseId, string $range, Response $response, Database $dbForProject) {
@ -3655,15 +3655,14 @@ App::get('/v1/databases/:databaseId/usage')
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS),
];
$total = [];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $count => $metric) {
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
$total[$count] = $result['value'] ?? 0;
$stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit'];
$period = $days['period'];
$results = $dbForProject->find('stats', [
@ -3672,10 +3671,10 @@ App::get('/v1/databases/:databaseId/usage')
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
$stats[$metric]['data'] = [];
foreach ($results as $result) {
$stats[$metric][$result->getAttribute('time')] = [
'value' => $total[$count] - $result->getAttribute('value'),
$stats[$metric]['data'][$result->getAttribute('time')] = [
'value' => $result->getAttribute('value'),
];
}
}
@ -3687,13 +3686,14 @@ App::get('/v1/databases/:databaseId/usage')
};
foreach ($metrics as $metric) {
$usage[$metric] = [];
$usage[$metric]['total'] = $stats[$metric]['total'];
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
$usage[$metric]['data'][] = [
'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
@ -3701,10 +3701,10 @@ App::get('/v1/databases/:databaseId/usage')
$response->dynamic(new Document([
'range' => $range,
'collectionsTotal' => $total[0],
'documentsTotal' => $total[1],
'collections' => $usage[$metrics[0]],
'documents' => $usage[$metrics[1]],
'collectionsTotal' => $usage[$metrics[0]]['total'],
'documentsTotal' => $usage[$metrics[1]]['total'],
'collections' => $usage[$metrics[0]]['data'],
'documents' => $usage[$metrics[1]]['data'],
]), Response::MODEL_USAGE_DATABASE);
});
@ -3720,7 +3720,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_COLLECTION)
->param('databaseId', '', new UID(), 'Database ID.')
->param('range', '24h', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->param('collectionId', '', new UID(), 'Collection ID.')
->inject('response')
->inject('dbForProject')
@ -3741,15 +3741,14 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collectionDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS),
];
$total = [];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $count => $metric) {
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
$total[$count] = $result['value'] ?? 0;
$stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit'];
$period = $days['period'];
$results = $dbForProject->find('stats', [
@ -3758,10 +3757,10 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
$stats[$metric]['data'] = [];
foreach ($results as $result) {
$stats[$metric][$result->getAttribute('time')] = [
'value' => $total[$count] - $result->getAttribute('value'),
$stats[$metric]['data'][$result->getAttribute('time')] = [
'value' => $result->getAttribute('value'),
];
}
}
@ -3773,13 +3772,14 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
};
foreach ($metrics as $metric) {
$usage[$metric] = [];
$usage[$metric]['total'] = $stats[$metric]['total'];
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
$usage[$metric]['data'][] = [
'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
@ -3787,7 +3787,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
$response->dynamic(new Document([
'range' => $range,
'documentsTotal' => $total[0],
'documents' => $usage[$metrics[0]],
'documentsTotal' => $usage[$metrics[0]]['total'],
'documents' => $usage[$metrics[0]]['data'],
]), Response::MODEL_USAGE_COLLECTION);
});

View file

@ -454,7 +454,7 @@ App::get('/v1/functions/:functionId/usage')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_FUNCTIONS)
->param('functionId', '', new UID(), 'Function ID.')
->param('range', '24h', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true)
->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, string $range, Response $response, Database $dbForProject) {
@ -478,15 +478,14 @@ App::get('/v1/functions/:functionId/usage')
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE),
];
$total = [];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $count => $metric) {
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
$total[$count] = $result['value'] ?? 0;
$stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit'];
$period = $days['period'];
$results = $dbForProject->find('stats', [
@ -495,10 +494,10 @@ App::get('/v1/functions/:functionId/usage')
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
$stats[$metric]['data'] = [];
foreach ($results as $result) {
$stats[$metric][$result->getAttribute('time')] = [
'value' => $total[$count] - $result->getAttribute('value'),
$stats[$metric]['data'][$result->getAttribute('time')] = [
'value' => $result->getAttribute('value'),
];
}
}
@ -510,13 +509,14 @@ App::get('/v1/functions/:functionId/usage')
};
foreach ($metrics as $metric) {
$usage[$metric] = [];
$usage[$metric]['total'] = $stats[$metric]['total'];
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
$usage[$metric]['data'][] = [
'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
@ -524,20 +524,20 @@ App::get('/v1/functions/:functionId/usage')
$response->dynamic(new Document([
'range' => $range,
'deploymentsTotal' => $total[0],
'deploymentsStorageTotal' => $total[1],
'buildsTotal' => $total[2],
'buildsStorageTotal' => $total[3],
'buildsTimeTotal' => $total[4],
'executionsTotal' => $total[5],
'executionsTimeTotal' => $total[6],
'deployments' => $usage[$metrics[0]],
'deploymentsStorage' => $usage[$metrics[1]],
'builds' => $usage[$metrics[2]],
'buildsStorage' => $usage[$metrics[3]],
'buildsTime' => $usage[$metrics[4]],
'executions' => $usage[$metrics[5]],
'executionsTime' => $usage[$metrics[6]],
'deploymentsTotal' => $usage[$metrics[0]]['total'],
'deploymentsStorageTotal' => $usage[$metrics[1]]['total'],
'buildsTotal' => $usage[$metrics[2]]['total'],
'buildsStorageTotal' => $usage[$metrics[3]]['total'],
'buildsTimeTotal' => $usage[$metrics[4]]['total'],
'executionsTotal' => $usage[$metrics[5]]['total'],
'executionsTimeTotal' => $usage[$metrics[6]]['total'],
'deployments' => $usage[$metrics[0]]['data'],
'deploymentsStorage' => $usage[$metrics[1]]['data'],
'builds' => $usage[$metrics[2]]['data'],
'buildsStorage' => $usage[$metrics[3]]['data'],
'buildsTime' => $usage[$metrics[4]]['data'],
'executions' => $usage[$metrics[5]]['data'],
'executionsTime' => $usage[$metrics[6]]['data'],
]), Response::MODEL_USAGE_FUNCTION);
});
@ -551,7 +551,7 @@ App::get('/v1/functions/usage')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_FUNCTIONS)
->param('range', '24h', new WhiteList(['24h', '30d', '90d']), 'Date range.', true)
->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $range, Response $response, Database $dbForProject) {
@ -570,15 +570,14 @@ App::get('/v1/functions/usage')
METRIC_EXECUTIONS_COMPUTE,
];
$total = [];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $count => $metric) {
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
$total[$count] = $result['value'] ?? 0;
$stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit'];
$period = $days['period'];
$results = $dbForProject->find('stats', [
@ -587,10 +586,10 @@ App::get('/v1/functions/usage')
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
$stats[$metric]['data'] = [];
foreach ($results as $result) {
$stats[$metric][$result->getAttribute('time')] = [
'value' => $total[$count] - $result->getAttribute('value'),
$stats[$metric]['data'][$result->getAttribute('time')] = [
'value' => $result->getAttribute('value'),
];
}
}
@ -602,35 +601,36 @@ App::get('/v1/functions/usage')
};
foreach ($metrics as $metric) {
$usage[$metric] = [];
$usage[$metric]['total'] = $stats[$metric]['total'];
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
$usage[$metric]['data'][] = [
'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
}
$response->dynamic(new Document([
'range' => $range,
'functionsTotal' => $total[0],
'deploymentsTotal' => $total[1],
'deploymentsStorageTotal' => $total[2],
'buildsTotal' => $total[3],
'buildsStorageTotal' => $total[4],
'buildsTimeTotal' => $total[5],
'executionsTotal' => $total[6],
'executionsTimeTotal' => $total[7],
'functions' => $usage[$metrics[0]],
'deployments' => $usage[$metrics[1]],
'deploymentsStorage' => $usage[$metrics[2]],
'builds' => $usage[$metrics[3]],
'buildsStorage' => $usage[$metrics[4]],
'buildsTime' => $usage[$metrics[5]],
'executions' => $usage[$metrics[6]],
'executionsTime' => $usage[$metrics[7]],
'functionsTotal' => $usage[$metrics[0]]['total'],
'deploymentsTotal' => $usage[$metrics[1]]['total'],
'deploymentsStorageTotal' => $usage[$metrics[2]]['total'],
'buildsTotal' => $usage[$metrics[3]]['total'],
'buildsStorageTotal' => $usage[$metrics[4]]['total'],
'buildsTimeTotal' => $usage[$metrics[5]]['total'],
'executionsTotal' => $usage[$metrics[6]]['total'],
'executionsTimeTotal' => $usage[$metrics[7]]['total'],
'functions' => $usage[$metrics[0]]['data'],
'deployments' => $usage[$metrics[1]]['data'],
'deploymentsStorage' => $usage[$metrics[2]]['data'],
'builds' => $usage[$metrics[3]]['data'],
'buildsStorage' => $usage[$metrics[4]]['data'],
'buildsTime' => $usage[$metrics[5]]['data'],
'executions' => $usage[$metrics[6]]['data'],
'executionsTime' => $usage[$metrics[7]]['data'],
]), Response::MODEL_USAGE_FUNCTIONS);
});

View file

@ -26,7 +26,7 @@ App::get('/v1/project/usage')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_PROJECT)
->param('range', '24h', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $range, Response $response, Database $dbForProject) {
@ -85,7 +85,6 @@ App::get('/v1/project/usage')
}
}
$response->dynamic(new Document([
'range' => $range,
'requestsTotal' => ($usage[$metrics[0]]),

View file

@ -1476,7 +1476,7 @@ App::get('/v1/storage/usage')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_STORAGE)
->param('range', '24h', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $range, Response $response, Database $dbForProject) {
@ -1492,13 +1492,13 @@ App::get('/v1/storage/usage')
$total = [];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $count => $metric) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
$total[$count] = $result['value'] ?? 0;
$stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit'];
$period = $days['period'];
$results = $dbForProject->find('stats', [
@ -1507,10 +1507,10 @@ App::get('/v1/storage/usage')
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
$stats[$metric]['data'] = [];
foreach ($results as $result) {
$stats[$metric][$result->getAttribute('time')] = [
'value' => $total[$count] - $result->getAttribute('value'),
$stats[$metric]['data'][$result->getAttribute('time')] = [
'value' => $result->getAttribute('value'),
];
}
}
@ -1522,25 +1522,26 @@ App::get('/v1/storage/usage')
};
foreach ($metrics as $metric) {
$usage[$metric] = [];
$usage[$metric]['total'] = $stats[$metric]['total'];
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
$usage[$metric]['data'][] = [
'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
}
$response->dynamic(new Document([
'range' => $range,
'bucketsTotal' => $total[0],
'filesTotal' => $total[1],
'filesStorageTotal' => $total[2],
'buckets' => $usage[$metrics[0]],
'files' => $usage[$metrics[1]],
'storage' => $usage[$metrics[2]],
'bucketsTotal' => $usage[$metrics[0]]['total'],
'filesTotal' => $usage[$metrics[1]]['total'],
'filesStorageTotal' => $usage[$metrics[2]]['total'],
'buckets' => $usage[$metrics[0]]['data'],
'files' => $usage[$metrics[1]]['data'],
'storage' => $usage[$metrics[2]]['data'],
]), Response::MODEL_USAGE_STORAGE);
});
@ -1555,7 +1556,7 @@ App::get('/v1/storage/:bucketId/usage')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_BUCKETS)
->param('bucketId', '', new UID(), 'Bucket ID.')
->param('range', '24h', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $bucketId, string $range, Response $response, Database $dbForProject) {
@ -1574,15 +1575,15 @@ App::get('/v1/storage/:bucketId/usage')
str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE),
];
$total = [];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $count => $metric) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
$total[$count] = $result['value'] ?? 0;
$stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit'];
$period = $days['period'];
$results = $dbForProject->find('stats', [
@ -1591,29 +1592,30 @@ App::get('/v1/storage/:bucketId/usage')
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
$stats[$metric]['data'] = [];
foreach ($results as $result) {
$stats[$metric][$result->getAttribute('time')] = [
'value' => $total[$count] - $result->getAttribute('value'),
$stats[$metric]['data'][$result->getAttribute('time')] = [
'value' => $result->getAttribute('value'),
];
}
}
});
$format = match ($days['period']) {
'1h' => 'Y-m-d\TH:00:00.000P',
'1d' => 'Y-m-d\T00:00:00.000P',
};
foreach ($metrics as $metric) {
$usage[$metric] = [];
$usage[$metric]['total'] = $stats[$metric]['total'];
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
$usage[$metric]['data'][] = [
'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
@ -1621,9 +1623,9 @@ App::get('/v1/storage/:bucketId/usage')
$response->dynamic(new Document([
'range' => $range,
'filesTotal' => $total[0],
'storageTotal' => $total[1],
'files' => $usage[$metrics[0]],
'storage' => $usage[$metrics[1]],
'filesTotal' => $usage[$metrics[0]]['total'],
'filesStorageTotal' => $usage[$metrics[1]]['total'],
'files' => $usage[$metrics[0]]['data'],
'storage' => $usage[$metrics[1]]['data'],
]), Response::MODEL_USAGE_BUCKETS);
});

View file

@ -1222,7 +1222,7 @@ App::get('/v1/users/usage')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_USERS)
->param('range', '24h', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->inject('register')
@ -1236,15 +1236,14 @@ App::get('/v1/users/usage')
METRIC_SESSIONS,
];
$total = [];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $count => $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
$total[$count] = $result['value'] ?? 0;
$stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit'];
$period = $days['period'];
$results = $dbForProject->find('stats', [
@ -1253,10 +1252,10 @@ App::get('/v1/users/usage')
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
$stats[$metric]['data'] = [];
foreach ($results as $result) {
$stats[$metric][$result->getAttribute('time')] = [
'value' => $total[$count] - $result->getAttribute('value'),
$stats[$metric]['data'][$result->getAttribute('time')] = [
'value' => $result->getAttribute('value'),
];
}
}
@ -1268,13 +1267,14 @@ App::get('/v1/users/usage')
};
foreach ($metrics as $metric) {
$usage[$metric] = [];
$usage[$metric]['total'] = $stats[$metric]['total'];
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
$usage[$metric]['data'][] = [
'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
@ -1282,9 +1282,9 @@ App::get('/v1/users/usage')
$response->dynamic(new Document([
'range' => $range,
'usersTotal' => $total[0],
'sessionsTotal' => $total[1],
'users' => $usage[$metrics[0]],
'sessions' => $usage[$metrics[1]],
'usersTotal' => $usage[$metrics[0]]['total'],
'sessionsTotal' => $usage[$metrics[1]]['total'],
'users' => $usage[$metrics[0]]['data'],
'sessions' => $usage[$metrics[1]]['data'],
]), Response::MODEL_USAGE_USERS);
});

3
bin/create-inf-metric Normal file
View file

@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/cli.php create-inf-metric $@

View file

@ -2,6 +2,7 @@
namespace Appwrite\Platform\Services;
use Appwrite\Platform\Tasks\CreateInfMetric;
use Utopia\Platform\Service;
use Appwrite\Platform\Tasks\Doctor;
use Appwrite\Platform\Tasks\Install;
@ -40,6 +41,7 @@ class Tasks extends Service
->addAction(Specs::getName(), new Specs())
->addAction(CalcTierStats::getName(), new CalcTierStats())
->addAction(DeleteOrphanedProjects::getName(), new DeleteOrphanedProjects())
->addAction(CreateInfMetric::getName(), new CreateInfMetric())
;
}

View file

@ -0,0 +1,314 @@
<?php
namespace Appwrite\Platform\Tasks;
use Utopia\App;
use Utopia\Database\Document;
use Utopia\Database\Exception;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Query;
use Utopia\Platform\Action;
use Utopia\Cache\Cache;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Pools\Group;
use Utopia\Registry\Registry;
class CreateInfMetric extends Action
{
public static function getName(): string
{
return 'create-inf-metric';
}
public function __construct()
{
$this
->desc('Create infinity stats metric')
->inject('pools')
->inject('cache')
->inject('dbForConsole')
->inject('register')
->callback(function (Group $pools, Cache $cache, Database $dbForConsole, Registry $register) {
$this->action($pools, $cache, $dbForConsole, $register);
});
}
public function action(Group $pools, Cache $cache, Database $dbForConsole, Registry $register): void
{
Console::title('Create infinity metric V1');
Console::success(APP_NAME . ' Create infinity metric started');
/* Initialise new Utopia app */
$app = new App('UTC');
$console = $app->getResource('console');
$projects = [$console];
/** Database connections */
$totalProjects = $dbForConsole->count('projects');
Console::success("Found a total of: {$totalProjects} projects");
$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;
}
try {
$db = $project->getAttribute('database');
$adapter = $pools
->get($db)
->pop()
->getResource();
$dbForProject = new Database($adapter, $cache);
$dbForProject->setDefaultDatabase('appwrite');
$dbForProject->setNamespace('_' . $project->getInternalId());
$this->network($dbForProject);
$this->sessions($dbForProject);
$this->users($dbForProject);
$this->teams($dbForProject);
$this->databases($dbForProject);
$this->functions($dbForProject);
$this->storage($dbForProject);
} catch (\Throwable $th) {
var_dump($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 - 1 . '/' . $totalProjects . ' projects');
}
/**
* @param string $metric
* @param int|float $value
* @return void
*/
private function createInfMetric(database $dbForProject, string $metric, int|float $value): void
{
try {
$id = \md5("_inf_{$metric}");
$dbForProject->deleteDocument('stats', $id);
echo "_inf_{$metric} , $value \n";
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'metric' => $metric,
'period' => 'inf',
'value' => (int)$value,
'time' => null,
'region' => 'default',
]));
} catch (Duplicate $th) {
console::log("Error while creating inf metric: duplicate id {$metric} {$id}");
}
}
/**
* @param Database $dbForProject
* @param string $metric
* @return int|float
* @throws Exception
*/
protected function getFromMetric(database $dbForProject, string $metric): int|float
{
return $dbForProject->sum('stats', 'value', [
Query::equal('metric', [
$metric,
]),
Query::equal('period', ['1d']),
]);
}
/**
* @throws Exception
*/
private function network(database $dbForProject)
{
$this->createInfMetric($dbForProject, 'network.inbound', $this->getFromMetric($dbForProject, 'network.inbound'));
$this->createInfMetric($dbForProject, 'network.outbound', $this->getFromMetric($dbForProject, 'network.outbound'));
$this->createInfMetric($dbForProject, 'network.requests', $this->getFromMetric($dbForProject, 'network.requests'));
}
private function storage(database $dbForProject)
{
$bucketsCount = 0;
$filesCount = 0;
$filesStorageSum = 0;
$buckets = $dbForProject->find('buckets');
foreach ($buckets as $bucket) {
$files = $dbForProject->count('bucket_' . $bucket->getInternalId());
$this->createInfMetric($dbForProject, $bucket->getInternalId() . '.files', $files);
$filesStorage = $dbForProject->sum('bucket_' . $bucket->getInternalId(), 'sizeOriginal');
$this->createInfMetric($dbForProject, $bucket->getInternalId() . '.files.storage', $filesStorage);
$bucketsCount++;
$filesCount += $files;
$filesStorageSum += $filesStorage;
}
$this->createInfMetric($dbForProject, 'buckets', $bucketsCount);
$this->createInfMetric($dbForProject, 'files', $filesCount);
$this->createInfMetric($dbForProject, 'files.storage', $filesStorageSum);
}
private function functions(Database $dbForProject)
{
$functionsCount = 0;
$deploymentsCount = 0;
$buildsCount = 0;
$buildsStorageSum = 0;
$buildsComputeSum = 0;
$executionsCount = 0;
$executionsComputeSum = 0;
$deploymentsStorageSum = 0;
//functions
$functions = $dbForProject->find('functions');
foreach ($functions as $function) {
//deployments
$deployments = $dbForProject->find('deployments', [
Query::equal('resourceType', ['functions']),
Query::equal('resourceInternalId', [$function->getInternalId()]),
]);
$deploymentCount = 0;
$deploymentStorageSum = 0;
foreach ($deployments as $deployment) {
//builds
$builds = $dbForProject->count('builds', [
Query::equal('deploymentInternalId', [$deployment->getInternalId()]),
]);
$buildsCompute = $dbForProject->sum('builds', 'duration', [
Query::equal('deploymentInternalId', [$deployment->getInternalId()]),
]);
$buildsStorage = $dbForProject->sum('builds', 'size', [
Query::equal('deploymentInternalId', [$deployment->getInternalId()]),
]);
$this->createInfMetric($dbForProject, $function->getInternalId() . '.builds', $builds);
$this->createInfMetric($dbForProject, $function->getInternalId() . '.builds.storage', $buildsCompute * 1000);
$this->createInfMetric($dbForProject, $function->getInternalId() . '.builds.compute', $buildsStorage);
$buildsCount += $builds;
$buildsComputeSum += $buildsCompute;
$buildsStorageSum += $buildsStorage;
$deploymentCount++;
$deploymentsCount++;
$deploymentsStorageSum += $deployment['size'];
$deploymentStorageSum += $deployment['size'];
}
$this->createInfMetric($dbForProject, 'functions.' . $function->getInternalId() . '.deployments', $deploymentCount);
$this->createInfMetric($dbForProject, 'functions.' . $function->getInternalId() . '.deployments.storage', $deploymentStorageSum);
//executions
$executions = $dbForProject->count('executions', [
Query::equal('functionInternalId', [$function->getInternalId()]),
]);
$executionsCompute = $dbForProject->sum('executions', 'duration', [
Query::equal('functionInternalId', [$function->getInternalId()]),
]);
$this->createInfMetric($dbForProject, $function->getInternalId() . '.executions', $executions);
$this->createInfMetric($dbForProject, $function->getInternalId() . '.executions.compute', $executionsCompute * 1000);
$executionsCount += $executions;
$executionsComputeSum += $executionsCompute;
$functionsCount++;
}
$this->createInfMetric($dbForProject, 'functions', $functionsCount);
$this->createInfMetric($dbForProject, 'deployments', $deploymentsCount);
$this->createInfMetric($dbForProject, 'deployments.storage', $deploymentsStorageSum);
$this->createInfMetric($dbForProject, 'builds', $buildsCount);
$this->createInfMetric($dbForProject, 'builds.compute', $buildsComputeSum * 1000);
$this->createInfMetric($dbForProject, 'builds.storage', $buildsStorageSum);
$this->createInfMetric($dbForProject, 'executions', $executionsCount);
$this->createInfMetric($dbForProject, 'executions.compute', $executionsComputeSum * 1000);
}
private function databases(Database $dbForProject)
{
$databasesCount = 0;
$collectionsCount = 0;
$documentsCount = 0;
$databases = $dbForProject->find('databases');
foreach ($databases as $database) {
$collectionCount = 0;
$collections = $dbForProject->find('database_' . $database->getInternalId());
foreach ($collections as $collection) {
$documents = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
$this->createInfMetric($dbForProject, $database->getInternalId() . '.' . $collection->getInternalId() . '.documents', $documents);
$documentsCount += $documents;
$collectionCount++;
$collectionsCount++;
}
$this->createInfMetric($dbForProject, $database->getInternalId() . '.collections', $collectionCount);
$this->createInfMetric($dbForProject, $database->getInternalId() . '.documents', $documentsCount);
$databasesCount++;
}
$this->createInfMetric($dbForProject, 'collections', $collectionsCount);
$this->createInfMetric($dbForProject, 'databases', $databasesCount);
$this->createInfMetric($dbForProject, 'documents', $documentsCount);
}
private function users(Database $dbForProject)
{
$users = $dbForProject->count('users');
$this->createInfMetric($dbForProject, 'users', $users);
}
private function sessions(Database $dbForProject)
{
$users = $dbForProject->count('sessions');
$this->createInfMetric($dbForProject, 'sessions', $users);
}
private function teams(Database $dbForProject)
{
$teams = $dbForProject->count('teams');
$this->createInfMetric($dbForProject, 'teams', $teams);
}
}

View file

@ -18,13 +18,13 @@ class UsageFunction extends Model
])
->addRule('deploymentsTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total statistics of function deployments.',
'description' => 'Aggregated total statistics of function deployments.',
'default' => 0,
'example' => 0,
])
->addRule('deploymentsStorageTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total statistics of function deployments storage.',
'description' => 'Aggregated total statistics of function deployments storage.',
'default' => 0,
'example' => 0,
])
@ -36,69 +36,66 @@ class UsageFunction extends Model
])
->addRule('buildsStorageTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Aggregate total statistics for build storage.',
'description' => 'Aggregate total statistics of builds storage.',
'default' => 0,
'example' => 0,
'array' => true
])
->addRule('buildsTimeTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Aggregate total function build compute time statistics.',
'default' => [],
'example' => [],
'array' => true
'description' => 'Aggregate total statistics of build compute time.',
'default' => 0,
'example' => 0,
])
->addRule('executionsTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total function executions statistics.',
'default' => [],
'example' => [],
'array' => true
'description' => 'Aggregated total statistics of executions.',
'default' => 0,
'example' => 0,
])
->addRule('executionsTimeTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated for total function execution compute time statistics.',
'description' => 'Aggregated total statistics if execution compute time.',
'default' => 0,
'example' => 0,
])
->addRule('deployments', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics for the number of function deployments per time period.',
'description' => 'Aggregated statistics of deployments per time period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('deploymentsStorage', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics for function deployment storage per time period.',
'description' => 'Aggregated statistics of deployment storage per time period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('builds', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics for the number of function builds per time period.',
'description' => 'Aggregated statistics of builds per time period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('buildsStorage', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics for build storage per time period.',
'description' => 'Aggregated statistics of build storage per time period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('buildsTime', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics for function build compute per time period.',
'description' => 'Aggregated statistics of build compute per time period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('executions', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics for the number of function executions per time period.',
'description' => 'Aggregated statistics of executions per time period.',
'default' => [],
'example' => [],
'array' => true
@ -106,7 +103,7 @@ class UsageFunction extends Model
->addRule('executionsTime', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics for function execution compute per time period.',
'description' => 'Aggregated statistics of executions compute per time period.',
'default' => [],
'example' => [],
'array' => true

View file

@ -17,49 +17,49 @@ class UsageFunctions extends Model
'example' => '30d',
])
->addRule('functionsTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total statistics of functions.',
'default' => 0,
'example' => 0,
])
->addRule('deploymentsTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total statistics of function deployments.',
'default' => 0,
'example' => 0,
])
->addRule('deploymentsStorage', [
'type' => Response::MODEL_METRIC,
->addRule('deploymentsStorageTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total statistics of function deployments storage.',
'default' => 0,
'example' => 0,
])
->addRule('buildsTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total statistics of function builds.',
'default' => 0,
'example' => 0,
])
->addRule('buildsStorage', [
'type' => Response::MODEL_METRIC,
->addRule('buildsStorageTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total statistics of builds storage.',
'default' => 0,
'example' => 0,
])
->addRule('buildsTime', [
'type' => Response::MODEL_METRIC,
->addRule('buildsTimeTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total statistics of build compute time.',
'default' => 0,
'example' => 0,
])
->addRule('executionsTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total statistics of functions executions.',
'default' => 0,
'example' => 0,
])
->addRule('executionsTime', [
'type' => Response::MODEL_METRIC,
->addRule('executionsTimeTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total statistics of functions execution compute time.',
'default' => 0,
'example' => 0,
@ -73,42 +73,42 @@ class UsageFunctions extends Model
])
->addRule('deployments', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics of function deployments per period.',
'description' => 'Aggregated statistics of deployments per period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('deploymentsStorage', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics of function deployments storage per period.',
'description' => 'Aggregated statistics of deployments storage per period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('builds', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics of function builds per period.',
'description' => 'Aggregated statistics of builds per period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('buildsStorage', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics of builds storage per period.',
'description' => 'Aggregated statistics of storage per period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('buildsTime', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics of function build compute time per period.',
'description' => 'Aggregated statistics of builds compute time per period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('executions', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics of function executions per period.',
'description' => 'Aggregated statistics of executions per period.',
'default' => [],
'example' => [],
'array' => true
@ -116,7 +116,7 @@ class UsageFunctions extends Model
->addRule('executionsTime', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated statistics of function execution compute time per period.',
'description' => 'Aggregated statistics of execution compute time per period.',
'default' => [],
'example' => [],
'array' => true

View file

@ -11,66 +11,58 @@ class UsageProject extends Model
{
$this
->addRule('range', [
'type' => self::TYPE_STRING,
'type' => self::TYPE_INTEGER,
'description' => 'The time range of the usage stats.',
'default' => '',
'example' => '30d',
])
->addRule('requestsTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated stats for number of requests.',
'default' => [],
'example' => [],
'array' => true
'default' => 0,
'example' => 0,
])
->addRule('networkTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated stats for consumed bandwidth.',
'default' => [],
'example' => [],
'array' => true
'default' => 0,
'example' => 0,
])
->addRule('executionsTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated stats for function executions.',
'default' => [],
'example' => [],
'array' => true
'default' => 0,
'example' => 0,
])
->addRule('documentsTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated stats for number of documents.',
'default' => [],
'example' => [],
'array' => true
'default' => 0,
'example' => 0,
])
->addRule('databasesTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated stats for number of databases.',
'default' => [],
'example' => [],
'array' => true
'default' => 0,
'example' => 0,
])
->addRule('usersTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated stats for number of users.',
'default' => [],
'example' => [],
'array' => true
'default' => 0,
'example' => 0,
])
->addRule('filesStorage', [
'type' => Response::MODEL_METRIC,
->addRule('filesStorageTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated stats for the occupied storage size (in bytes).',
'default' => [],
'example' => [],
'array' => true
'default' => 0,
'example' => 0,
])
->addRule('bucketsTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated stats for number of buckets.',
'default' => [],
'example' => [],
'array' => true
'default' => 0,
'example' => 0,
])
;
}

View file

@ -17,19 +17,17 @@ class UsageUsers extends Model
'example' => '30d',
])
->addRule('usersTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total statistics of users.',
'default' => [],
'example' => [],
'array' => true
'default' => 0,
'example' => 0,
])
->addRule('sessionsTotal', [
'type' => Response::MODEL_METRIC,
'type' => self::TYPE_INTEGER,
'description' => 'Aggregated total statistics sessions created.',
'default' => [],
'example' => [],
'array' => true
'default' => 0,
'example' => 0,
])
->addRule('users', [
'type' => Response::MODEL_METRIC,