mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
new usage integration
This commit is contained in:
@@ -48,10 +48,6 @@ _APP_STORAGE_WASABI_BUCKET=
|
||||
_APP_STORAGE_ANTIVIRUS=disabled
|
||||
_APP_STORAGE_ANTIVIRUS_HOST=clamav
|
||||
_APP_STORAGE_ANTIVIRUS_PORT=3310
|
||||
_APP_INFLUXDB_HOST=influxdb
|
||||
_APP_INFLUXDB_PORT=8086
|
||||
_APP_STATSD_HOST=telegraf
|
||||
_APP_STATSD_PORT=8125
|
||||
_APP_SMTP_HOST=maildev
|
||||
_APP_SMTP_PORT=1025
|
||||
_APP_SMTP_SECURE=
|
||||
|
||||
+35
@@ -137,6 +137,41 @@
|
||||
- Get default region from environment on project create [#4780](https://github.com/appwrite/appwrite/pull/4780)
|
||||
- Fix french translation [#4782](https://github.com/appwrite/appwrite/pull/4782)
|
||||
- Fix max mimetype size [#4814](https://github.com/appwrite/appwrite/pull/4814)
|
||||
- New usage metrics collection flow [#4770](https://github.com/appwrite/appwrite/pull/4770)
|
||||
- Deprecated influxdb, telegraf containers and removed all of their occurrences from the code.
|
||||
- Removed _APP_INFLUXDB_HOST, _APP_INFLUXDB_PORT, _APP_STATSD_HOST, _APP_STATSD_PORT env variables.
|
||||
- Removed usage labels dependency.
|
||||
- Dropped type attribute from stats collection.
|
||||
- Usage metrics are processed via new usage worker.
|
||||
- Metrics changes:
|
||||
- Storage
|
||||
- deprecated
|
||||
- filesCreate, filesRead, filesUpdate, filesDelete, bucketsCreate, bucketsRead, bucketsUpdate, bucketsDelete.
|
||||
- renamed
|
||||
- filesCount to filesTotal, storage to filesStorage, bucketsCount to bucketsTotal.
|
||||
- Auth
|
||||
- deprecated
|
||||
- usersCreate, usersRead, usersUpdate, usersDelete, sessionsCreate sessionsProviderCreate, sessionsDelete.
|
||||
- renamed
|
||||
- usersCount to usersTotal.
|
||||
- added
|
||||
- sessionsTotal.
|
||||
- Databases
|
||||
- deprecated
|
||||
- databasesCreate, databasesRead, databasesDelete, documentsCreate, documentsRead, documentsUpdate, documentsDelete, collectionsCreate, collectionsRead, collectionsUpdate, collectionsDelete.
|
||||
- renamed
|
||||
- databasesCount to databasesTotal, collectionsCount to collectionsTotal, documentsCount to documentsTotal.
|
||||
- Functions
|
||||
- deprecated
|
||||
- executionsFailure, executionsSuccess, buildsFailure, buildsSuccess, executionsFailure, executionsSuccess.
|
||||
- renamed
|
||||
- executionsTime to executionsCompute, buildsTime to buildsCompute, documentsCount to documentsTotal.
|
||||
- added
|
||||
- functionsTotal, buildsStorage, deploymentsTotal, deploymentsStorage.
|
||||
- Project
|
||||
- renamed
|
||||
- executions to executionsTotal, builds to buildsTotal, requests to requestsTotal, storage to filesStorage, buckets to bucketsTotal, users to usersTotal, documents to documentsTotal, collections to collectionsTotal, databases to databasesTotal.
|
||||
|
||||
## Bugs
|
||||
- Fix invited account verified status [#4776](https://github.com/appwrite/appwrite/pull/4776)
|
||||
|
||||
|
||||
@@ -240,8 +240,6 @@ Appwrite stack is a combination of a variety of open-source technologies and too
|
||||
|
||||
- Redis - for managing cache and in-memory data (currently, we do not use Redis for persistent data).
|
||||
- MariaDB - for database storage and queries.
|
||||
- InfluxDB - for managing stats and time-series based data
|
||||
- Statsd - for sending data over UDP protocol (using Telegraf)
|
||||
- ClamAV - for validating and scanning storage files.
|
||||
- Imagemagick - for manipulating and managing image media files.
|
||||
- Webp - for better compression of images on supporting clients.
|
||||
|
||||
+2
-15
@@ -91,10 +91,6 @@ ENV _APP_SERVER=swoole \
|
||||
_APP_DB_USER=root \
|
||||
_APP_DB_PASS=password \
|
||||
_APP_DB_SCHEMA=appwrite \
|
||||
_APP_INFLUXDB_HOST=influxdb \
|
||||
_APP_INFLUXDB_PORT=8086 \
|
||||
_APP_STATSD_HOST=telegraf \
|
||||
_APP_STATSD_PORT=8125 \
|
||||
_APP_SMTP_HOST= \
|
||||
_APP_SMTP_PORT= \
|
||||
_APP_SMTP_SECURE= \
|
||||
@@ -165,7 +161,6 @@ RUN mkdir -p /storage/uploads && \
|
||||
# Executables
|
||||
RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/maintenance && \
|
||||
chmod +x /usr/local/bin/usage && \
|
||||
chmod +x /usr/local/bin/install && \
|
||||
chmod +x /usr/local/bin/upgrade && \
|
||||
chmod +x /usr/local/bin/migrate && \
|
||||
@@ -185,16 +180,8 @@ RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/worker-mails && \
|
||||
chmod +x /usr/local/bin/worker-messaging && \
|
||||
chmod +x /usr/local/bin/worker-webhooks && \
|
||||
chmod +x /usr/local/bin/worker-migrations
|
||||
|
||||
# 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/clear-card-cache && \
|
||||
chmod +x /usr/local/bin/calc-users-stats && \
|
||||
chmod +x /usr/local/bin/calc-tier-stats
|
||||
chmod +x /usr/local/bin/worker-migrations && \
|
||||
chmod +x /usr/local/bin/worker-usage
|
||||
|
||||
# Letsencrypt Permissions
|
||||
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
|
||||
|
||||
-23
@@ -116,29 +116,6 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
|
||||
return $getProjectDB;
|
||||
}, ['pools', 'dbForConsole', 'cache']);
|
||||
|
||||
CLI::setResource('influxdb', function (Registry $register) {
|
||||
$client = $register->get('influxdb'); /** @var InfluxDB\Client $client */
|
||||
$attempts = 0;
|
||||
$max = 10;
|
||||
$sleep = 1;
|
||||
|
||||
do { // check if telegraf database is ready
|
||||
try {
|
||||
$attempts++;
|
||||
$database = $client->selectDB('telegraf');
|
||||
if (in_array('telegraf', $client->listDatabases())) {
|
||||
break; // leave the do-while if successful
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("InfluxDB not ready. Retrying connection ({$attempts})...");
|
||||
if ($attempts >= $max) {
|
||||
throw new \Exception('InfluxDB database not ready yet');
|
||||
}
|
||||
sleep($sleep);
|
||||
}
|
||||
} while ($attempts < $max);
|
||||
return $database;
|
||||
}, ['register']);
|
||||
|
||||
CLI::setResource('queueForFunctions', function (Group $pools) {
|
||||
return new Func($pools->get('queue')->pop()->getResource());
|
||||
|
||||
+42
-14
@@ -1302,7 +1302,7 @@ $commonCollections = [
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 8,
|
||||
'signed' => false,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
@@ -1330,17 +1330,6 @@ $commonCollections = [
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('type'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 1,
|
||||
'signed' => false,
|
||||
'required' => true,
|
||||
'default' => 0, // 0 -> count, 1 -> sum
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
@@ -1359,14 +1348,53 @@ $commonCollections = [
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_metric_period_time'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'attributes' => ['metric', 'period', 'time'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_DESC],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
'statsLogger' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('statsLogger'),
|
||||
'name' => 'StatsLogger',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => ID::custom('time'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('metrics'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 5012,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => [],
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => ID::custom('_key_time'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['time'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_DESC],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$projectCollections = array_merge([
|
||||
'databases' => [
|
||||
|
||||
@@ -58,7 +58,6 @@ App::post('/v1/account')
|
||||
->label('audits.event', 'user.create')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.create')
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'create')
|
||||
@@ -172,8 +171,6 @@ App::post('/v1/account/sessions/email')
|
||||
->label('audits.event', 'session.create')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('usage.metric', 'sessions.{scope}.requests.create')
|
||||
->label('usage.params', ['provider:email'])
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'createEmailSession')
|
||||
@@ -426,8 +423,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->label('abuse-limit', 50)
|
||||
->label('abuse-key', 'ip:{ip}')
|
||||
->label('docs', false)
|
||||
->label('usage.metric', 'sessions.{scope}.requests.create')
|
||||
->label('usage.params', ['provider:{request.provider}'])
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
|
||||
->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true)
|
||||
->param('state', '', new Text(2048), 'OAuth2 state params.', true)
|
||||
@@ -1094,8 +1089,6 @@ App::put('/v1/account/sessions/magic-url')
|
||||
->label('audits.event', 'session.update')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('usage.metric', 'sessions.{scope}.requests.create')
|
||||
->label('usage.params', ['provider:magic-url'])
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updateMagicURLSession')
|
||||
@@ -1468,8 +1461,6 @@ App::post('/v1/account/sessions/anonymous')
|
||||
->label('audits.event', 'session.create')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('usage.metric', 'sessions.{scope}.requests.create')
|
||||
->label('usage.params', ['provider:anonymous'])
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'createAnonymousSession')
|
||||
@@ -1646,7 +1637,6 @@ App::get('/v1/account')
|
||||
->desc('Get Account')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'get')
|
||||
@@ -1667,7 +1657,6 @@ App::get('/v1/account/prefs')
|
||||
->desc('Get Account Preferences')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'getPrefs')
|
||||
@@ -1690,7 +1679,6 @@ App::get('/v1/account/sessions')
|
||||
->desc('List Sessions')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'listSessions')
|
||||
@@ -1729,7 +1717,6 @@ App::get('/v1/account/logs')
|
||||
->desc('List Logs')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'listLogs')
|
||||
@@ -1790,7 +1777,6 @@ App::get('/v1/account/sessions/:sessionId')
|
||||
->desc('Get Session')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'getSession')
|
||||
@@ -1838,7 +1824,6 @@ App::patch('/v1/account/name')
|
||||
->label('scope', 'account')
|
||||
->label('audits.event', 'user.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updateName')
|
||||
@@ -1873,7 +1858,6 @@ App::patch('/v1/account/password')
|
||||
->label('audits.event', 'user.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updatePassword')
|
||||
@@ -1939,7 +1923,6 @@ App::patch('/v1/account/email')
|
||||
->label('scope', 'account')
|
||||
->label('audits.event', 'user.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updateEmail')
|
||||
@@ -2009,7 +1992,6 @@ App::patch('/v1/account/phone')
|
||||
->label('scope', 'account')
|
||||
->label('audits.event', 'user.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updatePhone')
|
||||
@@ -2068,7 +2050,6 @@ App::patch('/v1/account/prefs')
|
||||
->label('scope', 'account')
|
||||
->label('audits.event', 'user.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updatePrefs')
|
||||
@@ -2102,7 +2083,6 @@ App::patch('/v1/account/status')
|
||||
->label('scope', 'account')
|
||||
->label('audits.event', 'user.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.delete')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updateStatus')
|
||||
@@ -2146,7 +2126,6 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].delete')
|
||||
->label('audits.event', 'session.delete')
|
||||
->label('audits.resource', 'user/{user.$id}')
|
||||
->label('usage.metric', 'sessions.{scope}.requests.delete')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'deleteSession')
|
||||
@@ -2223,7 +2202,6 @@ App::patch('/v1/account/sessions/:sessionId')
|
||||
->label('audits.event', 'session.update')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('usage.metric', 'sessions.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updateSession')
|
||||
@@ -2308,7 +2286,6 @@ App::delete('/v1/account/sessions')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].delete')
|
||||
->label('audits.event', 'session.delete')
|
||||
->label('audits.resource', 'user/{user.$id}')
|
||||
->label('usage.metric', 'sessions.{scope}.requests.delete')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'deleteSessions')
|
||||
@@ -2370,7 +2347,6 @@ App::post('/v1/account/recovery')
|
||||
->label('audits.event', 'recovery.create')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'createRecovery')
|
||||
@@ -2553,7 +2529,6 @@ App::put('/v1/account/recovery')
|
||||
->label('audits.event', 'recovery.update')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updateRecovery')
|
||||
@@ -2640,7 +2615,6 @@ App::post('/v1/account/verification')
|
||||
->label('event', 'users.[userId].verification.[tokenId].create')
|
||||
->label('audits.event', 'verification.create')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'createVerification')
|
||||
@@ -2801,7 +2775,6 @@ App::put('/v1/account/verification')
|
||||
->label('event', 'users.[userId].verification.[tokenId].update')
|
||||
->label('audits.event', 'verification.update')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updateVerification')
|
||||
@@ -2862,7 +2835,6 @@ App::post('/v1/account/verification/phone')
|
||||
->label('event', 'users.[userId].verification.[tokenId].create')
|
||||
->label('audits.event', 'verification.create')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'createPhoneVerification')
|
||||
@@ -2958,7 +2930,6 @@ App::put('/v1/account/verification/phone')
|
||||
->label('event', 'users.[userId].verification.[tokenId].update')
|
||||
->label('audits.event', 'verification.update')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updatePhoneVerification')
|
||||
|
||||
+146
-346
@@ -377,7 +377,6 @@ App::post('/v1/databases')
|
||||
->label('scope', 'databases.write')
|
||||
->label('audits.event', 'database.create')
|
||||
->label('audits.resource', 'database/{response.$id}')
|
||||
->label('usage.metric', 'databases.{scope}.requests.create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'create')
|
||||
@@ -451,7 +450,6 @@ App::get('/v1/databases')
|
||||
->desc('List Databases')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'databases.read')
|
||||
->label('usage.metric', 'databases.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'list')
|
||||
@@ -499,7 +497,6 @@ App::get('/v1/databases/:databaseId')
|
||||
->desc('Get Database')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'databases.read')
|
||||
->label('usage.metric', 'databases.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'get')
|
||||
@@ -614,7 +611,6 @@ App::put('/v1/databases/:databaseId')
|
||||
->label('event', 'databases.[databaseId].update')
|
||||
->label('audits.event', 'database.update')
|
||||
->label('audits.resource', 'database/{response.$id}')
|
||||
->label('usage.metric', 'databases.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'update')
|
||||
@@ -659,7 +655,6 @@ App::delete('/v1/databases/:databaseId')
|
||||
->label('event', 'databases.[databaseId].delete')
|
||||
->label('audits.event', 'database.delete')
|
||||
->label('audits.resource', 'database/{request.databaseId}')
|
||||
->label('usage.metric', 'databases.{scope}.requests.delete')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'delete')
|
||||
@@ -704,8 +699,6 @@ App::post('/v1/databases/:databaseId/collections')
|
||||
->label('scope', 'collections.write')
|
||||
->label('audits.event', 'collection.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{response.$id}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.create')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'createCollection')
|
||||
@@ -771,8 +764,6 @@ App::get('/v1/databases/:databaseId/collections')
|
||||
->desc('List Collections')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('usage.metric', 'collections.{scope}.requests.read')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listCollections')
|
||||
@@ -830,8 +821,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId')
|
||||
->desc('Get Collection')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('usage.metric', 'collections.{scope}.requests.read')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'getCollection')
|
||||
@@ -866,8 +855,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
|
||||
->desc('List Collection Logs')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('usage.metric', 'collections.{scope}.requests.read')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listCollectionLogs')
|
||||
@@ -965,8 +952,6 @@ App::put('/v1/databases/:databaseId/collections/:collectionId')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].update')
|
||||
->label('audits.event', 'collection.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'updateCollection')
|
||||
@@ -1035,8 +1020,6 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].delete')
|
||||
->label('audits.event', 'collection.delete')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.delete')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'deleteCollection')
|
||||
@@ -1091,8 +1074,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
|
||||
->label('scope', 'collections.write')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'createStringAttribute')
|
||||
@@ -1149,8 +1130,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email'
|
||||
->label('scope', 'collections.write')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'createEmailAttribute')
|
||||
@@ -1193,8 +1172,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum')
|
||||
->label('scope', 'collections.write')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'createEnumAttribute')
|
||||
@@ -1253,8 +1230,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip')
|
||||
->label('scope', 'collections.write')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'createIpAttribute')
|
||||
@@ -1297,8 +1272,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url')
|
||||
->label('scope', 'collections.write')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'createUrlAttribute')
|
||||
@@ -1341,8 +1314,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege
|
||||
->label('scope', 'collections.write')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'createIntegerAttribute')
|
||||
@@ -1414,8 +1385,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float'
|
||||
->label('scope', 'collections.write')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'createFloatAttribute')
|
||||
@@ -1490,8 +1459,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea
|
||||
->label('scope', 'collections.write')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'createBooleanAttribute')
|
||||
@@ -1533,8 +1500,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
|
||||
->label('scope', 'collections.write')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'createDatetimeAttribute')
|
||||
@@ -1579,8 +1544,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati
|
||||
->label('scope', 'collections.write')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'createRelationshipAttribute')
|
||||
@@ -1658,8 +1621,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
|
||||
->desc('List Attributes')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('usage.metric', 'collections.{scope}.requests.read')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listAttributes')
|
||||
@@ -1733,8 +1694,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key')
|
||||
->desc('Get Attribute')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('usage.metric', 'collections.{scope}.requests.read')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'getAttribute')
|
||||
@@ -1812,8 +1771,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'updateStringAttribute')
|
||||
@@ -1853,8 +1810,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'updateEmailAttribute')
|
||||
@@ -1894,8 +1849,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'updateEnumAttribute')
|
||||
@@ -1937,8 +1890,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'updateIpAttribute')
|
||||
@@ -1978,8 +1929,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'updateUrlAttribute')
|
||||
@@ -2019,8 +1968,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'updateIntegerAttribute')
|
||||
@@ -2070,8 +2017,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'updateFloatAttribute')
|
||||
@@ -2121,8 +2066,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'updateBooleanAttribute')
|
||||
@@ -2161,8 +2104,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'documents.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'updateDatetimeAttribute')
|
||||
@@ -2201,8 +2142,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'documents.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'updateRelationshipAttribute')
|
||||
@@ -2257,8 +2196,6 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete')
|
||||
->label('audits.event', 'attribute.delete')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'deleteAttribute')
|
||||
@@ -2368,8 +2305,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
|
||||
->label('scope', 'collections.write')
|
||||
->label('audits.event', 'index.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'createIndex')
|
||||
@@ -2525,8 +2460,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes')
|
||||
->desc('List Indexes')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('usage.metric', 'collections.{scope}.requests.read')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listIndexes')
|
||||
@@ -2590,8 +2523,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
|
||||
->desc('Get Index')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('usage.metric', 'collections.{scope}.requests.read')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'getIndex')
|
||||
@@ -2634,8 +2565,6 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete')
|
||||
->label('audits.event', 'index.delete')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'collections.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'deleteIndex')
|
||||
@@ -2700,8 +2629,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
->label('scope', 'documents.write')
|
||||
->label('audits.event', 'document.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'documents.{scope}.requests.create')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
|
||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
@@ -2937,8 +2864,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
->desc('List Documents')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'documents.read')
|
||||
->label('usage.metric', 'documents.{scope}.requests.read')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listDocuments')
|
||||
@@ -3064,8 +2989,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
|
||||
->desc('Get Document')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'documents.read')
|
||||
->label('usage.metric', 'documents.{scope}.requests.read')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'getDocument')
|
||||
@@ -3159,8 +3082,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
|
||||
->desc('List Document Logs')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'documents.read')
|
||||
->label('usage.metric', 'documents.{scope}.requests.read')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listDocumentLogs')
|
||||
@@ -3263,8 +3184,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
|
||||
->label('scope', 'documents.write')
|
||||
->label('audits.event', 'document.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}')
|
||||
->label('usage.metric', 'documents.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
|
||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
@@ -3492,8 +3411,6 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].delete')
|
||||
->label('audits.event', 'document.delete')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{request.documentId}')
|
||||
->label('usage.metric', 'documents.{scope}.requests.delete')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
|
||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
@@ -3602,7 +3519,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
||||
|
||||
App::get('/v1/databases/usage')
|
||||
->desc('Get usage stats for the database')
|
||||
->groups(['api', 'database'])
|
||||
->groups(['api', 'database', 'usage'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'databases')
|
||||
@@ -3615,112 +3532,62 @@ App::get('/v1/databases/usage')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $range, Response $response, Database $dbForProject) {
|
||||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '1h',
|
||||
'limit' => 24,
|
||||
],
|
||||
'7d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 7,
|
||||
],
|
||||
'30d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 30,
|
||||
],
|
||||
'90d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
];
|
||||
$periods = Config::getParam('usage', []);
|
||||
$stats = $usage = [];
|
||||
$days = $periods[$range];
|
||||
$metrics = [
|
||||
METRIC_DATABASES,
|
||||
METRIC_COLLECTIONS,
|
||||
METRIC_DOCUMENTS,
|
||||
];
|
||||
|
||||
$metrics = [
|
||||
'databases.$all.count.total',
|
||||
'documents.$all.count.total',
|
||||
'collections.$all.count.total',
|
||||
'databases.$all.requests.create',
|
||||
'databases.$all.requests.read',
|
||||
'databases.$all.requests.update',
|
||||
'databases.$all.requests.delete',
|
||||
'collections.$all.requests.create',
|
||||
'collections.$all.requests.read',
|
||||
'collections.$all.requests.update',
|
||||
'collections.$all.requests.delete',
|
||||
'documents.$all.requests.create',
|
||||
'documents.$all.requests.read',
|
||||
'documents.$all.requests.update',
|
||||
'documents.$all.requests.delete'
|
||||
];
|
||||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'1h' => 3600,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
// Added 3'rd level to Index [period, metric, time] because of order by.
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $days['limit'];
|
||||
$period = $days['period'];
|
||||
$results = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
$stats[$metric] = [];
|
||||
foreach ($results as $result) {
|
||||
$stats[$metric][$result->getAttribute('time')] = [
|
||||
'value' => $result->getAttribute('value'),
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'databasesCount' => $stats['databases.$all.count.total'] ?? [],
|
||||
'documentsCount' => $stats['documents.$all.count.total'] ?? [],
|
||||
'collectionsCount' => $stats['collections.$all.count.total'] ?? [],
|
||||
'documentsCreate' => $stats['documents.$all.requests.create'] ?? [],
|
||||
'documentsRead' => $stats['documents.$all.requests.read'] ?? [],
|
||||
'documentsUpdate' => $stats['documents.$all.requests.update'] ?? [],
|
||||
'documentsDelete' => $stats['documents.$all.requests.delete'] ?? [],
|
||||
'collectionsCreate' => $stats['collections.$all.requests.create'] ?? [],
|
||||
'collectionsRead' => $stats['collections.$all.requests.read'] ?? [],
|
||||
'collectionsUpdate' => $stats['collections.$all.requests.update'] ?? [],
|
||||
'collectionsDelete' => $stats['collections.$all.requests.delete'] ?? [],
|
||||
'databasesCreate' => $stats['databases.$all.requests.create'] ?? [],
|
||||
'databasesRead' => $stats['databases.$all.requests.read'] ?? [],
|
||||
'databasesUpdate' => $stats['databases.$all.requests.update'] ?? [],
|
||||
'databasesDelete' => $stats['databases.$all.requests.delete'] ?? [],
|
||||
]);
|
||||
$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] = [];
|
||||
$leap = time() - ($days['limit'] * $days['factor']);
|
||||
while ($leap < time()) {
|
||||
$leap += $days['factor'];
|
||||
$formatDate = date($format, $leap);
|
||||
$usage[$metric][] = [
|
||||
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
|
||||
'date' => $formatDate,
|
||||
];
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_DATABASES);
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'range' => $range,
|
||||
'databasesTotal' => $usage[$metrics[0]],
|
||||
'collectionsTotal' => $usage[$metrics[1]],
|
||||
'documentsTotal' => $usage[$metrics[2]],
|
||||
]), Response::MODEL_USAGE_DATABASES);
|
||||
});
|
||||
|
||||
App::get('/v1/databases/:databaseId/usage')
|
||||
->desc('Get usage stats for the database')
|
||||
->groups(['api', 'database'])
|
||||
->groups(['api', 'database', 'usage'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'databases')
|
||||
@@ -3734,103 +3601,68 @@ App::get('/v1/databases/:databaseId/usage')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $databaseId, string $range, Response $response, Database $dbForProject) {
|
||||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '1h',
|
||||
'limit' => 24,
|
||||
],
|
||||
'7d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 7,
|
||||
],
|
||||
'30d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 30,
|
||||
],
|
||||
'90d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
];
|
||||
$database = $dbForProject->getDocument('databases', $databaseId);
|
||||
|
||||
$metrics = [
|
||||
'collections.' . $databaseId . '.count.total',
|
||||
'collections.' . $databaseId . '.requests.create',
|
||||
'collections.' . $databaseId . '.requests.read',
|
||||
'collections.' . $databaseId . '.requests.update',
|
||||
'collections.' . $databaseId . '.requests.delete',
|
||||
'documents.' . $databaseId . '.count.total',
|
||||
'documents.' . $databaseId . '.requests.create',
|
||||
'documents.' . $databaseId . '.requests.read',
|
||||
'documents.' . $databaseId . '.requests.update',
|
||||
'documents.' . $databaseId . '.requests.delete'
|
||||
];
|
||||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'1h' => 3600,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
// TODO@kodumbeats explore performance if query is ordered by time ASC
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'collectionsCount' => $stats["collections.{$databaseId}.count.total"] ?? [],
|
||||
'collectionsCreate' => $stats["collections.{$databaseId}.requests.create"] ?? [],
|
||||
'collectionsRead' => $stats["collections.{$databaseId}.requests.read"] ?? [],
|
||||
'collectionsUpdate' => $stats["collections.{$databaseId}.requests.update"] ?? [],
|
||||
'collectionsDelete' => $stats["collections.{$databaseId}.requests.delete"] ?? [],
|
||||
'documentsCount' => $stats["documents.{$databaseId}.count.total"] ?? [],
|
||||
'documentsCreate' => $stats["documents.{$databaseId}.requests.create"] ?? [],
|
||||
'documentsRead' => $stats["documents.{$databaseId}.requests.read"] ?? [],
|
||||
'documentsUpdate' => $stats["documents.{$databaseId}.requests.update"] ?? [],
|
||||
'documentsDelete' => $stats["documents.{$databaseId}.requests.delete"] ?? [],
|
||||
]);
|
||||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_DATABASE);
|
||||
$periods = Config::getParam('usage', []);
|
||||
$stats = $usage = [];
|
||||
$days = $periods[$range];
|
||||
$metrics = [
|
||||
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS),
|
||||
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS),
|
||||
];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $days['limit'];
|
||||
$period = $days['period'];
|
||||
$results = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
$stats[$metric] = [];
|
||||
foreach ($results as $result) {
|
||||
$stats[$metric][$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] = [];
|
||||
$leap = time() - ($days['limit'] * $days['factor']);
|
||||
while ($leap < time()) {
|
||||
$leap += $days['factor'];
|
||||
$formatDate = date($format, $leap);
|
||||
$usage[$metric][] = [
|
||||
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
|
||||
'date' => $formatDate,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'range' => $range,
|
||||
'collectionsTotal' => $usage[$metrics[0]],
|
||||
'documentsTotal' => $usage[$metrics[1]],
|
||||
]), Response::MODEL_USAGE_DATABASE);
|
||||
});
|
||||
|
||||
App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
|
||||
->alias('/v1/database/:collectionId/usage', ['databaseId' => 'default'])
|
||||
->desc('Get usage stats for a collection')
|
||||
->groups(['api', 'database'])
|
||||
->groups(['api', 'database', 'usage'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'databases')
|
||||
@@ -3853,84 +3685,52 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
|
||||
throw new Exception(Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '1h',
|
||||
'limit' => 24,
|
||||
],
|
||||
'7d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 7,
|
||||
],
|
||||
'30d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 30,
|
||||
],
|
||||
'90d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
];
|
||||
$periods = Config::getParam('usage', []);
|
||||
$stats = $usage = [];
|
||||
$days = $periods[$range];
|
||||
$metrics = [
|
||||
str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collectionDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS),
|
||||
];
|
||||
|
||||
$metrics = [
|
||||
"documents.{$databaseId}/{$collectionId}.count.total",
|
||||
"documents.{$databaseId}/{$collectionId}.requests.create",
|
||||
"documents.{$databaseId}/{$collectionId}.requests.read",
|
||||
"documents.{$databaseId}/{$collectionId}.requests.update",
|
||||
"documents.{$databaseId}/{$collectionId}.requests.delete",
|
||||
];
|
||||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'1h' => 3600,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $days['limit'];
|
||||
$period = $days['period'];
|
||||
$results = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
$stats[$metric] = [];
|
||||
foreach ($results as $result) {
|
||||
$stats[$metric][$result->getAttribute('time')] = [
|
||||
'value' => $result->getAttribute('value'),
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'documentsCount' => $stats["documents.{$databaseId}/{$collectionId}.count.total"] ?? [],
|
||||
'documentsCreate' => $stats["documents.{$databaseId}/{$collectionId}.requests.create"] ?? [],
|
||||
'documentsRead' => $stats["documents.{$databaseId}/{$collectionId}.requests.read"] ?? [],
|
||||
'documentsUpdate' => $stats["documents.{$databaseId}/{$collectionId}.requests.update"] ?? [],
|
||||
'documentsDelete' => $stats["documents.{$databaseId}/{$collectionId}.requests.delete" ?? []]
|
||||
]);
|
||||
$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] = [];
|
||||
$leap = time() - ($days['limit'] * $days['factor']);
|
||||
while ($leap < time()) {
|
||||
$leap += $days['factor'];
|
||||
$formatDate = date($format, $leap);
|
||||
$usage[$metric][] = [
|
||||
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
|
||||
'date' => $formatDate,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_COLLECTION);
|
||||
$response->dynamic(new Document([
|
||||
'range' => $range,
|
||||
'documentsTotal' => $usage[$metrics[0]],
|
||||
]), Response::MODEL_USAGE_COLLECTION);
|
||||
});
|
||||
|
||||
+121
-173
@@ -453,92 +453,66 @@ App::get('/v1/functions/:functionId/usage')
|
||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '1h',
|
||||
'limit' => 24,
|
||||
],
|
||||
'7d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 7,
|
||||
],
|
||||
'30d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 30,
|
||||
],
|
||||
'90d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
];
|
||||
$periods = Config::getParam('usage', []);
|
||||
$stats = $usage = [];
|
||||
$days = $periods[$range];
|
||||
$metrics = [
|
||||
str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $function->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS),
|
||||
str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $function->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE),
|
||||
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS),
|
||||
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE),
|
||||
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE),
|
||||
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS),
|
||||
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE),
|
||||
];
|
||||
|
||||
$metrics = [
|
||||
"executions.$functionId.compute.total",
|
||||
"executions.$functionId.compute.success",
|
||||
"executions.$functionId.compute.failure",
|
||||
"executions.$functionId.compute.time",
|
||||
"builds.$functionId.compute.total",
|
||||
"builds.$functionId.compute.success",
|
||||
"builds.$functionId.compute.failure",
|
||||
"builds.$functionId.compute.time",
|
||||
];
|
||||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'1h' => 3600,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $days['limit'];
|
||||
$period = $days['period'];
|
||||
$results = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
$stats[$metric] = [];
|
||||
foreach ($results as $result) {
|
||||
$stats[$metric][$result->getAttribute('time')] = [
|
||||
'value' => $result->getAttribute('value'),
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'executionsTotal' => $stats["executions.$functionId.compute.total"] ?? [],
|
||||
'executionsFailure' => $stats["executions.$functionId.compute.failure"] ?? [],
|
||||
'executionsSuccesse' => $stats["executions.$functionId.compute.success"] ?? [],
|
||||
'executionsTime' => $stats["executions.$functionId.compute.time"] ?? [],
|
||||
'buildsTotal' => $stats["builds.$functionId.compute.total"] ?? [],
|
||||
'buildsFailure' => $stats["builds.$functionId.compute.failure"] ?? [],
|
||||
'buildsSuccess' => $stats["builds.$functionId.compute.success"] ?? [],
|
||||
'buildsTime' => $stats["builds.$functionId.compute.time" ?? []]
|
||||
]);
|
||||
$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] = [];
|
||||
$leap = time() - ($days['limit'] * $days['factor']);
|
||||
while ($leap < time()) {
|
||||
$leap += $days['factor'];
|
||||
$formatDate = date($format, $leap);
|
||||
$usage[$metric][] = [
|
||||
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
|
||||
'date' => $formatDate,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_FUNCTION);
|
||||
$response->dynamic(new Document([
|
||||
'range' => $range,
|
||||
'deploymentsTotal' => $usage[$metrics[0]],
|
||||
'deploymentsStorage' => $usage[$metrics[1]],
|
||||
'buildsTotal' => $usage[$metrics[2]],
|
||||
'buildsStorage' => $usage[$metrics[3]],
|
||||
'buildsTime' => $usage[$metrics[4]],
|
||||
'executionsTotal' => $usage[$metrics[5]],
|
||||
'executionsTime' => $usage[$metrics[6]],
|
||||
]), Response::MODEL_USAGE_FUNCTION);
|
||||
});
|
||||
|
||||
App::get('/v1/functions/usage')
|
||||
@@ -556,92 +530,67 @@ App::get('/v1/functions/usage')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $range, Response $response, Database $dbForProject) {
|
||||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '1h',
|
||||
'limit' => 24,
|
||||
],
|
||||
'7d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 7,
|
||||
],
|
||||
'30d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 30,
|
||||
],
|
||||
'90d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
];
|
||||
$periods = Config::getParam('usage', []);
|
||||
$stats = $usage = [];
|
||||
$days = $periods[$range];
|
||||
$metrics = [
|
||||
METRIC_FUNCTIONS,
|
||||
METRIC_DEPLOYMENTS,
|
||||
METRIC_DEPLOYMENTS_STORAGE,
|
||||
METRIC_BUILDS,
|
||||
METRIC_BUILDS_STORAGE,
|
||||
METRIC_BUILDS_COMPUTE,
|
||||
METRIC_EXECUTIONS,
|
||||
METRIC_EXECUTIONS_COMPUTE,
|
||||
];
|
||||
|
||||
$metrics = [
|
||||
'executions.$all.compute.total',
|
||||
'executions.$all.compute.failure',
|
||||
'executions.$all.compute.success',
|
||||
'executions.$all.compute.time',
|
||||
'builds.$all.compute.total',
|
||||
'builds.$all.compute.failure',
|
||||
'builds.$all.compute.success',
|
||||
'builds.$all.compute.time',
|
||||
];
|
||||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'1h' => 3600,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $days['limit'];
|
||||
$period = $days['period'];
|
||||
$results = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
$stats[$metric] = [];
|
||||
foreach ($results as $result) {
|
||||
$stats[$metric][$result->getAttribute('time')] = [
|
||||
'value' => $result->getAttribute('value'),
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'executionsTotal' => $stats[$metrics[0]] ?? [],
|
||||
'executionsFailure' => $stats[$metrics[1]] ?? [],
|
||||
'executionsSuccess' => $stats[$metrics[2]] ?? [],
|
||||
'executionsTime' => $stats[$metrics[3]] ?? [],
|
||||
'buildsTotal' => $stats[$metrics[4]] ?? [],
|
||||
'buildsFailure' => $stats[$metrics[5]] ?? [],
|
||||
'buildsSuccess' => $stats[$metrics[6]] ?? [],
|
||||
'buildsTime' => $stats[$metrics[7]] ?? [],
|
||||
]);
|
||||
$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] = [];
|
||||
$leap = time() - ($days['limit'] * $days['factor']);
|
||||
while ($leap < time()) {
|
||||
$leap += $days['factor'];
|
||||
$formatDate = date($format, $leap);
|
||||
$usage[$metric][] = [
|
||||
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
|
||||
'date' => $formatDate,
|
||||
];
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_FUNCTIONS);
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'range' => $range,
|
||||
'functionsTotal' => $usage[$metrics[0]],
|
||||
'deploymentsTotal' => $usage[$metrics[1]],
|
||||
'deploymentsStorage' => $usage[$metrics[2]],
|
||||
'buildsTotal' => $usage[$metrics[3]],
|
||||
'buildsStorage' => $usage[$metrics[4]],
|
||||
'buildsTime' => $usage[$metrics[5]],
|
||||
'executionsTotal' => $usage[$metrics[6]],
|
||||
'executionsTime' => $usage[$metrics[7]],
|
||||
]), Response::MODEL_USAGE_FUNCTIONS);
|
||||
});
|
||||
|
||||
App::put('/v1/functions/:functionId')
|
||||
@@ -1506,11 +1455,11 @@ App::post('/v1/functions/:functionId/executions')
|
||||
->inject('dbForProject')
|
||||
->inject('user')
|
||||
->inject('events')
|
||||
->inject('usage')
|
||||
->inject('mode')
|
||||
->inject('queueForFunctions')
|
||||
->inject('geodb')
|
||||
->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $events, Stats $usage, string $mode, Func $queueForFunctions, Reader $geodb) {
|
||||
->inject('queueForUsage')
|
||||
->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $events, string $mode, Func $queueForFunctions, Reader $geodb, Usage $queueForUsage) {
|
||||
|
||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||
|
||||
@@ -1714,6 +1663,13 @@ App::post('/v1/functions/:functionId/executions')
|
||||
$execution->setAttribute('logs', $executionResponse['logs']);
|
||||
$execution->setAttribute('errors', $executionResponse['errors']);
|
||||
$execution->setAttribute('duration', $executionResponse['duration']);
|
||||
/**
|
||||
* Sync execution compute usage from
|
||||
*/
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($executionResponse['duration'] * 1000)) // per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($executionResponse['duration'] * 1000)) // per function
|
||||
;
|
||||
} catch (\Throwable $th) {
|
||||
$durationEnd = \microtime(true);
|
||||
|
||||
@@ -1730,14 +1686,6 @@ App::post('/v1/functions/:functionId/executions')
|
||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
|
||||
// TODO revise this later using route label
|
||||
$usage
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('executions.{scope}.compute', 1)
|
||||
->setParam('executionStatus', $execution->getAttribute('status', ''))
|
||||
->setParam('executionTime', $execution->getAttribute('duration')); // ms
|
||||
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
@@ -14,11 +14,10 @@ use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\Database\DateTime;
|
||||
|
||||
App::get('/v1/project/usage')
|
||||
->desc('Get usage stats for a project')
|
||||
->groups(['api'])
|
||||
->groups(['api', 'usage'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'project')
|
||||
@@ -30,94 +29,76 @@ App::get('/v1/project/usage')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $range, Response $response, Database $dbForProject) {
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '1h',
|
||||
'limit' => 24,
|
||||
],
|
||||
'7d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 7,
|
||||
],
|
||||
'30d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 30,
|
||||
],
|
||||
'90d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
];
|
||||
|
||||
$metrics = [
|
||||
'project.$all.network.requests',
|
||||
'project.$all.network.bandwidth',
|
||||
'project.$all.storage.size',
|
||||
'users.$all.count.total',
|
||||
'databases.$all.count.total',
|
||||
'documents.$all.count.total',
|
||||
'executions.$all.compute.total',
|
||||
'buckets.$all.count.total'
|
||||
];
|
||||
$periods = Config::getParam('usage', []);
|
||||
$stats = $usage = [];
|
||||
$days = $periods[$range];
|
||||
$metrics = [
|
||||
METRIC_NETWORK_REQUESTS,
|
||||
METRIC_NETWORK_INBOUND,
|
||||
METRIC_NETWORK_OUTBOUND,
|
||||
METRIC_EXECUTIONS,
|
||||
METRIC_DOCUMENTS,
|
||||
METRIC_DATABASES,
|
||||
METRIC_USERS,
|
||||
METRIC_BUCKETS,
|
||||
METRIC_FILES_STORAGE
|
||||
];
|
||||
|
||||
$stats = [];
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $days['limit'];
|
||||
$period = $days['period'];
|
||||
$results = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'1h' => 3600,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
$stats[$metric] = [];
|
||||
foreach ($results as $result) {
|
||||
$stats[$metric][$result->getAttribute('time')] = [
|
||||
'value' => $result->getAttribute('value'),
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'requests' => $stats[$metrics[0]] ?? [],
|
||||
'network' => $stats[$metrics[1]] ?? [],
|
||||
'storage' => $stats[$metrics[2]] ?? [],
|
||||
'users' => $stats[$metrics[3]] ?? [],
|
||||
'databases' => $stats[$metrics[4]] ?? [],
|
||||
'documents' => $stats[$metrics[5]] ?? [],
|
||||
'executions' => $stats[$metrics[6]] ?? [],
|
||||
'buckets' => $stats[$metrics[7]] ?? [],
|
||||
]);
|
||||
|
||||
$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] = [];
|
||||
$leap = time() - ($days['limit'] * $days['factor']);
|
||||
while ($leap < time()) {
|
||||
$leap += $days['factor'];
|
||||
$formatDate = date($format, $leap);
|
||||
$usage[$metric][] = [
|
||||
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
|
||||
'date' => $formatDate,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_PROJECT);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'range' => $range,
|
||||
'requestsTotal' => ($usage[$metrics[0]]),
|
||||
'network' => ($usage[$metrics[1]] + $usage[$metrics[2]]),
|
||||
'executionsTotal' => $usage[$metrics[3]],
|
||||
'documentsTotal' => $usage[$metrics[4]],
|
||||
'databasesTotal' => $usage[$metrics[5]],
|
||||
'usersTotal' => $usage[$metrics[6]],
|
||||
'bucketsTotal' => $usage[$metrics[7]],
|
||||
'filesStorage' => $usage[$metrics[8]],
|
||||
]), Response::MODEL_USAGE_PROJECT);
|
||||
});
|
||||
|
||||
|
||||
// Variables
|
||||
|
||||
App::post('/v1/project/variables')
|
||||
|
||||
+94
-188
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Hash\Sha;
|
||||
use Appwrite\ClamAV\Network;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
@@ -52,7 +51,6 @@ App::post('/v1/storage/buckets')
|
||||
->label('event', 'buckets.[bucketId].create')
|
||||
->label('audits.event', 'bucket.create')
|
||||
->label('audits.resource', 'bucket/{response.$id}')
|
||||
->label('usage.metric', 'buckets.{scope}.requests.create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'createBucket')
|
||||
@@ -148,7 +146,6 @@ App::get('/v1/storage/buckets')
|
||||
->desc('List buckets')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'buckets.read')
|
||||
->label('usage.metric', 'buckets.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'listBuckets')
|
||||
@@ -197,7 +194,6 @@ App::get('/v1/storage/buckets/:bucketId')
|
||||
->desc('Get Bucket')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'buckets.read')
|
||||
->label('usage.metric', 'buckets.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getBucket')
|
||||
@@ -226,7 +222,6 @@ App::put('/v1/storage/buckets/:bucketId')
|
||||
->label('event', 'buckets.[bucketId].update')
|
||||
->label('audits.event', 'bucket.update')
|
||||
->label('audits.resource', 'bucket/{response.$id}')
|
||||
->label('usage.metric', 'buckets.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'updateBucket')
|
||||
@@ -294,7 +289,6 @@ App::delete('/v1/storage/buckets/:bucketId')
|
||||
->label('audits.event', 'bucket.delete')
|
||||
->label('event', 'buckets.[bucketId].delete')
|
||||
->label('audits.resource', 'bucket/{request.bucketId}')
|
||||
->label('usage.metric', 'buckets.{scope}.requests.delete')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'deleteBucket')
|
||||
@@ -337,8 +331,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
->label('audits.event', 'file.create')
|
||||
->label('event', 'buckets.[bucketId].files.[fileId].create')
|
||||
->label('audits.resource', 'file/{response.$id}')
|
||||
->label('usage.metric', 'files.{scope}.requests.create')
|
||||
->label('usage.params', ['bucketId:{request.bucketId}'])
|
||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
@@ -688,8 +680,6 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('usage.metric', 'files.{scope}.requests.read')
|
||||
->label('usage.params', ['bucketId:{request.bucketId}'])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'listFiles')
|
||||
->label('sdk.description', '/docs/references/storage/list-files.md')
|
||||
@@ -769,8 +759,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('usage.metric', 'files.{scope}.requests.read')
|
||||
->label('usage.params', ['bucketId:{request.bucketId}'])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFile')
|
||||
->label('sdk.description', '/docs/references/storage/get-file.md')
|
||||
@@ -820,8 +808,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
->label('cache', true)
|
||||
->label('cache.resourceType', 'bucket/{request.bucketId}')
|
||||
->label('cache.resource', 'file/{request.fileId}')
|
||||
->label('usage.metric', 'files.{scope}.requests.read')
|
||||
->label('usage.params', ['bucketId:{request.bucketId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFilePreview')
|
||||
@@ -986,8 +972,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
->desc('Get File for Download')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('usage.metric', 'files.{scope}.requests.read')
|
||||
->label('usage.params', ['bucketId:{request.bucketId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFileDownload')
|
||||
@@ -1129,8 +1113,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
->desc('Get File for View')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('usage.metric', 'files.{scope}.requests.read')
|
||||
->label('usage.params', ['bucketId:{request.bucketId}'])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFileView')
|
||||
@@ -1285,8 +1267,6 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->label('event', 'buckets.[bucketId].files.[fileId].update')
|
||||
->label('audits.event', 'file.update')
|
||||
->label('audits.resource', 'file/{response.$id}')
|
||||
->label('usage.metric', 'files.{scope}.requests.update')
|
||||
->label('usage.params', ['bucketId:{request.bucketId}'])
|
||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
@@ -1394,8 +1374,6 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->label('event', 'buckets.[bucketId].files.[fileId].delete')
|
||||
->label('audits.event', 'file.delete')
|
||||
->label('audits.resource', 'file/{request.fileId}')
|
||||
->label('usage.metric', 'files.{scope}.requests.delete')
|
||||
->label('usage.params', ['bucketId:{request.bucketId}'])
|
||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
@@ -1500,103 +1478,62 @@ App::get('/v1/storage/usage')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $range, Response $response, Database $dbForProject) {
|
||||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') {
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '1h',
|
||||
'limit' => 24,
|
||||
],
|
||||
'7d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 7,
|
||||
],
|
||||
'30d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 30,
|
||||
],
|
||||
'90d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
];
|
||||
$periods = Config::getParam('usage', []);
|
||||
$stats = $usage = [];
|
||||
$days = $periods[$range];
|
||||
$metrics = [
|
||||
METRIC_BUCKETS,
|
||||
METRIC_FILES,
|
||||
METRIC_FILES_STORAGE,
|
||||
];
|
||||
|
||||
$metrics = [
|
||||
'project.$all.storage.size',
|
||||
'buckets.$all.count.total',
|
||||
'buckets.$all.requests.create',
|
||||
'buckets.$all.requests.read',
|
||||
'buckets.$all.requests.update',
|
||||
'buckets.$all.requests.delete',
|
||||
'files.$all.storage.size',
|
||||
'files.$all.count.total',
|
||||
'files.$all.requests.create',
|
||||
'files.$all.requests.read',
|
||||
'files.$all.requests.update',
|
||||
'files.$all.requests.delete',
|
||||
];
|
||||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'1h' => 3600,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $days['limit'];
|
||||
$period = $days['period'];
|
||||
$results = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
$stats[$metric] = [];
|
||||
foreach ($results as $result) {
|
||||
$stats[$metric][$result->getAttribute('time')] = [
|
||||
'value' => $result->getAttribute('value'),
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'bucketsCount' => $stats['buckets.$all.count.total'],
|
||||
'bucketsCreate' => $stats['buckets.$all.requests.create'],
|
||||
'bucketsRead' => $stats['buckets.$all.requests.read'],
|
||||
'bucketsUpdate' => $stats['buckets.$all.requests.update'],
|
||||
'bucketsDelete' => $stats['buckets.$all.requests.delete'],
|
||||
'storage' => $stats['project.$all.storage.size'],
|
||||
'filesCount' => $stats['files.$all.count.total'],
|
||||
'filesCreate' => $stats['files.$all.requests.create'],
|
||||
'filesRead' => $stats['files.$all.requests.read'],
|
||||
'filesUpdate' => $stats['files.$all.requests.update'],
|
||||
'filesDelete' => $stats['files.$all.requests.delete'],
|
||||
]);
|
||||
$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] = [];
|
||||
$leap = time() - ($days['limit'] * $days['factor']);
|
||||
while ($leap < time()) {
|
||||
$leap += $days['factor'];
|
||||
$formatDate = date($format, $leap);
|
||||
$usage[$metric][] = [
|
||||
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
|
||||
'date' => $formatDate,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_STORAGE);
|
||||
$response->dynamic(new Document([
|
||||
'range' => $range,
|
||||
'bucketsTotal' => $usage[$metrics[0]],
|
||||
'filesTotal' => $usage[$metrics[1]],
|
||||
'filesStorage' => $usage[$metrics[2]],
|
||||
]), Response::MODEL_USAGE_STORAGE);
|
||||
});
|
||||
|
||||
App::get('/v1/storage/:bucketId/usage')
|
||||
->desc('Get usage stats for a storage bucket')
|
||||
->desc('Get usage stats for storage bucket')
|
||||
->groups(['api', 'storage', 'usage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
@@ -1617,86 +1554,55 @@ App::get('/v1/storage/:bucketId/usage')
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') {
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '1h',
|
||||
'limit' => 24,
|
||||
],
|
||||
'7d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 7,
|
||||
],
|
||||
'30d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 30,
|
||||
],
|
||||
'90d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
];
|
||||
$periods = Config::getParam('usage', []);
|
||||
$stats = $usage = [];
|
||||
$days = $periods[$range];
|
||||
$metrics = [
|
||||
str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES),
|
||||
str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE),
|
||||
];
|
||||
|
||||
$metrics = [
|
||||
"files.{$bucketId}.count.total",
|
||||
"files.{$bucketId}.storage.size",
|
||||
"files.{$bucketId}.requests.create",
|
||||
"files.{$bucketId}.requests.read",
|
||||
"files.{$bucketId}.requests.update",
|
||||
"files.{$bucketId}.requests.delete",
|
||||
];
|
||||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'1h' => 3600,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $days['limit'];
|
||||
$period = $days['period'];
|
||||
$results = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
$stats[$metric] = [];
|
||||
foreach ($results as $result) {
|
||||
$stats[$metric][$result->getAttribute('time')] = [
|
||||
'value' => $result->getAttribute('value'),
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'filesCount' => $stats[$metrics[0]],
|
||||
'filesStorage' => $stats[$metrics[1]],
|
||||
'filesCreate' => $stats[$metrics[2]],
|
||||
'filesRead' => $stats[$metrics[3]],
|
||||
'filesUpdate' => $stats[$metrics[4]],
|
||||
'filesDelete' => $stats[$metrics[5]],
|
||||
]);
|
||||
$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] = [];
|
||||
$leap = time() - ($days['limit'] * $days['factor']);
|
||||
|
||||
while ($leap < time()) {
|
||||
$leap += $days['factor'];
|
||||
$formatDate = date($format, $leap);
|
||||
$usage[$metric][] = [
|
||||
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
|
||||
'date' => $formatDate,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_BUCKETS);
|
||||
$response->dynamic(new Document([
|
||||
'range' => $range,
|
||||
'filesTotal' => $usage[$metrics[0]],
|
||||
'filesStorage' => $usage[$metrics[1]],
|
||||
]), Response::MODEL_USAGE_BUCKETS);
|
||||
});
|
||||
|
||||
@@ -115,7 +115,6 @@ App::post('/v1/users')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'user.create')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'create')
|
||||
|
||||
+132
-69
@@ -8,6 +8,7 @@ use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Mail;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Event\Usage;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Usage\Stats;
|
||||
use Appwrite\Utopia\Response;
|
||||
@@ -48,43 +49,99 @@ $parseLabel = function (string $label, array $responsePayload, array $requestPar
|
||||
return $label;
|
||||
};
|
||||
|
||||
$databaseListener = function (string $event, Document $document, Stats $usage) {
|
||||
$multiplier = 1;
|
||||
$databaseListener = function (string $event, Document $document, Document $project, Usage $queueForUsage, Database $dbForProject) {
|
||||
|
||||
$value = 1;
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
$multiplier = -1;
|
||||
$value = -1;
|
||||
}
|
||||
|
||||
$collection = $document->getCollection();
|
||||
switch ($collection) {
|
||||
case 'users':
|
||||
$usage->setParam('users.{scope}.count.total', 1 * $multiplier);
|
||||
switch (true) {
|
||||
case $document->getCollection() === 'teams':
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_TEAMS, $value); // per project
|
||||
break;
|
||||
case 'databases':
|
||||
$usage->setParam('databases.{scope}.count.total', 1 * $multiplier);
|
||||
case $document->getCollection() === 'users':
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_USERS, $value); // per project
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
$queueForUsage
|
||||
->addReduce($document);
|
||||
}
|
||||
break;
|
||||
case 'buckets':
|
||||
$usage->setParam('buckets.{scope}.count.total', 1 * $multiplier);
|
||||
case $document->getCollection() === 'sessions': // sessions
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_SESSIONS, $value); //per project
|
||||
break;
|
||||
case 'deployments':
|
||||
$usage->setParam('deployments.{scope}.storage.size', $document->getAttribute('size') * $multiplier);
|
||||
case $document->getCollection() === 'databases': // databases
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_DATABASES, $value); // per project
|
||||
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
$queueForUsage
|
||||
->addReduce($document);
|
||||
}
|
||||
break;
|
||||
case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections
|
||||
$parts = explode('_', $document->getCollection());
|
||||
$databaseInternalId = $parts[1] ?? 0;
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_COLLECTIONS, $value) // per project
|
||||
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value) // per database
|
||||
;
|
||||
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
$queueForUsage
|
||||
->addReduce($document);
|
||||
}
|
||||
break;
|
||||
case str_starts_with($document->getCollection(), 'database_') && str_contains($document->getCollection(), '_collection_'): //documents
|
||||
$parts = explode('_', $document->getCollection());
|
||||
$databaseInternalId = $parts[1] ?? 0;
|
||||
$collectionInternalId = $parts[3] ?? 0;
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_DOCUMENTS, $value) // per project
|
||||
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_DOCUMENTS), $value) // per database
|
||||
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $collectionInternalId], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), $value); // per collection
|
||||
break;
|
||||
case $document->getCollection() === 'buckets': //buckets
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_BUCKETS, $value); // per project
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
$queueForUsage
|
||||
->addReduce($document);
|
||||
}
|
||||
break;
|
||||
case str_starts_with($document->getCollection(), 'bucket_'): // files
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_FILES, $value) // per project
|
||||
->addMetric(METRIC_FILES_STORAGE, $document->getAttribute('sizeOriginal') * $value) // per project
|
||||
->addMetric(str_replace('{bucketInternalId}', $document->getAttribute('bucketInternalId'), METRIC_BUCKET_ID_FILES), $value) // per bucket
|
||||
->addMetric(str_replace('{bucketInternalId}', $document->getAttribute('bucketInternalId'), METRIC_BUCKET_ID_FILES_STORAGE), $document->getAttribute('sizeOriginal') * $value); // per bucket
|
||||
break;
|
||||
case $document->getCollection() === 'functions':
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_FUNCTIONS, $value); // per project
|
||||
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
$queueForUsage
|
||||
->addReduce($document);
|
||||
}
|
||||
break;
|
||||
case $document->getCollection() === 'deployments':
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_DEPLOYMENTS, $value) // per project
|
||||
->addMetric(METRIC_DEPLOYMENTS_STORAGE, $document->getAttribute('size') * $value) // per project
|
||||
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS), $value)// per function
|
||||
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE), $document->getAttribute('size') * $value);// per function
|
||||
|
||||
break;
|
||||
case $document->getCollection() === 'executions':
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_EXECUTIONS, $value) // per project
|
||||
->addMetric(str_replace('{functionInternalId}', $document->getAttribute('functionInternalId'), METRIC_FUNCTION_ID_EXECUTIONS), $value);// per function
|
||||
break;
|
||||
default:
|
||||
if (strpos($collection, 'bucket_') === 0) {
|
||||
$usage
|
||||
->setParam('bucketId', $document->getAttribute('bucketId'))
|
||||
->setParam('files.{scope}.storage.size', $document->getAttribute('sizeOriginal') * $multiplier)
|
||||
->setParam('files.{scope}.count.total', 1 * $multiplier);
|
||||
} elseif (strpos($collection, 'database_') === 0) {
|
||||
$usage
|
||||
->setParam('databaseId', $document->getAttribute('databaseId'));
|
||||
if (strpos($collection, '_collection_') !== false) {
|
||||
$usage
|
||||
->setParam('collectionId', $document->getAttribute('$collectionId'))
|
||||
->setParam('documents.{scope}.count.total', 1 * $multiplier);
|
||||
} else {
|
||||
$usage->setParam('collections.{scope}.count.total', 1 * $multiplier);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
@@ -101,10 +158,10 @@ App::init()
|
||||
->inject('deletes')
|
||||
->inject('database')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForUsage')
|
||||
->inject('mode')
|
||||
->inject('mails')
|
||||
->inject('usage')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Delete $deletes, EventDatabase $database, Database $dbForProject, string $mode, Mail $mails, Stats $usage) use ($databaseListener) {
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Delete $deletes, EventDatabase $database, Database $dbForProject, Usage $queueForUsage, string $mode, Mail $mails) use ($databaseListener) {
|
||||
|
||||
$route = $utopia->getRoute();
|
||||
|
||||
@@ -186,19 +243,14 @@ App::init()
|
||||
->setProject($project)
|
||||
->setUser($user);
|
||||
|
||||
$usage
|
||||
->setParam('projectInternalId', $project->getInternalId())
|
||||
->setParam('projectId', $project->getId())
|
||||
->setParam('project.{scope}.network.requests', 1)
|
||||
->setParam('httpMethod', $request->getMethod())
|
||||
->setParam('project.{scope}.network.inbound', 0)
|
||||
->setParam('project.{scope}.network.outbound', 0);
|
||||
|
||||
$deletes->setProject($project);
|
||||
$database->setProject($project);
|
||||
|
||||
$dbForProject->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, Document $document) => $databaseListener($event, $document, $usage));
|
||||
$dbForProject->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, Document $document) => $databaseListener($event, $document, $usage));
|
||||
$calculateUsage = fn ($event, Document $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject);
|
||||
|
||||
$dbForProject
|
||||
->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', $calculateUsage)
|
||||
->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', $calculateUsage);
|
||||
|
||||
$useCache = $route->getLabel('cache', false);
|
||||
|
||||
@@ -364,14 +416,14 @@ App::shutdown()
|
||||
->inject('user')
|
||||
->inject('events')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('deletes')
|
||||
->inject('database')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForFunctions')
|
||||
->inject('queueForUsage')
|
||||
->inject('mode')
|
||||
->inject('dbForConsole')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Stats $usage, Delete $deletes, EventDatabase $database, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole) use ($parseLabel) {
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Delete $deletes, EventDatabase $database, Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage, string $mode, Database $dbForConsole) use ($parseLabel) {
|
||||
|
||||
$responsePayload = $response->getPayload();
|
||||
|
||||
@@ -524,35 +576,46 @@ App::shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled'
|
||||
&& $project->getId()
|
||||
&& !empty($route->getLabel('sdk.namespace', null))
|
||||
) { // Don't calculate console usage on admin mode
|
||||
$metric = $route->getLabel('usage.metric', '');
|
||||
$usageParams = $route->getLabel('usage.params', []);
|
||||
if ($project->getId() !== 'console') {
|
||||
if ($mode !== APP_MODE_ADMIN) {
|
||||
$fileSize = 0;
|
||||
$file = $request->getFiles('file');
|
||||
if (!empty($file)) {
|
||||
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
|
||||
}
|
||||
|
||||
if (!empty($metric)) {
|
||||
$usage->setParam($metric, 1);
|
||||
foreach ($usageParams as $param) {
|
||||
$param = $parseLabel($param, $responsePayload, $requestParams, $user);
|
||||
$parts = explode(':', $param);
|
||||
if (count($parts) != 2) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Usage params not properly set');
|
||||
}
|
||||
$usage->setParam($parts[0], $parts[1]);
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_NETWORK_REQUESTS, 1)
|
||||
->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize)
|
||||
->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize());
|
||||
}
|
||||
|
||||
$queueForUsage
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user last activity
|
||||
*/
|
||||
if (!$user->isEmpty()) {
|
||||
$accessedAt = $user->getAttribute('accessedAt', '');
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCCESS)) > $accessedAt) {
|
||||
$user->setAttribute('accessedAt', DateTime::now());
|
||||
|
||||
if (APP_MODE_ADMIN !== $mode) {
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
} else {
|
||||
$dbForConsole->updateDocument('users', $user->getId(), $user);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$fileSize = 0;
|
||||
$file = $request->getFiles('file');
|
||||
if (!empty($file)) {
|
||||
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
|
||||
}
|
||||
|
||||
$usage
|
||||
->setParam('project.{scope}.network.inbound', $request->getSize() + $fileSize)
|
||||
->setParam('project.{scope}.network.outbound', $response->getSize())
|
||||
->submit();
|
||||
App::init()
|
||||
->groups(['usage'])
|
||||
->action(function () {
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') {
|
||||
throw new Exception(Exception::GENERAL_USAGE_DISABLED);
|
||||
}
|
||||
});
|
||||
|
||||
+4
-29
@@ -32,7 +32,6 @@ use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Appwrite\URL\URL as AppwriteURL;
|
||||
use Appwrite\Usage\Stats;
|
||||
use Utopia\App;
|
||||
use Utopia\Logger\Logger;
|
||||
use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||
@@ -237,6 +236,7 @@ Config::load('providers', __DIR__ . '/config/providers.php');
|
||||
Config::load('platforms', __DIR__ . '/config/platforms.php');
|
||||
Config::load('collections', __DIR__ . '/config/collections.php');
|
||||
Config::load('runtimes', __DIR__ . '/config/runtimes.php');
|
||||
Config::load('usage', __DIR__ . '/config/usage.php');
|
||||
Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes
|
||||
Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes
|
||||
Config::load('services', __DIR__ . '/config/services.php'); // List of services
|
||||
@@ -771,31 +771,6 @@ $register->set('pools', function () {
|
||||
return $group;
|
||||
});
|
||||
|
||||
$register->set('influxdb', function () {
|
||||
|
||||
// Register DB connection
|
||||
$host = App::getEnv('_APP_INFLUXDB_HOST', '');
|
||||
$port = App::getEnv('_APP_INFLUXDB_PORT', '');
|
||||
|
||||
if (empty($host) || empty($port)) {
|
||||
return;
|
||||
}
|
||||
$driver = new InfluxDB\Driver\Curl("http://{$host}:{$port}");
|
||||
$client = new InfluxDB\Client($host, $port, '', '', false, false, 5);
|
||||
$client->setDriver($driver);
|
||||
|
||||
return $client;
|
||||
});
|
||||
$register->set('statsd', function () {
|
||||
// Register DB connection
|
||||
$host = App::getEnv('_APP_STATSD_HOST', 'telegraf');
|
||||
$port = App::getEnv('_APP_STATSD_PORT', 8125);
|
||||
|
||||
$connection = new \Domnikl\Statsd\Connection\UdpSocket($host, $port);
|
||||
$statsd = new \Domnikl\Statsd\Client($connection);
|
||||
|
||||
return $statsd;
|
||||
});
|
||||
$register->set('smtp', function () {
|
||||
$mail = new PHPMailer(true);
|
||||
|
||||
@@ -899,9 +874,9 @@ App::setResource('queue', function (Group $pools) {
|
||||
App::setResource('queueForFunctions', function (Connection $queue) {
|
||||
return new Func($queue);
|
||||
}, ['queue']);
|
||||
App::setResource('usage', function ($register) {
|
||||
return new Stats($register->get('statsd'));
|
||||
}, ['register']);
|
||||
App::setResource('queueForUsage', function (Connection $queue) {
|
||||
return new Usage($queue);
|
||||
}, ['queue']);
|
||||
App::setResource('clients', function ($request, $console, $project) {
|
||||
$console->setAttribute('platforms', [ // Always allow current host
|
||||
'$collection' => ID::custom('platforms'),
|
||||
|
||||
@@ -71,7 +71,6 @@ services:
|
||||
- mariadb
|
||||
- redis
|
||||
# - clamav
|
||||
- influxdb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
@@ -104,8 +103,6 @@ services:
|
||||
- _APP_SMTP_USERNAME
|
||||
- _APP_SMTP_PASSWORD
|
||||
- _APP_USAGE_STATS
|
||||
- _APP_INFLUXDB_HOST
|
||||
- _APP_INFLUXDB_PORT
|
||||
- _APP_STORAGE_LIMIT
|
||||
- _APP_STORAGE_PREVIEW_LIMIT
|
||||
- _APP_STORAGE_ANTIVIRUS
|
||||
@@ -142,8 +139,6 @@ services:
|
||||
- _APP_EXECUTOR_HOST
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_STATSD_HOST
|
||||
- _APP_STATSD_PORT
|
||||
- _APP_MAINTENANCE_INTERVAL
|
||||
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||
@@ -534,37 +529,6 @@ services:
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
|
||||
appwrite-usage:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
entrypoint: usage
|
||||
container_name: appwrite-usage
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
depends_on:
|
||||
- influxdb
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_INFLUXDB_HOST
|
||||
- _APP_INFLUXDB_PORT
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
appwrite-schedule:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
@@ -676,26 +640,41 @@ services:
|
||||
# volumes:
|
||||
# - appwrite-uploads:/storage/uploads
|
||||
|
||||
influxdb:
|
||||
image: appwrite/influxdb:1.5.0
|
||||
container_name: appwrite-influxdb
|
||||
appwrite-worker-usage:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
entrypoint: worker-usage
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
container_name: appwrite-worker-usage
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- appwrite-influxdb:/var/lib/influxdb:rw
|
||||
|
||||
telegraf:
|
||||
image: appwrite/telegraf:1.4.0
|
||||
container_name: appwrite-telegraf
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_INFLUXDB_HOST
|
||||
- _APP_INFLUXDB_PORT
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_CONNECTIONS_MAX
|
||||
- _APP_POOL_CLIENTS
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_CONNECTIONS_DB_CONSOLE
|
||||
- _APP_CONNECTIONS_DB_PROJECT
|
||||
- _APP_CONNECTIONS_CACHE
|
||||
- _APP_CONNECTIONS_QUEUE
|
||||
- _APP_USAGE_STATS
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
networks:
|
||||
gateway:
|
||||
@@ -713,5 +692,4 @@ volumes:
|
||||
appwrite-certificates:
|
||||
appwrite-functions:
|
||||
appwrite-builds:
|
||||
appwrite-influxdb:
|
||||
appwrite-config:
|
||||
|
||||
+10
-5
@@ -4,7 +4,6 @@ require_once __DIR__ . '/init.php';
|
||||
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Usage;
|
||||
use Appwrite\Usage\Stats;
|
||||
use Swoole\Runtime;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
@@ -89,16 +88,22 @@ Server::setResource('queueForFunctions', function (Registry $register) {
|
||||
);
|
||||
}, ['register']);
|
||||
|
||||
Server::setResource('queueForUsage', function (Registry $register) {
|
||||
$pools = $register->get('pools');
|
||||
return new Usage(
|
||||
$pools
|
||||
->get('queue')
|
||||
->pop()
|
||||
->getResource()
|
||||
);
|
||||
}, ['register']);
|
||||
|
||||
Server::setResource('log', fn() => new Log());
|
||||
|
||||
Server::setResource('logger', function ($register) {
|
||||
return $register->get('logger');
|
||||
}, ['register']);
|
||||
|
||||
Server::setResource('statsd', function ($register) {
|
||||
return $register->get('statsd');
|
||||
}, ['register']);
|
||||
|
||||
Server::setResource('pools', function ($register) {
|
||||
return $register->get('pools');
|
||||
}, ['register']);
|
||||
|
||||
+12
-16
@@ -490,23 +490,19 @@ class BuildsV1 extends Worker
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles']
|
||||
);
|
||||
|
||||
/** Update usage stats */
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') {
|
||||
$statsd = $register->get('statsd');
|
||||
$usage = new Stats($statsd);
|
||||
$usage
|
||||
->setParam('projectInternalId', $project->getInternalId())
|
||||
->setParam('projectId', $project->getId())
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('builds.{scope}.compute', 1)
|
||||
->setParam('buildStatus', $build->getAttribute('status', ''))
|
||||
->setParam('buildTime', $build->getAttribute('duration'))
|
||||
->setParam('networkRequestSize', 0)
|
||||
->setParam('networkResponseSize', 0)
|
||||
->submit();
|
||||
}
|
||||
}
|
||||
|
||||
/** Trigger usage queue */
|
||||
$this
|
||||
->getUsageQueue()
|
||||
->setProject($project)
|
||||
->addMetric(METRIC_BUILDS, 1) // per project
|
||||
->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0))
|
||||
->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1) // per function
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
protected function runGitAction(string $status, GitHub $github, string $providerCommitHash, string $owner, string $repositoryName, Document $project, Document $function, string $deploymentId, Database $dbForProject, Database $dbForConsole): void
|
||||
|
||||
+24
-22
@@ -2,12 +2,11 @@
|
||||
|
||||
require_once __DIR__ . '/../worker.php';
|
||||
|
||||
use Domnikl\Statsd\Client;
|
||||
use Appwrite\Event\Usage;
|
||||
use Utopia\Queue\Message;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Usage\Stats;
|
||||
use Appwrite\Utopia\Response\Model\Execution;
|
||||
use Executor\Executor;
|
||||
use Utopia\App;
|
||||
@@ -31,7 +30,7 @@ Server::setResource('execute', function () {
|
||||
Log $log,
|
||||
Func $queueForFunctions,
|
||||
Database $dbForProject,
|
||||
Client $statsd,
|
||||
Usage $queueForUsage,
|
||||
Document $project,
|
||||
Document $function,
|
||||
string $trigger,
|
||||
@@ -47,6 +46,7 @@ Server::setResource('execute', function () {
|
||||
) {
|
||||
$user ??= new Document();
|
||||
$functionId = $function->getId();
|
||||
$functionInternalId = $function->getInternalId();
|
||||
$deploymentId = $function->getAttribute('deployment', '');
|
||||
|
||||
$log->addTag('functionId', $functionId);
|
||||
@@ -54,6 +54,7 @@ Server::setResource('execute', function () {
|
||||
|
||||
/** Check if deployment exists */
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
$deploymentInternalId = $deployment->getInternalId();
|
||||
|
||||
if ($deployment->getAttribute('resourceId') !== $functionId) {
|
||||
throw new Exception('Deployment not found. Create deployment before trying to execute a function');
|
||||
@@ -127,6 +128,14 @@ Server::setResource('execute', function () {
|
||||
if ($execution->isEmpty()) {
|
||||
throw new Exception('Failed to create or read execution');
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage
|
||||
*/
|
||||
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_EXECUTIONS, 1) // per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1); // per function
|
||||
}
|
||||
|
||||
if ($execution->getAttribute('status') !== 'processing') {
|
||||
@@ -265,20 +274,13 @@ Server::setResource('execute', function () {
|
||||
roles: $target['roles']
|
||||
);
|
||||
|
||||
/** Update usage stats */
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') {
|
||||
$usage = new Stats($statsd);
|
||||
$usage
|
||||
->setParam('projectId', $project->getId())
|
||||
->setParam('projectInternalId', $project->getInternalId())
|
||||
->setParam('functionId', $function->getId()) // TODO: We should use functionInternalId in usage stats
|
||||
->setParam('executions.{scope}.compute', 1)
|
||||
->setParam('executionStatus', $execution->getAttribute('status', ''))
|
||||
->setParam('executionTime', $execution->getAttribute('duration'))
|
||||
->setParam('networkRequestSize', 0)
|
||||
->setParam('networkResponseSize', 0)
|
||||
->submit();
|
||||
}
|
||||
/** Trigger usage queue */
|
||||
$queueForUsage
|
||||
->setProject($project)
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000))// per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000))
|
||||
->trigger()
|
||||
;
|
||||
|
||||
if (!empty($error)) {
|
||||
throw new Exception($error, $errorCode);
|
||||
@@ -290,10 +292,10 @@ $server->job()
|
||||
->inject('message')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForFunctions')
|
||||
->inject('statsd')
|
||||
->inject('queueForUsage')
|
||||
->inject('execute')
|
||||
->inject('log')
|
||||
->action(function (Message $message, Database $dbForProject, Func $queueForFunctions, Client $statsd, callable $execute, Log $log) {
|
||||
->action(function (Message $message, Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage, callable $execute, Log $log) {
|
||||
$payload = $message->getPayload() ?? [];
|
||||
|
||||
if (empty($payload)) {
|
||||
@@ -335,7 +337,7 @@ $server->job()
|
||||
}
|
||||
Console::success('Iterating function: ' . $function->getAttribute('name'));
|
||||
$execute(
|
||||
statsd: $statsd,
|
||||
queueForUsage: $queueForUsage,
|
||||
dbForProject: $dbForProject,
|
||||
project: $project,
|
||||
function: $function,
|
||||
@@ -383,7 +385,7 @@ $server->job()
|
||||
path: $payload['path'],
|
||||
method: $payload['method'],
|
||||
headers: $payload['headers'],
|
||||
statsd: $statsd,
|
||||
queueForUsage: $queueForUsage,
|
||||
);
|
||||
break;
|
||||
case 'schedule':
|
||||
@@ -403,7 +405,7 @@ $server->job()
|
||||
path: $payload['path'],
|
||||
method: $payload['method'],
|
||||
headers: $payload['headers'],
|
||||
statsd: $statsd,
|
||||
queueForUsage: $queueForUsage,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../worker.php';
|
||||
|
||||
use Swoole\Timer;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Queue\Message;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Queue\Server;
|
||||
use Utopia\Registry\Registry;
|
||||
|
||||
Authorization::disable();
|
||||
Authorization::setDefaultStatus(false);
|
||||
|
||||
$stats = [];
|
||||
|
||||
$periods['1h'] = 'Y-m-d H:00';
|
||||
$periods['1d'] = 'Y-m-d 00:00';
|
||||
//$periods['1m'] = 'Y-m-1 00:00';
|
||||
$periods['inf'] = '0000-00-00 00:00';
|
||||
|
||||
const INFINITY_PERIOD = '_inf_';
|
||||
|
||||
/**
|
||||
* On Documents that tied by relations like functions>deployments>build || documents>collection>database || buckets>files.
|
||||
* When we remove a parent document we need to deduct his children aggregation from the project scope.
|
||||
*/
|
||||
Server::setResource('reduce', function (Cache $cache, Registry $register, $pools) {
|
||||
return function ($database, $projectInternalId, Document $document, array &$metrics) use ($pools, $cache, $register): void {
|
||||
try {
|
||||
$dbForProject = new Database(
|
||||
$pools
|
||||
->get($database)
|
||||
->pop()
|
||||
->getResource(),
|
||||
$cache
|
||||
);
|
||||
|
||||
$dbForProject->setNamespace('_' . $projectInternalId);
|
||||
|
||||
switch (true) {
|
||||
case $document->getCollection() === 'users': // users
|
||||
$sessions = count($document->getAttribute(METRIC_SESSIONS, 0));
|
||||
if (!empty($sessions)) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_SESSIONS,
|
||||
'value' => ($sessions * -1),
|
||||
];
|
||||
}
|
||||
break;
|
||||
case $document->getCollection() === 'databases': // databases
|
||||
$collections = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS)));
|
||||
$documents = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS)));
|
||||
if (!empty($collections['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_COLLECTIONS,
|
||||
'value' => ($collections['value'] * -1),
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($documents['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_DOCUMENTS,
|
||||
'value' => ($documents['value'] * -1),
|
||||
];
|
||||
}
|
||||
break;
|
||||
case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections
|
||||
$parts = explode('_', $document->getCollection());
|
||||
$databaseInternalId = $parts[1] ?? 0;
|
||||
$documents = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS)));
|
||||
|
||||
if (!empty($documents['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_DOCUMENTS,
|
||||
'value' => ($documents['value'] * -1),
|
||||
];
|
||||
$metrics[] = [
|
||||
'key' => str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_DOCUMENTS),
|
||||
'value' => ($documents['value'] * -1),
|
||||
];
|
||||
}
|
||||
break;
|
||||
|
||||
case $document->getCollection() === 'buckets':
|
||||
$files = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES)));
|
||||
$storage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE)));
|
||||
|
||||
if (!empty($files['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_FILES,
|
||||
'value' => ($files['value'] * -1),
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($storage['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_FILES_STORAGE,
|
||||
'value' => ($storage['value'] * -1),
|
||||
];
|
||||
}
|
||||
break;
|
||||
|
||||
case $document->getCollection() === 'functions':
|
||||
$deployments = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS)));
|
||||
$deploymentsStorage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE)));
|
||||
$builds = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS)));
|
||||
$buildsStorage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE)));
|
||||
$buildsCompute = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE)));
|
||||
$executions = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS)));
|
||||
$executionsCompute = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE)));
|
||||
|
||||
if (!empty($deployments['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_DEPLOYMENTS,
|
||||
'value' => ($deployments['value'] * -1),
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($deploymentsStorage['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_DEPLOYMENTS_STORAGE,
|
||||
'value' => ($deploymentsStorage['value'] * -1),
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($builds['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_BUILDS,
|
||||
'value' => ($builds['value'] * -1),
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($buildsStorage['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_BUILDS_STORAGE,
|
||||
'value' => ($buildsStorage['value'] * -1),
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($buildsCompute['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_BUILDS_COMPUTE,
|
||||
'value' => ($buildsCompute['value'] * -1),
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($executions['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_EXECUTIONS,
|
||||
'value' => ($executions['value'] * -1),
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($executionsCompute['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_EXECUTIONS_COMPUTE,
|
||||
'value' => ($executionsCompute['value'] * -1),
|
||||
];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
console::error("[reducer] " . " {DateTime::now()} " . " {$projectInternalId} " . " {$e->getMessage()}");
|
||||
} finally {
|
||||
$pools->reclaim();
|
||||
}
|
||||
};
|
||||
}, ['cache', 'register', 'pools']);
|
||||
|
||||
|
||||
$server->job()
|
||||
->inject('message')
|
||||
->inject('reduce')
|
||||
|
||||
->action(function (Message $message, callable $reduce) use (&$stats) {
|
||||
|
||||
$payload = $message->getPayload() ?? [];
|
||||
$project = new Document($payload['project'] ?? []);
|
||||
$projectId = $project->getInternalId();
|
||||
foreach ($payload['reduce'] ?? [] as $document) {
|
||||
if (empty($document)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$reduce(
|
||||
database: $project->getAttribute('database'),
|
||||
projectInternalId: $project->getInternalId(),
|
||||
document: new Document($document),
|
||||
metrics: $payload['metrics'],
|
||||
);
|
||||
}
|
||||
|
||||
$stats[$projectId]['database'] = $project->getAttribute('database');
|
||||
foreach ($payload['metrics'] ?? [] as $metric) {
|
||||
if (!isset($stats[$projectId]['keys'][$metric['key']])) {
|
||||
$stats[$projectId]['keys'][$metric['key']] = $metric['value'];
|
||||
continue;
|
||||
}
|
||||
$stats[$projectId]['keys'][$metric['key']] += $metric['value'];
|
||||
}
|
||||
});
|
||||
|
||||
$server
|
||||
->workerStart()
|
||||
->inject('register')
|
||||
->inject('cache')
|
||||
->inject('pools')
|
||||
->action(function ($register, $cache, $pools) use ($periods, &$stats) {
|
||||
Timer::tick(30000, function () use ($register, $cache, $pools, $periods, &$stats) {
|
||||
|
||||
$offset = count($stats);
|
||||
$projects = array_slice($stats, 0, $offset, true);
|
||||
array_splice($stats, 0, $offset);
|
||||
|
||||
foreach ($projects as $projectInternalId => $project) {
|
||||
try {
|
||||
$dbForProject = new Database(
|
||||
$pools
|
||||
->get($project['database'])
|
||||
->pop()
|
||||
->getResource(),
|
||||
$cache
|
||||
);
|
||||
|
||||
$dbForProject->setNamespace('_' . $projectInternalId);
|
||||
|
||||
foreach ($project['keys'] ?? [] as $key => $value) {
|
||||
if ($value == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($periods as $period => $format) {
|
||||
$time = 'inf' === $period ? null : date($format, time());
|
||||
$id = \md5("{$time}_{$period}_{$key}");
|
||||
|
||||
try {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'period' => $period,
|
||||
'time' => $time,
|
||||
'metric' => $key,
|
||||
'value' => $value,
|
||||
'region' => App::getEnv('_APP_REGION', 'default'),
|
||||
]));
|
||||
} catch (Duplicate $th) {
|
||||
if ($value < 0) {
|
||||
$dbForProject->decreaseDocumentAttribute(
|
||||
'stats',
|
||||
$id,
|
||||
'value',
|
||||
abs($value)
|
||||
);
|
||||
} else {
|
||||
$dbForProject->increaseDocumentAttribute(
|
||||
'stats',
|
||||
$id,
|
||||
'value',
|
||||
$value
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($project['keys'])) {
|
||||
$dbForProject->createDocument('statsLogger', new Document([
|
||||
'time' => DateTime::now(),
|
||||
'metrics' => $project['keys'],
|
||||
]));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$now = DateTime::now();
|
||||
console::error("[Error] " . " Time: {$now} " . " projectInternalId: {$projectInternalId}" . " File: {$e->getFile()}" . " Line: {$e->getLine()} " . " message: {$e->getMessage()}");
|
||||
} finally {
|
||||
$pools->reclaim();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
$server->start();
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
QUEUE=v1-usage php /usr/src/code/app/workers/usage.php $@
|
||||
@@ -71,7 +71,6 @@
|
||||
"resque/php-resque": "1.3.6",
|
||||
"matomo/device-detector": "6.1.*",
|
||||
"dragonmantank/cron-expression": "3.3.2",
|
||||
"influxdb/influxdb-php": "1.15.2",
|
||||
"phpmailer/phpmailer": "6.8.0",
|
||||
"chillerlan/php-qrcode": "4.3.4",
|
||||
"adhocore/jwt": "1.1.2",
|
||||
|
||||
Generated
-6100
File diff suppressed because it is too large
Load Diff
+10
-54
@@ -125,8 +125,6 @@ services:
|
||||
- _APP_SMTP_USERNAME
|
||||
- _APP_SMTP_PASSWORD
|
||||
- _APP_USAGE_STATS
|
||||
- _APP_INFLUXDB_HOST
|
||||
- _APP_INFLUXDB_PORT
|
||||
- _APP_STORAGE_LIMIT
|
||||
- _APP_STORAGE_PREVIEW_LIMIT
|
||||
- _APP_STORAGE_ANTIVIRUS
|
||||
@@ -163,8 +161,6 @@ services:
|
||||
- _APP_EXECUTOR_HOST
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_STATSD_HOST
|
||||
- _APP_STATSD_PORT
|
||||
- _APP_MAINTENANCE_INTERVAL
|
||||
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||
@@ -620,36 +616,38 @@ services:
|
||||
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
|
||||
appwrite-usage:
|
||||
entrypoint: usage
|
||||
appwrite-worker-usage:
|
||||
entrypoint: worker-usage
|
||||
<<: *x-logging
|
||||
container_name: appwrite-usage
|
||||
container_name: appwrite-worker-usage
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
- ./dev:/usr/local/dev
|
||||
depends_on:
|
||||
- influxdb
|
||||
- redis
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_CONNECTIONS_MAX
|
||||
- _APP_POOL_CLIENTS
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_INFLUXDB_HOST
|
||||
- _APP_INFLUXDB_PORT
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_CONNECTIONS_DB_CONSOLE
|
||||
- _APP_CONNECTIONS_DB_PROJECT
|
||||
- _APP_CONNECTIONS_CACHE
|
||||
- _APP_CONNECTIONS_QUEUE
|
||||
- _APP_USAGE_STATS
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
@@ -812,26 +810,6 @@ services:
|
||||
# - appwrite
|
||||
# volumes:
|
||||
# - appwrite-uploads:/storage/uploads
|
||||
|
||||
influxdb:
|
||||
image: appwrite/influxdb:1.5.0
|
||||
container_name: appwrite-influxdb
|
||||
<<: *x-logging
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- appwrite-influxdb:/var/lib/influxdb:rw
|
||||
|
||||
telegraf:
|
||||
image: appwrite/telegraf:1.4.0
|
||||
container_name: appwrite-telegraf
|
||||
<<: *x-logging
|
||||
networks:
|
||||
- appwrite
|
||||
environment:
|
||||
- _APP_INFLUXDB_HOST
|
||||
- _APP_INFLUXDB_PORT
|
||||
|
||||
# Dev Tools Start ------------------------------------------------------------------------------------------
|
||||
#
|
||||
# The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack
|
||||
@@ -895,26 +873,6 @@ services:
|
||||
# - RESQUE_WEB_PORT=6379
|
||||
# - RESQUE_WEB_HTTP_BASIC_AUTH_USER=user
|
||||
# - RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD=password
|
||||
|
||||
# chronograf:
|
||||
# image: chronograf:1.6
|
||||
# container_name: appwrite-chronograf
|
||||
# restart: unless-stopped
|
||||
# networks:
|
||||
# - appwrite
|
||||
# volumes:
|
||||
# - appwrite-chronograf:/var/lib/chronograf
|
||||
# ports:
|
||||
# - "8888:8888"
|
||||
# environment:
|
||||
# - INFLUXDB_URL=http://influxdb:8086
|
||||
# - KAPACITOR_URL=http://kapacitor:9092
|
||||
# - AUTH_DURATION=48h
|
||||
# - TOKEN_SECRET=duperduper5674829!jwt
|
||||
# - GH_CLIENT_ID=d86f7145a41eacfc52cc
|
||||
# - GH_CLIENT_SECRET=9e0081062367a2134e7f2ea95ba1a32d08b6c8ab
|
||||
# - GH_ORGS=appwrite
|
||||
|
||||
# webgrind:
|
||||
# image: 'jokkedk/webgrind:latest'
|
||||
# volumes:
|
||||
@@ -952,6 +910,4 @@ volumes:
|
||||
appwrite-certificates:
|
||||
appwrite-functions:
|
||||
appwrite-builds:
|
||||
appwrite-influxdb:
|
||||
appwrite-config:
|
||||
# appwrite-chronograf:
|
||||
|
||||
@@ -15,7 +15,6 @@ use Appwrite\Platform\Tasks\SSL;
|
||||
use Appwrite\Platform\Tasks\Hamster;
|
||||
use Appwrite\Platform\Tasks\PatchDeleteScheduleUpdatedAtAttribute;
|
||||
use Appwrite\Platform\Tasks\ClearCardCache;
|
||||
use Appwrite\Platform\Tasks\Usage;
|
||||
use Appwrite\Platform\Tasks\Vars;
|
||||
use Appwrite\Platform\Tasks\Version;
|
||||
use Appwrite\Platform\Tasks\VolumeSync;
|
||||
@@ -31,7 +30,6 @@ class Tasks extends Service
|
||||
$this->type = self::TYPE_CLI;
|
||||
$this
|
||||
->addAction(Version::getName(), new Version())
|
||||
->addAction(Usage::getName(), new Usage())
|
||||
->addAction(Vars::getName(), new Vars())
|
||||
->addAction(SSL::getName(), new SSL())
|
||||
->addAction(Hamster::getName(), new Hamster())
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Appwrite\Usage\Calculators\TimeSeries;
|
||||
use InfluxDB\Database as InfluxDatabase;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database as UtopiaDatabase;
|
||||
use Throwable;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Registry\Registry;
|
||||
|
||||
class Usage extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'usage';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->desc('Schedules syncing data from influxdb to Appwrite console db')
|
||||
->inject('dbForConsole')
|
||||
->inject('influxdb')
|
||||
->inject('register')
|
||||
->inject('getProjectDB')
|
||||
->inject('logError')
|
||||
->callback(fn ($dbForConsole, $influxDB, $register, $getProjectDB, $logError) => $this->action($dbForConsole, $influxDB, $register, $getProjectDB, $logError));
|
||||
}
|
||||
|
||||
protected function aggregateTimeseries(UtopiaDatabase $database, InfluxDatabase $influxDB, callable $logError): void
|
||||
{
|
||||
}
|
||||
|
||||
public function action(UtopiaDatabase $dbForConsole, InfluxDatabase $influxDB, Registry $register, callable $getProjectDB, callable $logError)
|
||||
{
|
||||
Console::title('Usage Aggregation V1');
|
||||
Console::success(APP_NAME . ' usage aggregation process v1 has started');
|
||||
|
||||
$errorLogger = fn(Throwable $error, string $action = 'syncUsageStats') => $logError($error, "usage", $action);
|
||||
|
||||
$interval = (int) App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '30'); // 30 seconds (by default)
|
||||
$region = App::getEnv('region', 'default');
|
||||
$usage = new TimeSeries($region, $dbForConsole, $influxDB, $getProjectDB, $register, $errorLogger);
|
||||
|
||||
Console::loop(function () use ($interval, $usage) {
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Aggregating Timeseries Usage data every {$interval} seconds");
|
||||
$loopStart = microtime(true);
|
||||
|
||||
$usage->collect();
|
||||
|
||||
$loopTook = microtime(true) - $loopStart;
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Aggregation took {$loopTook} seconds");
|
||||
}, $interval);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Usage;
|
||||
|
||||
abstract class Calculator
|
||||
{
|
||||
protected string $region;
|
||||
|
||||
public function __construct(string $region)
|
||||
{
|
||||
$this->region = $region;
|
||||
}
|
||||
|
||||
abstract public function collect(): void;
|
||||
}
|
||||
@@ -1,557 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Usage\Calculators;
|
||||
|
||||
use Utopia\App;
|
||||
use Appwrite\Usage\Calculator;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use InfluxDB\Database as InfluxDatabase;
|
||||
use DateTime;
|
||||
use Utopia\Registry\Registry;
|
||||
|
||||
class TimeSeries extends Calculator
|
||||
{
|
||||
/**
|
||||
* InfluxDB
|
||||
*
|
||||
* @var InfluxDatabase
|
||||
*/
|
||||
protected InfluxDatabase $influxDB;
|
||||
|
||||
/**
|
||||
* Utopia Database
|
||||
*
|
||||
* @var Database
|
||||
*/
|
||||
protected Database $database;
|
||||
|
||||
/**
|
||||
* Error Handler Callback
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
protected $errorHandler;
|
||||
|
||||
/**
|
||||
* Callback to get project DB
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
protected mixed $getProjectDB;
|
||||
|
||||
/**
|
||||
* Registry
|
||||
*
|
||||
* @var Registry
|
||||
*/
|
||||
protected Registry $register;
|
||||
|
||||
/**
|
||||
* Latest times for metric that was synced to the database
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private array $latestTime = [];
|
||||
|
||||
/**
|
||||
* Periods the metrics are collected for
|
||||
* @var array
|
||||
*/
|
||||
protected array $periods = [
|
||||
[
|
||||
'key' => '1h',
|
||||
'startTime' => '-24 hours'
|
||||
],
|
||||
[
|
||||
'key' => '1d',
|
||||
'startTime' => '-30 days'
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* All the metrics that we are collecting
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $metrics = [
|
||||
'project.$all.network.requests' => [
|
||||
'table' => 'appwrite_usage_project_{scope}_network_requests',
|
||||
],
|
||||
'project.$all.network.bandwidth' => [
|
||||
'table' => 'appwrite_usage_project_{scope}_network_bandwidth',
|
||||
],
|
||||
'project.$all.network.inbound' => [
|
||||
'table' => 'appwrite_usage_project_{scope}_network_inbound',
|
||||
],
|
||||
'project.$all.network.outbound' => [
|
||||
'table' => 'appwrite_usage_project_{scope}_network_outbound',
|
||||
],
|
||||
/* Users service metrics */
|
||||
'users.$all.requests.create' => [
|
||||
'table' => 'appwrite_usage_users_{scope}_requests_create',
|
||||
],
|
||||
'users.$all.requests.read' => [
|
||||
'table' => 'appwrite_usage_users_{scope}_requests_read',
|
||||
],
|
||||
'users.$all.requests.update' => [
|
||||
'table' => 'appwrite_usage_users_{scope}_requests_update',
|
||||
],
|
||||
'users.$all.requests.delete' => [
|
||||
'table' => 'appwrite_usage_users_{scope}_requests_delete',
|
||||
],
|
||||
|
||||
'databases.$all.requests.create' => [
|
||||
'table' => 'appwrite_usage_databases_{scope}_requests_create',
|
||||
],
|
||||
'databases.$all.requests.read' => [
|
||||
'table' => 'appwrite_usage_databases_{scope}_requests_read',
|
||||
],
|
||||
'databases.$all.requests.update' => [
|
||||
'table' => 'appwrite_usage_databases_{scope}_requests_update',
|
||||
],
|
||||
'databases.$all.requests.delete' => [
|
||||
'table' => 'appwrite_usage_databases_{scope}_requests_delete',
|
||||
],
|
||||
|
||||
'collections.$all.requests.create' => [
|
||||
'table' => 'appwrite_usage_collections_{scope}_requests_create',
|
||||
],
|
||||
'collections.$all.requests.read' => [
|
||||
'table' => 'appwrite_usage_collections_{scope}_requests_read',
|
||||
],
|
||||
'collections.$all.requests.update' => [
|
||||
'table' => 'appwrite_usage_collections_{scope}_requests_update',
|
||||
],
|
||||
'collections.$all.requests.delete' => [
|
||||
'table' => 'appwrite_usage_collections_{scope}_requests_delete',
|
||||
],
|
||||
|
||||
'documents.$all.requests.create' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_requests_create',
|
||||
],
|
||||
'documents.$all.requests.read' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_requests_read',
|
||||
],
|
||||
'documents.$all.requests.update' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_requests_update',
|
||||
],
|
||||
'documents.$all.requests.delete' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_requests_delete',
|
||||
],
|
||||
|
||||
'collections.databaseId.requests.create' => [
|
||||
'table' => 'appwrite_usage_collections_{scope}_requests_create',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'collections.databaseId.requests.read' => [
|
||||
'table' => 'appwrite_usage_collections_{scope}_requests_read',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'collections.databaseId.requests.update' => [
|
||||
'table' => 'appwrite_usage_collections_{scope}_requests_update',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'collections.databaseId.requests.delete' => [
|
||||
'table' => 'appwrite_usage_collections_{scope}_requests_delete',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
|
||||
'documents.databaseId.requests.create' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_requests_create',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'documents.databaseId.requests.read' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_requests_read',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'documents.databaseId.requests.update' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_requests_update',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
'documents.databaseId.requests.delete' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_requests_delete',
|
||||
'groupBy' => ['databaseId'],
|
||||
],
|
||||
|
||||
'documents.databaseId/collectionId.requests.create' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_requests_create',
|
||||
'groupBy' => ['databaseId', 'collectionId'],
|
||||
],
|
||||
'documents.databaseId/collectionId.requests.read' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_requests_read',
|
||||
'groupBy' => ['databaseId', 'collectionId'],
|
||||
],
|
||||
'documents.databaseId/collectionId.requests.update' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_requests_update',
|
||||
'groupBy' => ['databaseId', 'collectionId'],
|
||||
],
|
||||
'documents.databaseId/collectionId.requests.delete' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_requests_delete',
|
||||
'groupBy' => ['databaseId', 'collectionId'],
|
||||
],
|
||||
|
||||
'buckets.$all.requests.create' => [
|
||||
'table' => 'appwrite_usage_buckets_{scope}_requests_create',
|
||||
],
|
||||
'buckets.$all.requests.read' => [
|
||||
'table' => 'appwrite_usage_buckets_{scope}_requests_read',
|
||||
],
|
||||
'buckets.$all.requests.update' => [
|
||||
'table' => 'appwrite_usage_buckets_{scope}_requests_update',
|
||||
],
|
||||
'buckets.$all.requests.delete' => [
|
||||
'table' => 'appwrite_usage_buckets_{scope}_requests_delete',
|
||||
],
|
||||
|
||||
'files.$all.requests.create' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_requests_create',
|
||||
],
|
||||
'files.$all.requests.read' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_requests_read',
|
||||
],
|
||||
'files.$all.requests.update' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_requests_update',
|
||||
],
|
||||
'files.$all.requests.delete' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_requests_delete',
|
||||
],
|
||||
|
||||
'files.bucketId.requests.create' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_requests_create',
|
||||
'groupBy' => ['bucketId'],
|
||||
],
|
||||
'files.bucketId.requests.read' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_requests_read',
|
||||
'groupBy' => ['bucketId'],
|
||||
],
|
||||
'files.bucketId.requests.update' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_requests_update',
|
||||
'groupBy' => ['bucketId'],
|
||||
],
|
||||
'files.bucketId.requests.delete' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_requests_delete',
|
||||
'groupBy' => ['bucketId'],
|
||||
],
|
||||
|
||||
'sessions.$all.requests.create' => [
|
||||
'table' => 'appwrite_usage_sessions__{scope}_requests_create',
|
||||
],
|
||||
'sessions.provider.requests.create' => [
|
||||
'table' => 'appwrite_usage_sessions_{scope}_requests_create',
|
||||
'groupBy' => ['provider'],
|
||||
],
|
||||
'sessions.$all.requests.delete' => [
|
||||
'table' => 'appwrite_usage_sessions_{scope}_requests_delete',
|
||||
],
|
||||
'executions.$all.compute.total' => [
|
||||
'table' => 'appwrite_usage_executions_{scope}_compute',
|
||||
],
|
||||
'builds.$all.compute.total' => [
|
||||
'table' => 'appwrite_usage_builds_{scope}_compute',
|
||||
],
|
||||
'executions.$all.compute.failure' => [
|
||||
'table' => 'appwrite_usage_executions_{scope}_compute',
|
||||
'filters' => [
|
||||
'functionStatus' => 'failed',
|
||||
],
|
||||
],
|
||||
'builds.$all.compute.failure' => [
|
||||
'table' => 'appwrite_usage_builds_{scope}_compute',
|
||||
'filters' => [
|
||||
'functionStatus' => 'failed',
|
||||
],
|
||||
],
|
||||
'executions.$all.compute.success' => [
|
||||
'table' => 'appwrite_usage_executions_{scope}_compute',
|
||||
'filters' => [
|
||||
'functionStatus' => 'success',
|
||||
],
|
||||
],
|
||||
'builds.$all.compute.success' => [
|
||||
'table' => 'appwrite_usage_builds_{scope}_compute',
|
||||
'filters' => [
|
||||
'functionStatus' => 'success',
|
||||
],
|
||||
],
|
||||
'executions.functionId.compute.total' => [
|
||||
'table' => 'appwrite_usage_executions_{scope}_compute',
|
||||
'groupBy' => ['functionId'],
|
||||
],
|
||||
'builds.functionId.compute.total' => [
|
||||
'table' => 'appwrite_usage_builds_{scope}_compute',
|
||||
'groupBy' => ['functionId'],
|
||||
],
|
||||
|
||||
'executions.functionId.compute.failure' => [
|
||||
'table' => 'appwrite_usage_executions_{scope}_compute',
|
||||
'groupBy' => ['functionId'],
|
||||
'filters' => [
|
||||
'functionStatus' => 'failed',
|
||||
],
|
||||
],
|
||||
'builds.functionId.compute.failure' => [
|
||||
'table' => 'appwrite_usage_builds_{scope}_compute',
|
||||
'groupBy' => ['functionId'],
|
||||
'filters' => [
|
||||
'functionBuildStatus' => 'failed',
|
||||
],
|
||||
],
|
||||
'executions.functionId.compute.success' => [
|
||||
'table' => 'appwrite_usage_executions_{scope}_compute',
|
||||
'groupBy' => ['functionId'],
|
||||
'filters' => [
|
||||
'functionStatus' => 'success',
|
||||
],
|
||||
],
|
||||
'builds.functionId.compute.success' => [
|
||||
'table' => 'appwrite_usage_builds_{scope}_compute',
|
||||
'groupBy' => ['functionId'],
|
||||
'filters' => [
|
||||
'functionBuildStatus' => 'success',
|
||||
],
|
||||
],
|
||||
|
||||
// counters
|
||||
'users.$all.count.total' => [
|
||||
'table' => 'appwrite_usage_users_{scope}_count_total',
|
||||
],
|
||||
'buckets.$all.count.total' => [
|
||||
'table' => 'appwrite_usage_buckets_{scope}_count_total',
|
||||
],
|
||||
'files.$all.count.total' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_count_total',
|
||||
],
|
||||
'files.bucketId.count.total' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_count_total',
|
||||
'groupBy' => ['bucketId']
|
||||
],
|
||||
'databases.$all.count.total' => [
|
||||
'table' => 'appwrite_usage_databases_{scope}_count_total',
|
||||
],
|
||||
'collections.$all.count.total' => [
|
||||
'table' => 'appwrite_usage_collections_{scope}_count_total',
|
||||
],
|
||||
'documents.$all.count.total' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_count_total',
|
||||
],
|
||||
'collections.databaseId.count.total' => [
|
||||
'table' => 'appwrite_usage_collections_{scope}_count_total',
|
||||
'groupBy' => ['databaseId']
|
||||
],
|
||||
'documents.databaseId.count.total' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_count_total',
|
||||
'groupBy' => ['databaseId']
|
||||
],
|
||||
'documents.databaseId/collectionId.count.total' => [
|
||||
'table' => 'appwrite_usage_documents_{scope}_count_total',
|
||||
'groupBy' => ['databaseId', 'collectionId']
|
||||
],
|
||||
'deployments.$all.storage.size' => [
|
||||
'table' => 'appwrite_usage_deployments_{scope}_storage_size',
|
||||
],
|
||||
'project.$all.storage.size' => [
|
||||
'table' => 'appwrite_usage_project_{scope}_storage_size',
|
||||
],
|
||||
'files.$all.storage.size' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_storage_size',
|
||||
],
|
||||
'files.$bucketId.storage.size' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_storage_size',
|
||||
'groupBy' => ['bucketId']
|
||||
],
|
||||
|
||||
'builds.$all.compute.time' => [
|
||||
'table' => 'appwrite_usage_executions_{scope}_compute_time',
|
||||
],
|
||||
'executions.$all.compute.time' => [
|
||||
'table' => 'appwrite_usage_executions_{scope}_compute_time',
|
||||
],
|
||||
|
||||
'executions.functionId.compute.time' => [
|
||||
'table' => 'appwrite_usage_executions_{scope}_compute_time',
|
||||
'groupBy' => ['functionId'],
|
||||
],
|
||||
'builds.functionId.compute.time' => [
|
||||
'table' => 'appwrite_usage_builds_{scope}_compute_time',
|
||||
'groupBy' => ['functionId'],
|
||||
],
|
||||
|
||||
'project.$all.compute.time' => [ // Built time + execution time
|
||||
'table' => 'appwrite_usage_project_{scope}_compute_time',
|
||||
'groupBy' => ['functionId'],
|
||||
],
|
||||
|
||||
'deployments.$all.storage.size' => [
|
||||
'table' => 'appwrite_usage_deployments_{scope}_storage_size'
|
||||
],
|
||||
'project.$all.storage.size' => [
|
||||
'table' => 'appwrite_usage_project_{scope}_storage_size'
|
||||
],
|
||||
'files.$all.storage.size' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_storage_size'
|
||||
],
|
||||
'files.bucketId.storage.size' => [
|
||||
'table' => 'appwrite_usage_files_{scope}_storage_size',
|
||||
'groupBy' => ['bucketId']
|
||||
]
|
||||
];
|
||||
|
||||
public function __construct(string $region, Database $database, InfluxDatabase $influxDB, callable $getProjectDB, Registry $register, callable $errorHandler = null)
|
||||
{
|
||||
parent::__construct($region);
|
||||
$this->database = $database;
|
||||
$this->influxDB = $influxDB;
|
||||
$this->getProjectDB = $getProjectDB;
|
||||
$this->register = $register;
|
||||
$this->errorHandler = $errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or Update Mertic
|
||||
* Create or update each metric in the stats collection for the given project
|
||||
*
|
||||
* @param string $projectId
|
||||
* @param int $time
|
||||
* @param string $period
|
||||
* @param string $metric
|
||||
* @param int $value
|
||||
* @param int $type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function createOrUpdateMetric(string $projectId, string $time, string $period, string $metric, int $value, int $type): void
|
||||
{
|
||||
$id = \md5("{$time}_{$period}_{$metric}");
|
||||
$project = $this->database->getDocument('projects', $projectId);
|
||||
$database = call_user_func($this->getProjectDB, $project);
|
||||
|
||||
try {
|
||||
$document = $database->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
$database->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'period' => $period,
|
||||
'time' => $time,
|
||||
'metric' => $metric,
|
||||
'value' => $value,
|
||||
'type' => $type,
|
||||
'region' => $this->region,
|
||||
]));
|
||||
} else {
|
||||
$database->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $value)
|
||||
);
|
||||
}
|
||||
} catch (\Exception $e) { // if projects are deleted this might fail
|
||||
if (is_callable($this->errorHandler)) {
|
||||
call_user_func($this->errorHandler, $e, "sync_project_{$projectId}_metric_{$metric}");
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$this->register->get('pools')->reclaim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync From InfluxDB
|
||||
* Sync stats from influxDB to stats collection in the Appwrite database
|
||||
*
|
||||
* @param string $metric
|
||||
* @param array $options
|
||||
* @param array $period
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function syncFromInfluxDB(string $metric, array $options, array $period): void
|
||||
{
|
||||
$start = DateTime::createFromFormat('U', \strtotime($period['startTime']))->format(DateTime::RFC3339);
|
||||
if (!empty($this->latestTime[$metric][$period['key']])) {
|
||||
$start = $this->latestTime[$metric][$period['key']];
|
||||
}
|
||||
$end = (new DateTime())->format(DateTime::RFC3339);
|
||||
|
||||
$table = $options['table']; //Which influxdb table to query for this metric
|
||||
$groupBy = empty($options['groupBy']) ? '' : ', ' . implode(', ', array_map(fn($groupBy) => '"' . $groupBy . '" ', $options['groupBy'])); //Some sub level metrics may be grouped by other tags like collectionId, bucketId, etc
|
||||
|
||||
$filters = $options['filters'] ?? []; // Some metrics might have additional filters, like function's status
|
||||
if (!empty($filters)) {
|
||||
$filters = ' AND ' . implode(' AND ', array_map(fn ($filter, $value) => "\"{$filter}\"='{$value}'", array_keys($filters), array_values($filters)));
|
||||
} else {
|
||||
$filters = '';
|
||||
}
|
||||
|
||||
$query = "SELECT sum(value) AS \"value\" ";
|
||||
$query .= "FROM \"{$table}\" ";
|
||||
$query .= "WHERE \"time\" > '{$start}' ";
|
||||
$query .= "AND \"time\" < '{$end}' ";
|
||||
$query .= "AND \"metric_type\"='counter' {$filters} ";
|
||||
$query .= "GROUP BY time({$period['key']}), \"projectId\" {$groupBy} ";
|
||||
$query .= "FILL(null)";
|
||||
|
||||
try {
|
||||
$result = $this->influxDB->query($query);
|
||||
$points = $result->getPoints();
|
||||
foreach ($points as $point) {
|
||||
$projectId = $point['projectId'];
|
||||
|
||||
if (!empty($projectId) && $projectId !== 'console') {
|
||||
$metricUpdated = $metric;
|
||||
if (!empty($groupBy)) {
|
||||
foreach ($options['groupBy'] as $groupBy) {
|
||||
$groupedBy = $point[$groupBy] ?? '';
|
||||
if (empty($groupedBy)) {
|
||||
continue;
|
||||
}
|
||||
$metricUpdated = str_replace($groupBy, $groupedBy, $metricUpdated);
|
||||
}
|
||||
}
|
||||
|
||||
$value = (!empty($point['value'])) ? $point['value'] : 0;
|
||||
|
||||
$this->createOrUpdateMetric(
|
||||
$point['projectId'],
|
||||
$point['time'],
|
||||
$period['key'],
|
||||
$metricUpdated,
|
||||
$value,
|
||||
0
|
||||
);
|
||||
$this->latestTime[$metric][$period['key']] = $point['time'];
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) { // if projects are deleted this might fail
|
||||
if (is_callable($this->errorHandler)) {
|
||||
call_user_func($this->errorHandler, $e, "sync_metric_{$metric}_influxdb");
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect Stats
|
||||
* Collect all the stats from Influd DB to Database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function collect(): void
|
||||
{
|
||||
foreach ($this->periods as $period) {
|
||||
foreach ($this->metrics as $metric => $options) { //for each metrics
|
||||
try {
|
||||
$this->syncFromInfluxDB($metric, $options, $period);
|
||||
} catch (\Exception $e) {
|
||||
if (is_callable($this->errorHandler)) {
|
||||
call_user_func($this->errorHandler, $e);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Usage;
|
||||
|
||||
use Utopia\App;
|
||||
|
||||
class Stats
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $params = [];
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $statsd;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'appwrite.usage';
|
||||
|
||||
/**
|
||||
* Event constructor.
|
||||
*
|
||||
* @param mixed $statsd
|
||||
*/
|
||||
public function __construct($statsd)
|
||||
{
|
||||
$this->statsd = $statsd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParam(string $key, $value): self
|
||||
{
|
||||
$this->params[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getParam(string $key)
|
||||
{
|
||||
return (isset($this->params[$key])) ? $this->params[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setNamespace(string $namespace): self
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit data to StatsD.
|
||||
* Send various metrics to StatsD based on the parameters that are set
|
||||
* @return void
|
||||
*/
|
||||
public function submit(): void
|
||||
{
|
||||
$projectId = $this->params['projectId'] ?? '';
|
||||
$projectInternalId = $this->params['projectInternalId'];
|
||||
$tags = ",projectInternalId={$projectInternalId},projectId={$projectId},version=" . App::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
// the global namespace is prepended to every key (optional)
|
||||
$this->statsd->setNamespace($this->namespace);
|
||||
|
||||
$httpRequest = $this->params['project.{scope}.network.requests'] ?? 0;
|
||||
$httpMethod = $this->params['httpMethod'] ?? '';
|
||||
if ($httpRequest >= 1) {
|
||||
$this->statsd->increment('project.{scope}.network.requests' . $tags . ',method=' . \strtolower($httpMethod));
|
||||
}
|
||||
|
||||
$inbound = $this->params['project.{scope}.network.inbound'] ?? 0;
|
||||
$outbound = $this->params['project.{scope}.network.outbound'] ?? 0;
|
||||
$this->statsd->count('project.{scope}.network.inbound' . $tags, $inbound);
|
||||
$this->statsd->count('project.{scope}.network.outbound' . $tags, $outbound);
|
||||
$this->statsd->count('project.{scope}.network.bandwidth' . $tags, $inbound + $outbound);
|
||||
|
||||
$usersMetrics = [
|
||||
'users.{scope}.requests.create',
|
||||
'users.{scope}.requests.read',
|
||||
'users.{scope}.requests.update',
|
||||
'users.{scope}.requests.delete',
|
||||
'users.{scope}.count.total',
|
||||
];
|
||||
|
||||
foreach ($usersMetrics as $metric) {
|
||||
$value = $this->params[$metric] ?? 0;
|
||||
if ($value === 1 || $value === -1) {
|
||||
$this->statsd->count($metric . $tags, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$dbMetrics = [
|
||||
'databases.{scope}.requests.create',
|
||||
'databases.{scope}.requests.read',
|
||||
'databases.{scope}.requests.update',
|
||||
'databases.{scope}.requests.delete',
|
||||
'collections.{scope}.requests.create',
|
||||
'collections.{scope}.requests.read',
|
||||
'collections.{scope}.requests.update',
|
||||
'collections.{scope}.requests.delete',
|
||||
'documents.{scope}.requests.create',
|
||||
'documents.{scope}.requests.read',
|
||||
'documents.{scope}.requests.update',
|
||||
'documents.{scope}.requests.delete',
|
||||
'databases.{scope}.count.total',
|
||||
'collections.{scope}.count.total',
|
||||
'documents.{scope}.count.total'
|
||||
];
|
||||
|
||||
foreach ($dbMetrics as $metric) {
|
||||
$value = $this->params[$metric] ?? 0;
|
||||
if ($value === 1 || $value === -1) {
|
||||
$dbTags = $tags . ",collectionId=" . ($this->params['collectionId'] ?? '') . ",databaseId=" . ($this->params['databaseId'] ?? '');
|
||||
$this->statsd->count($metric . $dbTags, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$storageMertics = [
|
||||
'buckets.{scope}.requests.create',
|
||||
'buckets.{scope}.requests.read',
|
||||
'buckets.{scope}.requests.update',
|
||||
'buckets.{scope}.requests.delete',
|
||||
'files.{scope}.requests.create',
|
||||
'files.{scope}.requests.read',
|
||||
'files.{scope}.requests.update',
|
||||
'files.{scope}.requests.delete',
|
||||
'buckets.{scope}.count.total',
|
||||
'files.{scope}.count.total',
|
||||
'files.{scope}.storage.size'
|
||||
];
|
||||
|
||||
foreach ($storageMertics as $metric) {
|
||||
$value = $this->params[$metric] ?? 0;
|
||||
if ($value !== 0) {
|
||||
$storageTags = $tags . ",bucketId=" . ($this->params['bucketId'] ?? '');
|
||||
$this->statsd->count($metric . $storageTags, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$sessionsMetrics = [
|
||||
'sessions.{scope}.requests.create',
|
||||
'sessions.{scope}.requests.update',
|
||||
'sessions.{scope}.requests.delete',
|
||||
];
|
||||
|
||||
foreach ($sessionsMetrics as $metric) {
|
||||
$value = $this->params[$metric] ?? 0;
|
||||
if ($value >= 1) {
|
||||
$sessionTags = $tags . ",provider=" . ($this->params['provider'] ?? '');
|
||||
$this->statsd->count($metric . $sessionTags, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$functionId = $this->params['functionId'] ?? '';
|
||||
$functionExecution = $this->params['executions.{scope}.compute'] ?? 0;
|
||||
$functionExecutionTime = ($this->params['executionTime'] ?? 0) * 1000; // ms
|
||||
$functionExecutionStatus = $this->params['executionStatus'] ?? '';
|
||||
|
||||
$functionBuild = $this->params['builds.{scope}.compute'] ?? 0;
|
||||
$functionBuildTime = ($this->params['buildTime'] ?? 0) * 1000; // ms
|
||||
$functionBuildStatus = $this->params['buildStatus'] ?? '';
|
||||
$functionCompute = $functionExecutionTime + $functionBuildTime;
|
||||
$functionTags = $tags . ',functionId=' . $functionId;
|
||||
|
||||
$deploymentSize = $this->params['deployment.{scope}.storage.size'] ?? 0;
|
||||
$storageSize = $this->params['files.{scope}.storage.size'] ?? 0;
|
||||
if ($deploymentSize + $storageSize > 0 || $deploymentSize + $storageSize <= -1) {
|
||||
$this->statsd->count('project.{scope}.storage.size' . $tags, $deploymentSize + $storageSize);
|
||||
}
|
||||
|
||||
if ($deploymentSize !== 0) {
|
||||
$this->statsd->count('deployments.{scope}.storage.size' . $functionTags, $deploymentSize);
|
||||
}
|
||||
|
||||
if ($functionExecution >= 1) {
|
||||
$this->statsd->increment('executions.{scope}.compute' . $functionTags . ',functionStatus=' . $functionExecutionStatus);
|
||||
if ($functionExecutionTime > 0) {
|
||||
$this->statsd->count('executions.{scope}.compute.time' . $functionTags, $functionExecutionTime);
|
||||
}
|
||||
}
|
||||
if ($functionBuild >= 1) {
|
||||
$this->statsd->increment('builds.{scope}.compute' . $functionTags . ',functionBuildStatus=' . $functionBuildStatus);
|
||||
$this->statsd->count('builds.{scope}.compute.time' . $functionTags, $functionBuildTime);
|
||||
}
|
||||
if ($functionBuild + $functionExecution >= 1) {
|
||||
$this->statsd->count('project.{scope}.compute.time' . $functionTags, $functionCompute);
|
||||
}
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
public function reset(): self
|
||||
{
|
||||
$this->params = [];
|
||||
$this->namespace = 'appwrite.usage';
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user