mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
22
Commits
main
...
feat-benchmarks
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3903b51daf | ||
|
|
9dd5da479d | ||
|
|
eaa10fdba0 | ||
|
|
5b8c16b7a7 | ||
|
|
b4bd3622c6 | ||
|
|
8448e92894 | ||
|
|
dbffea2ba3 | ||
|
|
a1cd6d90a2 | ||
|
|
caceef7fa6 | ||
|
|
a08cbe3638 | ||
|
|
8774810314 | ||
|
|
7cbe827404 | ||
|
|
9335563e87 | ||
|
|
3c9d4b877a | ||
|
|
0b45c508b7 | ||
|
|
84df9c5478 | ||
|
|
aaf896f27e | ||
|
|
50c7c7aa7a | ||
|
|
dae75a6128 | ||
|
|
f1d8403fba | ||
|
|
f1abf00f09 | ||
|
|
f52e4c3e63 |
@@ -0,0 +1,42 @@
|
||||
name: "Benchmark Update Baseline"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
benchmark-update:
|
||||
name: Benchmark Update Baseline
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Build & Run Appwrite
|
||||
run: |
|
||||
export COMPOSE_INTERACTIVE_NO_CLI
|
||||
export DOCKER_BUILDKIT=1
|
||||
export COMPOSE_DOCKER_CLI_BUILD=1
|
||||
export BUILDKIT_PROGRESS=plain
|
||||
docker pull composer:2.0
|
||||
docker compose build appwrite
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
|
||||
- name: Doctor
|
||||
run: docker compose exec -T appwrite doctor
|
||||
|
||||
- name: Run Benchmark and Update Baseline
|
||||
run: |
|
||||
rm -rf .phpbench/*
|
||||
docker compose exec -T appwrite benchmark --tag=baseline
|
||||
|
||||
- name: Commit updated baseline benchmark
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: Update benchmark baseline
|
||||
@@ -12,7 +12,7 @@ on:
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
name: "Linter"
|
||||
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
lint:
|
||||
name: Linter
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- run: git checkout HEAD^2
|
||||
|
||||
- name: Run Linter
|
||||
run: |
|
||||
docker run --rm -v $PWD:/app composer sh -c \
|
||||
"composer install --profile --ignore-platform-reqs && composer lint"
|
||||
+19
-12
@@ -3,21 +3,23 @@ name: "Tests"
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
tests:
|
||||
name: Unit & E2E
|
||||
runs-on: ubuntu-latest
|
||||
name: Lint, Test, Benchmark
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
- name: Checkout Pull Request HEAD
|
||||
run: git checkout HEAD^2
|
||||
|
||||
- name: Install Dependencies
|
||||
run: docker run --rm -v $PWD:/app composer install --profile --ignore-platform-reqs
|
||||
|
||||
- name: Run Linter
|
||||
run: docker run --rm -v $PWD:/app composer lint
|
||||
|
||||
- name: Build Appwrite
|
||||
# Upstream bug causes buildkit pulls to fail so prefetch base images
|
||||
@@ -31,11 +33,16 @@ jobs:
|
||||
docker compose build appwrite
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
- name: Doctor
|
||||
|
||||
- name: Run Doctor
|
||||
run: docker compose exec -T appwrite doctor
|
||||
|
||||
- name: Environment Variables
|
||||
- name: Print Environment Variables
|
||||
run: docker compose exec -T appwrite vars
|
||||
|
||||
- name: Run Tests
|
||||
run: docker compose exec -T appwrite test --debug
|
||||
|
||||
- name: Run Benchmark
|
||||
continue-on-error: true
|
||||
run: docker compose exec -T appwrite benchmark --ref=baseline --progress=plain
|
||||
|
||||
File diff suppressed because one or more lines are too long
+33
-22
@@ -318,12 +318,12 @@ The Runtimes for all supported cloud functions (multicore builds) can be found a
|
||||
|
||||
For generating a new console SDK follow the next steps:
|
||||
|
||||
1. Update the console spec file located at `app/config/specs/swagger2-<version-number>.console.json` using Appwrite Tasks. Run the `php app/cli.php specs <version-number> normal` command in a running `appwrite/appwrite` container.
|
||||
2. Generate a new SDK using the command `php app/cli.php sdks`
|
||||
1. Generate new spec files using the command `docker compose exec appwrite specs <version-number>`
|
||||
2. Generate new SDKs using the command `docker compose exec appwrite sdks`
|
||||
3. Change your working dir using `cd app/sdks/console-web`
|
||||
4. Build the new SDK `npm run build`
|
||||
5. Copy `iife/sdk.js` to `appwrite.js`
|
||||
6. Go back to the root of the project `run npm run build`
|
||||
5. Copy `dist/iife/sdk.js` to `public/scripts/dependencies/appwrite.js`
|
||||
6. Go back to the root of the project and run `npm run build`
|
||||
|
||||
## Checklist for Releasing SDKs
|
||||
|
||||
@@ -332,7 +332,7 @@ Things to remember when releasing SDKs
|
||||
- Update the Changelogs in **docs/sdks** (right now only Dart and Flutter are using these)
|
||||
- Update **GETTING_STARTED.md** in **docs/sdks** for each SDKs if any changes in the related APIs in there
|
||||
- Update SDK versions as required on **app/config/platforms.php**
|
||||
- Generate SDKs using the command `php app/cli.php sdks` and follow the instructions
|
||||
- Generate SDKs using the command `docker compose exec appwrite sdks` and follow the instructions
|
||||
- Release new tags on GitHub repository for each SDKs
|
||||
|
||||
## Debug
|
||||
@@ -370,42 +370,53 @@ docker compose exec appwrite test
|
||||
To run unit tests use:
|
||||
|
||||
```bash
|
||||
docker compose exec appwrite test /usr/src/code/tests/unit
|
||||
docker compose exec appwrite test tests/unit
|
||||
```
|
||||
|
||||
To run end-2-end tests use:
|
||||
|
||||
```bash
|
||||
docker compose exec appwrite test /usr/src/code/tests/e2e
|
||||
docker compose exec appwrite test tests/e2e
|
||||
```
|
||||
|
||||
To run end-2-end tests for a specific service use:
|
||||
|
||||
```bash
|
||||
docker compose exec appwrite test /usr/src/code/tests/e2e/Services/[ServiceName]
|
||||
docker compose exec appwrite test tests/e2e/Services/[ServiceName]
|
||||
```
|
||||
|
||||
## Benchmarking
|
||||
|
||||
You can use WRK Docker image to benchmark the server performance. Benchmarking is extremely useful when you want to compare how the server behaves before and after a change has been applied. Replace [APPWRITE_HOSTNAME_OR_IP] with your Appwrite server hostname or IP. Note that localhost is not accessible from inside the WRK container.
|
||||
Benchmarking is extremely useful when you want to compare how the server behaves before and after a change has been applied.
|
||||
|
||||
```
|
||||
Options:
|
||||
-c, --connections <N> Connections to keep open
|
||||
-d, --duration <T> Duration of test
|
||||
-t, --threads <N> Number of threads to use
|
||||
|
||||
-s, --script <S> Load Lua script file
|
||||
-H, --header <H> Add header to request
|
||||
--latency Print latency statistics
|
||||
--timeout <T> Socket/request timeout
|
||||
-v, --version Print version details
|
||||
```
|
||||
To run all benchmarks manually, run the following command:
|
||||
|
||||
```bash
|
||||
docker run --rm skandyla/wrk -t3 -c100 -d30 https://[APPWRITE_HOSTNAME_OR_IP]
|
||||
composer benchmark
|
||||
```
|
||||
|
||||
To run benchmarks for a specific service, run the following command:
|
||||
|
||||
```bash
|
||||
composer benchmark tests/benchmarks/Services/[ServiceName]
|
||||
```
|
||||
|
||||
Because benchmarking is dependent on the resources available to the server, results will vary from machine to machine. To combat this, you should first run a benchmark to set a baseline using your machine before making any changes, then run a comparison after you've completed your changes.
|
||||
|
||||
To create a baseline benchmark, run the following command:
|
||||
|
||||
```bash
|
||||
composer benchmark-tag
|
||||
```
|
||||
|
||||
To run a comparison benchmark, run the following command:
|
||||
|
||||
```bash
|
||||
composer benchmark-compare
|
||||
```
|
||||
|
||||
The comparison benchmark output will show you a % difference in performance between the baseline and the current code.
|
||||
|
||||
## Code Maintenance
|
||||
|
||||
We use some automation tools to help us keep a healthy codebase.
|
||||
|
||||
+1
-22
@@ -330,28 +330,7 @@ RUN mkdir -p /storage/uploads && \
|
||||
chown -Rf www-data.www-data /storage/debug && chmod -Rf 0755 /storage/debug
|
||||
|
||||
# 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/migrate && \
|
||||
chmod +x /usr/local/bin/realtime && \
|
||||
chmod +x /usr/local/bin/executor && \
|
||||
chmod +x /usr/local/bin/schedule && \
|
||||
chmod +x /usr/local/bin/sdks && \
|
||||
chmod +x /usr/local/bin/specs && \
|
||||
chmod +x /usr/local/bin/ssl && \
|
||||
chmod +x /usr/local/bin/test && \
|
||||
chmod +x /usr/local/bin/vars && \
|
||||
chmod +x /usr/local/bin/worker-audits && \
|
||||
chmod +x /usr/local/bin/worker-certificates && \
|
||||
chmod +x /usr/local/bin/worker-databases && \
|
||||
chmod +x /usr/local/bin/worker-deletes && \
|
||||
chmod +x /usr/local/bin/worker-functions && \
|
||||
chmod +x /usr/local/bin/worker-builds && \
|
||||
chmod +x /usr/local/bin/worker-mails && \
|
||||
chmod +x /usr/local/bin/worker-messaging && \
|
||||
chmod +x /usr/local/bin/worker-webhooks
|
||||
RUN chmod +x /usr/local/bin/*
|
||||
|
||||
# Letsencrypt Permissions
|
||||
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
|
||||
|
||||
@@ -873,6 +873,11 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId')
|
||||
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''));
|
||||
$deployment->setAttribute('status', $build->getAttribute('status', 'processing'));
|
||||
$deployment->setAttribute('buildStderr', $build->getAttribute('stderr', ''));
|
||||
$deployment->setAttribute('buildStdout', $build->getAttribute('stdout', ''));
|
||||
|
||||
$response->dynamic($deployment, Response::MODEL_DEPLOYMENT);
|
||||
});
|
||||
|
||||
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
/usr/src/code/vendor/bin/phpbench run --config /usr/src/code/phpbench.json --report appwrite $@
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+23
-3
@@ -12,7 +12,19 @@
|
||||
"scripts": {
|
||||
"test": "vendor/bin/phpunit",
|
||||
"lint": "vendor/bin/phpcs",
|
||||
"format": "vendor/bin/phpcbf"
|
||||
"format": "vendor/bin/phpcbf",
|
||||
"benchmark": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"vendor/bin/phpbench run --report=appwrite --"
|
||||
],
|
||||
"benchmark-tag": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"vendor/bin/phpbench run --report=appwrite --tag=baseline --"
|
||||
],
|
||||
"benchmark-compare": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"vendor/bin/phpbench run --report=appwrite --ref=baseline --"
|
||||
]
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -24,11 +36,12 @@
|
||||
"psr-4": {
|
||||
"Tests\\E2E\\": "tests/e2e",
|
||||
"Tests\\Unit\\": "tests/unit",
|
||||
"Tests\\Benchmarks\\": "tests/benchmarks",
|
||||
"Appwrite\\Tests\\": "tests/extensions"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0.0",
|
||||
"php": "8.0.*",
|
||||
"ext-curl": "*",
|
||||
"ext-imagick": "*",
|
||||
"ext-mbstring": "*",
|
||||
@@ -82,7 +95,14 @@
|
||||
"phpunit/phpunit": "9.5.20",
|
||||
"squizlabs/php_codesniffer": "^3.6",
|
||||
"swoole/ide-helper": "4.8.9",
|
||||
"textalk/websocket": "1.5.7"
|
||||
"textalk/websocket": "1.5.7",
|
||||
"phpbench/phpbench": "^1.2",
|
||||
|
||||
"symfony/console": "^5.0",
|
||||
"symfony/filesystem": "^5.0",
|
||||
"symfony/finder": "^5.0",
|
||||
"symfony/options-resolver": "^5.0",
|
||||
"symfony/process": "^5.0"
|
||||
},
|
||||
"provide": {
|
||||
"ext-phpiredis": "*"
|
||||
|
||||
Generated
+1413
-10
File diff suppressed because it is too large
Load Diff
@@ -78,6 +78,8 @@ services:
|
||||
- appwrite-certificates:/storage/certificates:rw
|
||||
- appwrite-functions:/storage/functions:rw
|
||||
- ./phpunit.xml:/usr/src/code/phpunit.xml
|
||||
- ./phpbench.json:/usr/src/code/phpbench.json
|
||||
- ./.phpbench:/usr/src/code/.phpbench
|
||||
- ./tests:/usr/src/code/tests
|
||||
- ./app:/usr/src/code/app
|
||||
- ./docs:/usr/src/code/docs
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "./vendor/phpbench/phpbench/phpbench.schema.json",
|
||||
"runner.path": "tests/benchmarks",
|
||||
"runner.file_pattern": "*Bench.php",
|
||||
"runner.bootstrap": "app/init.php",
|
||||
"runner.revs": 1000,
|
||||
"runner.iterations": 3,
|
||||
"runner.retry_threshold": 5,
|
||||
"runner.warmup": 1,
|
||||
"report.generators": {
|
||||
"appwrite": {
|
||||
"extends": "aggregate",
|
||||
"cols": ["benchmark", "subject", "set" ,"revs", "its", "worst", "best", "mean", "mode", "rstdev"],
|
||||
"break": ["benchmark"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Benchmarks\Scopes;
|
||||
|
||||
use PhpBench\Attributes\AfterMethods;
|
||||
use PhpBench\Attributes\BeforeMethods;
|
||||
use Tests\E2E\Scopes\Scope as E2EScope;
|
||||
|
||||
#[BeforeMethods(['setUp'])]
|
||||
#[AfterMethods(['tearDown'])]
|
||||
abstract class Scope extends E2EScope
|
||||
{
|
||||
protected $endpoint = 'http://localhost/v1';
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Benchmarks\Services\Databases;
|
||||
|
||||
use PhpBench\Attributes\BeforeMethods;
|
||||
use PhpBench\Attributes\ParamProviders;
|
||||
use Tests\Benchmarks\Scopes\Scope;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
abstract class Base extends Scope
|
||||
{
|
||||
use ProjectCustom;
|
||||
|
||||
protected static string $databaseId;
|
||||
protected static string $collectionId;
|
||||
protected static string $documentId;
|
||||
|
||||
#[BeforeMethods(['createDatabase', 'createCollection'])]
|
||||
public function benchDocumentCreate()
|
||||
{
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . static::$databaseId . '/collections/' . static::$collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'The Matrix',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::write(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
#[ParamProviders(['provideCounts'])]
|
||||
#[BeforeMethods(['createDatabase', 'createCollection', 'createDocuments'])]
|
||||
public function benchDocumentReadList(array $params)
|
||||
{
|
||||
$this->client->call(Client::METHOD_GET, '/databases/' . static::$databaseId . '/collections/' . static::$collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['limit(' . $params['documents'] . ')'],
|
||||
]);
|
||||
}
|
||||
|
||||
#[BeforeMethods(['createDatabase', 'createCollection', 'createDocuments'])]
|
||||
public function benchDocumentRead()
|
||||
{
|
||||
$this->client->call(Client::METHOD_GET, '/databases/' . static::$databaseId . '/collections/' . static::$collectionId . '/documents/' . static::$documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
}
|
||||
|
||||
#[BeforeMethods(['createDatabase', 'createCollection', 'createDocuments'])]
|
||||
public function benchDocumentUpdate()
|
||||
{
|
||||
$this->client->call(Client::METHOD_PATCH, '/databases/' . static::$databaseId . '/collections/' . static::$collectionId . '/documents/' . static::$documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'The Matrix Reloaded',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function provideCounts(): array
|
||||
{
|
||||
return [
|
||||
'1 Document' => ['documents' => 1],
|
||||
'10 Documents' => ['documents' => 10],
|
||||
'100 Documents' => ['documents' => 100],
|
||||
];
|
||||
}
|
||||
|
||||
public function createDatabase(array $params = [])
|
||||
{
|
||||
$database = $this->client->call(Client::METHOD_POST, '/databases', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Test Database'
|
||||
]);
|
||||
static::$databaseId = $database['body']['$id'];
|
||||
}
|
||||
|
||||
public function createCollection(array $params = [])
|
||||
{
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . static::$databaseId . '/collections', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Movies',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::write(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
]);
|
||||
static::$collectionId = $collection['body']['$id'];
|
||||
|
||||
// Create attribute
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . static::$databaseId . '/collections/' . static::$collectionId . '/attributes/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
// Wait for attribute to be ready
|
||||
sleep(2);
|
||||
}
|
||||
|
||||
public function createDocuments(array $params = [])
|
||||
{
|
||||
$count = $params['documents'] ?? 1;
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . static::$databaseId . '/collections/' . static::$collectionId . '/documents', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Captain America' . $i,
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::write(Role::user($this->getUser()['$id'])),
|
||||
]
|
||||
]);
|
||||
|
||||
static::$documentId = $response['body']['$id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Benchmarks\Services\Databases;
|
||||
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
|
||||
class DatabasesCustomClientBench extends Base
|
||||
{
|
||||
use SideClient;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Benchmarks\Services\Databases;
|
||||
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
|
||||
class DatabasesCustomServerBench extends Base
|
||||
{
|
||||
use SideServer;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Benchmarks\Services\Functions;
|
||||
|
||||
use CURLFile;
|
||||
use PhpBench\Attributes\BeforeMethods;
|
||||
use Tests\Benchmarks\Scopes\Scope;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
abstract class Base extends Scope
|
||||
{
|
||||
use ProjectCustom;
|
||||
|
||||
protected static string $functionId;
|
||||
protected static string $deploymentId;
|
||||
protected static string $executionId;
|
||||
|
||||
#[BeforeMethods(['createFunction', 'prepareDeployment', 'createDeployment', 'patchDeployment'])]
|
||||
public function benchExecutionCreate()
|
||||
{
|
||||
$this->client->call(Client::METHOD_POST, '/functions/' . static::$functionId . '/executions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
}
|
||||
|
||||
public function createFunction()
|
||||
{
|
||||
$response = $this->client->call(Client::METHOD_POST, '/functions', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'runtime' => 'php-8.0',
|
||||
'timeout' => 10,
|
||||
'execute' => [Role::users()->toString()]
|
||||
]);
|
||||
static::$functionId = $response['body']['$id'];
|
||||
}
|
||||
|
||||
public function prepareDeployment()
|
||||
{
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
|
||||
Console::execute(
|
||||
'cd ' . realpath(__DIR__ . "/../../resources/functions/php") . " && \
|
||||
tar --exclude code.tar.gz -czf code.tar.gz .",
|
||||
'',
|
||||
$stdout,
|
||||
$stderr
|
||||
);
|
||||
}
|
||||
|
||||
public function createDeployment()
|
||||
{
|
||||
$code = realpath(__DIR__ . '/../../resources/functions/php/code.tar.gz');
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/functions/' . static::$functionId . '/deployments', [
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => new CURLFile(
|
||||
$code,
|
||||
'application/x-gzip',
|
||||
\basename($code)
|
||||
),
|
||||
]);
|
||||
|
||||
static::$deploymentId = $response['body']['$id'];
|
||||
|
||||
while (true) {
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions/' . static::$functionId . '/deployments/' . static::$deploymentId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]);
|
||||
|
||||
$status = $response['body']['status'] ?? '';
|
||||
|
||||
switch ($status) {
|
||||
case '':
|
||||
case 'processing':
|
||||
case 'building':
|
||||
usleep(200);
|
||||
break;
|
||||
case 'ready':
|
||||
break 2;
|
||||
case 'failed':
|
||||
throw new \Exception('Failed to build function');
|
||||
}
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
public function patchDeployment()
|
||||
{
|
||||
$this->client->call(Client::METHOD_PATCH, '/functions/' . static::$functionId . '/deployments/' . static::$deploymentId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], []);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Benchmarks\Services\Functions;
|
||||
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
|
||||
class FunctionsCustomClientBench extends Base
|
||||
{
|
||||
use SideClient;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Benchmarks\Services\Functions;
|
||||
|
||||
use PhpBench\Attributes\BeforeMethods;
|
||||
use PhpBench\Attributes\Iterations;
|
||||
use PhpBench\Attributes\Revs;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
|
||||
class FunctionsCustomServerBench extends Base
|
||||
{
|
||||
use SideServer;
|
||||
|
||||
#[Revs(1)]
|
||||
#[Iterations(1)]
|
||||
#[BeforeMethods(['createFunction', 'prepareDeployment'])]
|
||||
public function benchDeploymentCreate()
|
||||
{
|
||||
$this->createDeployment();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Benchmarks\Services\Storage;
|
||||
|
||||
use CURLFile;
|
||||
use PhpBench\Attributes\BeforeMethods;
|
||||
use PhpBench\Attributes\ParamProviders;
|
||||
use Tests\Benchmarks\Scopes\Scope;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
|
||||
abstract class Base extends Scope
|
||||
{
|
||||
use ProjectCustom;
|
||||
|
||||
protected static string $bucketId;
|
||||
protected static string $fileId;
|
||||
|
||||
#[BeforeMethods(['createBucket'])]
|
||||
public function benchFileCreate()
|
||||
{
|
||||
$this->client->call(Client::METHOD_POST, '/storage/buckets/' . static::$bucketId . '/files', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => ID::unique(),
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::write(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../resources/logo.png'), 'image/png', 'logo.png'),
|
||||
]);
|
||||
}
|
||||
|
||||
#[ParamProviders(['provideCounts'])]
|
||||
#[BeforeMethods(['createBucket', 'createFiles'])]
|
||||
public function benchFileReadList(array $params)
|
||||
{
|
||||
$this->client->call(Client::METHOD_GET, '/storage/buckets/' . static::$bucketId . '/files', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['limit(' . $params['files'] . ')'],
|
||||
]);
|
||||
}
|
||||
|
||||
#[BeforeMethods(['createBucket', 'createFiles'])]
|
||||
public function benchFileRead()
|
||||
{
|
||||
$this->client->call(Client::METHOD_GET, '/storage/buckets/' . static::$bucketId . '/files/' . static::$fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
}
|
||||
|
||||
#[BeforeMethods(['createBucket', 'createFiles'])]
|
||||
public function benchFileUpdate()
|
||||
{
|
||||
$this->client->call(Client::METHOD_PUT, '/storage/buckets/' . static::$bucketId . '/files/' . static::$fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Updated name',
|
||||
'permissions' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
public function provideCounts(): array
|
||||
{
|
||||
return [
|
||||
'10 Files' => ['files' => 10],
|
||||
'100 Files' => ['files' => 100],
|
||||
];
|
||||
}
|
||||
|
||||
public function createBucket(array $params = [])
|
||||
{
|
||||
// Create bucket
|
||||
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket',
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::write(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'fileSecurity' => true
|
||||
]);
|
||||
static::$bucketId = $bucket['body']['$id'];
|
||||
}
|
||||
|
||||
public function createFiles(array $params = [])
|
||||
{
|
||||
$count = $params['files'] ?? 1;
|
||||
|
||||
// Create files
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$response = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . static::$bucketId . '/files', [
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../resources/logo.png'), 'image/png', 'logo.png'),
|
||||
]);
|
||||
|
||||
static::$fileId = $response['body']['$id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Benchmarks\Services\Storage;
|
||||
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
|
||||
class StorageCustomClientBench extends Base
|
||||
{
|
||||
use SideClient;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Benchmarks\Services\Storage;
|
||||
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
|
||||
class StorageCustomServerBench extends Base
|
||||
{
|
||||
use SideServer;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Benchmarks\Services\Users;
|
||||
|
||||
use PhpBench\Attributes\BeforeMethods;
|
||||
use PhpBench\Attributes\ParamProviders;
|
||||
use Tests\Benchmarks\Scopes\Scope;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
class UserCustomServerBench extends Scope
|
||||
{
|
||||
use ProjectCustom;
|
||||
use SideServer;
|
||||
|
||||
protected static string $userId;
|
||||
|
||||
public function benchUserCreate()
|
||||
{
|
||||
$id = ID::unique();
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => $id,
|
||||
'email' => 'test' . $id . '@example.com',
|
||||
'password' => 'password',
|
||||
]);
|
||||
}
|
||||
|
||||
#[ParamProviders(['provideCounts'])]
|
||||
#[BeforeMethods(['createUsers'])]
|
||||
public function benchUserReadList(array $params)
|
||||
{
|
||||
$this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['limit(' . $params['users'] . ')'],
|
||||
]);
|
||||
}
|
||||
|
||||
#[BeforeMethods(['createUsers'])]
|
||||
public function benchUserRead()
|
||||
{
|
||||
$this->client->call(Client::METHOD_GET, '/users/' . static::$userId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
}
|
||||
|
||||
#[BeforeMethods(['createUsers'])]
|
||||
public function benchUserUpdate()
|
||||
{
|
||||
$this->client->call(Client::METHOD_PUT, '/users/' . static::$userId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'New Name',
|
||||
]);
|
||||
}
|
||||
|
||||
public function createUsers(array $params = [])
|
||||
{
|
||||
$count = $params['documents'] ?? 1;
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$id = ID::unique();
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => $id,
|
||||
'email' => 'test' . $id . '@example.com',
|
||||
'password' => 'password',
|
||||
]);
|
||||
|
||||
static::$userId = $response['body']['$id'];
|
||||
}
|
||||
}
|
||||
|
||||
public function provideCounts(): array
|
||||
{
|
||||
return [
|
||||
'1 User' => ['users' => 1],
|
||||
'10 Users' => ['users' => 10],
|
||||
'100 Users' => ['users' => 100],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import http from 'k6/http';
|
||||
import { check } from 'k6';
|
||||
import { Counter } from 'k6/metrics';
|
||||
|
||||
// A simple counter for http requests
|
||||
export const requests = new Counter('http_reqs');
|
||||
|
||||
// you can specify stages of your test (ramp up/down patterns) through the options object
|
||||
// target is the number of VUs you are aiming for
|
||||
|
||||
export const options = {
|
||||
stages: [
|
||||
{ target: 50, duration: '1m' },
|
||||
// { target: 15, duration: '1m' },
|
||||
// { target: 0, duration: '1m' },
|
||||
],
|
||||
thresholds: {
|
||||
requests: ['count < 100'],
|
||||
},
|
||||
};
|
||||
|
||||
export default function () {
|
||||
const config = {
|
||||
headers: {
|
||||
'X-Appwrite-Key': '24356eb021863f81eb7dd77c7750304d0464e141cad6e9a8befa1f7d2b066fde190df3dab1e8d2639dbb82ee848da30501424923f4cd80d887ee40ad77ded62763ee489448523f6e39667f290f9a54b2ab8fad131a0bc985e6c0f760015f7f3411e40626c75646bb19d2bb2f7bf2f63130918220a206758cbc48845fd725a695',
|
||||
'X-Appwrite-Project': '60479fe35d95d'
|
||||
}}
|
||||
|
||||
const resDb = http.get('http://localhost:9501/', config);
|
||||
|
||||
check(resDb, {
|
||||
'status is 200': (r) => r.status === 200,
|
||||
});
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// k6 run tests/benchmarks/ws.js
|
||||
|
||||
import { URL } from 'https://jslib.k6.io/url/1.0.0/index.js';
|
||||
import ws from 'k6/ws';
|
||||
import { check } from 'k6';
|
||||
|
||||
export let options = {
|
||||
stages: [
|
||||
{
|
||||
duration: '10s',
|
||||
target: 500
|
||||
},
|
||||
{
|
||||
duration: '1m',
|
||||
target: 500
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default function () {
|
||||
// const url = new URL('wss://appwrite-realtime.monitor-api.com/v1/realtime');
|
||||
// url.searchParams.append('project', '604249e6b1a9f');
|
||||
const url = new URL('ws://localhost/v1/realtime');
|
||||
url.searchParams.append('project', 'console');
|
||||
url.searchParams.append('channels[]', 'files');
|
||||
|
||||
const res = ws.connect(url.toString(), function (socket) {
|
||||
let connection = false;
|
||||
let checked = false;
|
||||
let payload = null;
|
||||
socket.on('open', () => {
|
||||
connection = true;
|
||||
});
|
||||
|
||||
socket.on('message', (data) => {
|
||||
payload = data;
|
||||
checked = true;
|
||||
});
|
||||
|
||||
socket.setTimeout(function () {
|
||||
check(payload, {
|
||||
'connection opened': (r) => connection,
|
||||
'message received': (r) => checked,
|
||||
'channels are right': (r) => r === JSON.stringify({
|
||||
"type": "connected",
|
||||
"data": {
|
||||
"channels": [
|
||||
"files"
|
||||
],
|
||||
"user": null
|
||||
}
|
||||
})
|
||||
})
|
||||
socket.close();
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
check(res, { 'status is 101': (r) => r && r.status === 101 });
|
||||
}
|
||||
@@ -17,7 +17,7 @@ class AbuseTest extends Scope
|
||||
use ProjectCustom;
|
||||
use SideNone;
|
||||
|
||||
protected function setUp(): void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class UsageTest extends Scope
|
||||
|
||||
protected string $projectId;
|
||||
|
||||
protected function setUp(): void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
@@ -754,7 +754,7 @@ class UsageTest extends Scope
|
||||
$this->validateDates($response['executionsFailure']);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
public function tearDown(): void
|
||||
{
|
||||
$this->usersCount = 0;
|
||||
$this->requestsCount = 0;
|
||||
|
||||
@@ -21,7 +21,7 @@ abstract class Scope extends TestCase
|
||||
*/
|
||||
protected $endpoint = 'http://localhost/v1';
|
||||
|
||||
protected function setUp(): void
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->client = new Client();
|
||||
|
||||
@@ -30,7 +30,7 @@ abstract class Scope extends TestCase
|
||||
;
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
public function tearDown(): void
|
||||
{
|
||||
$this->client = null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user