Compare commits

...
Author SHA1 Message Date
Jake Barnby 3903b51daf Make namespaces consistent with tests 2022-11-16 15:36:55 +13:00
Jake Barnby 9dd5da479d Expose 'set' column in benchmark report 2022-11-16 15:20:13 +13:00
Jake Barnby eaa10fdba0 Expose endpoint var for config visibility 2022-11-16 15:19:11 +13:00
Jake Barnby 5b8c16b7a7 Add user benchmarks 2022-11-16 15:16:11 +13:00
Jake Barnby b4bd3622c6 Merge remote-tracking branch 'origin/master' into feat-benchmarks 2022-11-16 14:11:30 +13:00
Jake Barnby 8448e92894 One rev/iter for deployment benchmark 2022-11-04 20:32:47 +13:00
Jake Barnby dbffea2ba3 Remove static code path 2022-11-03 20:50:19 +13:00
Jake Barnby a1cd6d90a2 Fix baseline 2022-11-03 20:26:29 +13:00
Jake Barnby caceef7fa6 1 rev 1 iteration test 2022-11-03 20:04:59 +13:00
Jake Barnby a08cbe3638 Fix function code path 2022-11-03 20:03:38 +13:00
Jake Barnby 8774810314 Temp skip tests for checking benchmarking run 2022-11-02 19:14:38 +13:00
Jake Barnby 7cbe827404 Give +x to all bin scripts locally 2022-11-02 19:13:15 +13:00
Jake Barnby 9335563e87 Add bin script to run benchmarks inside container 2022-11-02 19:12:26 +13:00
Jake Barnby 3c9d4b877a test add explicit safe directories 2022-11-02 16:45:10 +13:00
Jake Barnby 0b45c508b7 Pin ubuntu version for git fixes 2022-11-02 15:52:06 +13:00
Jake Barnby 84df9c5478 Add test baseline 2022-11-02 13:21:34 +13:00
Jake Barnby aaf896f27e Fix tests 2022-11-02 13:16:42 +13:00
Jake Barnby 50c7c7aa7a Fix transitive dependency version using PHP 8.1 2022-11-01 19:58:56 +13:00
Jake Barnby dae75a6128 Update contributing 2022-11-01 16:19:11 +13:00
Jake Barnby f1d8403fba Update workflows 2022-11-01 16:18:36 +13:00
Jake Barnby f1abf00f09 Merge remote-tracking branch 'origin/master' into feat-benchmarks
# Conflicts:
#	composer.lock
2022-11-01 11:39:57 +13:00
Jake Barnby f52e4c3e63 Add benchmarks base 2022-10-31 19:46:20 +13:00
44 changed files with 2125 additions and 188 deletions
+42
View File
@@ -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
+1 -1
View File
@@ -12,7 +12,7 @@ on:
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
strategy:
fail-fast: false
-20
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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/
+5
View File
@@ -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
View File
@@ -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
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
+23 -3
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -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
+17
View File
@@ -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"]
}
}
}
+14
View File
@@ -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();
}
}
+116
View File
@@ -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],
];
}
}
-34
View File
@@ -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,
});
}
-59
View File
@@ -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 });
}
+1 -1
View File
@@ -17,7 +17,7 @@ class AbuseTest extends Scope
use ProjectCustom;
use SideNone;
protected function setUp(): void
public function setUp(): void
{
parent::setUp();
+2 -2
View File
@@ -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;
+2 -2
View File
@@ -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;
}