Merge branch 'new-pool-adapter' into documents-db-api

This commit is contained in:
ArnabChatterjee20k
2026-01-22 12:34:22 +05:30
7 changed files with 199 additions and 31 deletions
+1
View File
@@ -138,3 +138,4 @@ _APP_PROJECT_REGIONS=default
_APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000
_APP_STATS_USAGE_DUAL_WRITING_DBS=database_db_main
_APP_TRUSTED_HEADERS=x-forwarded-for
_APP_POOL_ADAPTER=stack
+5 -1
View File
@@ -25,6 +25,8 @@ use Utopia\Logger\Adapter\LogOwl;
use Utopia\Logger\Adapter\Raygun;
use Utopia\Logger\Adapter\Sentry;
use Utopia\Logger\Logger;
use Utopia\Pools\Adapter\Stack as StackPool;
use Utopia\Pools\Adapter\Swoole as SwoolePool;
use Utopia\Mongo\Client as MongoClient;
use Utopia\Pools\Group;
use Utopia\Pools\Pool;
@@ -338,7 +340,9 @@ $register->set('pools', function () {
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'),
};
$pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn, $key) {
$poolAdapter = System::getEnv('_APP_POOL_ADAPTER', default: 'stack') === 'swoole' ? new SwoolePool() : new StackPool();
$pool = new Pool($poolAdapter, $name, $poolSize, function () use ($type, $resource, $dsn, $key) {
// Get Adapter
switch ($type) {
case 'database':
+1 -1
View File
@@ -80,7 +80,7 @@
"utopia-php/migration": "dev-multitype-db as 1.4.5",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.8.*",
"utopia-php/pools": "1.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/queue": "0.15.*",
"utopia-php/registry": "0.5.*",
Generated
+40 -28
View File
@@ -1361,15 +1361,19 @@
{
"name": "open-telemetry/exporter-otlp",
"version": "1.3.4",
"version": "1.3.4",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/exporter-otlp.git",
"reference": "62e680d587beb42e5247aa6ecd89ad1ca406e8ca"
"reference": "62e680d587beb42e5247aa6ecd89ad1ca406e8ca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/62e680d587beb42e5247aa6ecd89ad1ca406e8ca",
"reference": "62e680d587beb42e5247aa6ecd89ad1ca406e8ca",
"url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/62e680d587beb42e5247aa6ecd89ad1ca406e8ca",
"reference": "62e680d587beb42e5247aa6ecd89ad1ca406e8ca",
"shasum": ""
},
"require": {
@@ -1421,6 +1425,7 @@
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2026-01-15T09:31:34+00:00"
"time": "2026-01-15T09:31:34+00:00"
},
{
"name": "open-telemetry/gen-otlp-protobuf",
@@ -1488,15 +1493,19 @@
{
"name": "open-telemetry/sdk",
"version": "1.11.0",
"version": "1.11.0",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/sdk.git",
"reference": "d91f21addcdb42da9a451c002777f8318432461a"
"reference": "d91f21addcdb42da9a451c002777f8318432461a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/d91f21addcdb42da9a451c002777f8318432461a",
"reference": "d91f21addcdb42da9a451c002777f8318432461a",
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/d91f21addcdb42da9a451c002777f8318432461a",
"reference": "d91f21addcdb42da9a451c002777f8318432461a",
"shasum": ""
},
"require": {
@@ -1581,6 +1590,7 @@
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2026-01-15T11:21:03+00:00"
"time": "2026-01-15T11:21:03+00:00"
},
{
"name": "open-telemetry/sem-conv",
@@ -3721,16 +3731,16 @@
},
{
"name": "utopia-php/cache",
"version": "0.13.2",
"version": "0.13.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cache.git",
"reference": "5768498c9f451482f0bf3eede4d6452ddcd4a0f6"
"reference": "355707ab2c0090435059216165db86976b68a126"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/5768498c9f451482f0bf3eede4d6452ddcd4a0f6",
"reference": "5768498c9f451482f0bf3eede4d6452ddcd4a0f6",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/355707ab2c0090435059216165db86976b68a126",
"reference": "355707ab2c0090435059216165db86976b68a126",
"shasum": ""
},
"require": {
@@ -3738,7 +3748,7 @@
"ext-memcached": "*",
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/pools": "0.8.*",
"utopia-php/pools": "1.*",
"utopia-php/telemetry": "*"
},
"require-dev": {
@@ -3767,9 +3777,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/cache/issues",
"source": "https://github.com/utopia-php/cache/tree/0.13.2"
"source": "https://github.com/utopia-php/cache/tree/0.13.3"
},
"time": "2025-12-17T08:55:43+00:00"
"time": "2026-01-16T07:54:34+00:00"
},
{
"name": "utopia-php/cli",
@@ -3963,16 +3973,16 @@
},
{
"name": "utopia-php/database",
"version": "4.5.2",
"version": "4.6.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "8e6a033d4da09a2f2ac1f79fd85fcfa2da018d23"
"reference": "b5c16caf4f6b12fa2c04d5a48f6e5785c99da8df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/8e6a033d4da09a2f2ac1f79fd85fcfa2da018d23",
"reference": "8e6a033d4da09a2f2ac1f79fd85fcfa2da018d23",
"url": "https://api.github.com/repos/utopia-php/database/zipball/b5c16caf4f6b12fa2c04d5a48f6e5785c99da8df",
"reference": "b5c16caf4f6b12fa2c04d5a48f6e5785c99da8df",
"shasum": ""
},
"require": {
@@ -3983,7 +3993,7 @@
"utopia-php/cache": "0.13.*",
"utopia-php/framework": "0.33.*",
"utopia-php/mongo": "0.11.*",
"utopia-php/pools": "0.8.*"
"utopia-php/pools": "1.*"
},
"require-dev": {
"fakerphp/faker": "1.23.*",
@@ -4015,9 +4025,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/4.5.2"
"source": "https://github.com/utopia-php/database/tree/4.6.0"
},
"time": "2026-01-15T04:23:30+00:00"
"time": "2026-01-16T12:35:16+00:00"
},
{
"name": "utopia-php/detector",
@@ -4815,16 +4825,16 @@
},
{
"name": "utopia-php/pools",
"version": "0.8.3",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/pools.git",
"reference": "ad7d6ba946376e81c603204285ce9a674b6502b8"
"reference": "74ba7dc985c2f629df8cf08ed95507955e3bcf86"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/pools/zipball/ad7d6ba946376e81c603204285ce9a674b6502b8",
"reference": "ad7d6ba946376e81c603204285ce9a674b6502b8",
"url": "https://api.github.com/repos/utopia-php/pools/zipball/74ba7dc985c2f629df8cf08ed95507955e3bcf86",
"reference": "74ba7dc985c2f629df8cf08ed95507955e3bcf86",
"shasum": ""
},
"require": {
@@ -4834,7 +4844,8 @@
"require-dev": {
"laravel/pint": "1.*",
"phpstan/phpstan": "1.*",
"phpunit/phpunit": "11.*"
"phpunit/phpunit": "11.*",
"swoole/ide-helper": "5.1.2"
},
"type": "library",
"autoload": {
@@ -4861,9 +4872,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/pools/issues",
"source": "https://github.com/utopia-php/pools/tree/0.8.3"
"source": "https://github.com/utopia-php/pools/tree/1.0.0"
},
"time": "2025-12-17T09:35:18+00:00"
"time": "2026-01-15T12:34:17+00:00"
},
{
"name": "utopia-php/preloader",
@@ -4920,16 +4931,16 @@
},
{
"name": "utopia-php/queue",
"version": "0.15.0",
"version": "0.15.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/queue.git",
"reference": "6abb268ba7ec00dea4e5201b007776ea1bce9242"
"reference": "e551606385990ec7901d222017c4cfc2749a518c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/6abb268ba7ec00dea4e5201b007776ea1bce9242",
"reference": "6abb268ba7ec00dea4e5201b007776ea1bce9242",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/e551606385990ec7901d222017c4cfc2749a518c",
"reference": "e551606385990ec7901d222017c4cfc2749a518c",
"shasum": ""
},
"require": {
@@ -4938,7 +4949,7 @@
"utopia-php/console": "0.0.*",
"utopia-php/fetch": "0.5.*",
"utopia-php/framework": "0.33.*",
"utopia-php/pools": "0.8.*",
"utopia-php/pools": "1.*",
"utopia-php/telemetry": "*"
},
"require-dev": {
@@ -4980,9 +4991,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/queue/issues",
"source": "https://github.com/utopia-php/queue/tree/0.15.0"
"source": "https://github.com/utopia-php/queue/tree/0.15.1"
},
"time": "2026-01-06T12:41:51+00:00"
"time": "2026-01-16T07:54:54+00:00"
},
{
"name": "utopia-php/registry",
@@ -9111,4 +9122,5 @@
"php": "8.3"
},
"plugin-api-version": "2.9.0"
"plugin-api-version": "2.9.0"
}
+1
View File
@@ -319,6 +319,7 @@ services:
- _APP_LOGGING_CONFIG
- _APP_LOGGING_CONFIG_REALTIME
- _APP_DATABASE_SHARED_TABLES
- _APP_POOL_ADAPTER=swoole
appwrite-worker-audits:
entrypoint: worker-audits
@@ -1979,7 +1979,7 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals(1, count($sessions));
$this->assertEquals($sessionId2, $sessions[0]['$id']);
});
}, 120_000, 300);
/**
* Reset Limit
@@ -4,6 +4,7 @@ namespace Tests\E2E\Services\Realtime;
use CURLFile;
use Exception;
use Swoole\Coroutine;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
@@ -3123,6 +3124,155 @@ class RealtimeCustomClientTest extends Scope
$client->close();
}
/**
* Simulate concurrent realtime traffic using Swoole coroutines.
* Opens multiple websocket clients concurrently, then performs create/update/delete ops.
*/
public function testConcurrentRealtimeTrafficCoroutines()
{
if (!class_exists(\Swoole\Coroutine::class)) {
$this->markTestSkipped('Swoole Coroutine not available in this environment.');
}
$user = $this->getUser();
$session = $user['session'] ?? '';
$projectId = $this->getProject()['$id'];
Coroutine\run(function () use ($session, $projectId) {
$headers = [
'origin' => 'http://localhost',
'cookie' => 'a_session_' . $projectId . '=' . $session
];
$clientCount = 5;
$clients = [];
for ($i = 0; $i < $clientCount; $i++) {
$clients[] = $this->getWebsocket(['documents', 'collections'], $headers);
}
foreach ($clients as $client) {
$response = json_decode($client->receive(), true);
$this->assertEquals('connected', $response['type']);
}
// Setup DB/collection/attribute
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'databaseId' => ID::unique(),
'name' => 'Concurrent DB',
]);
$databaseId = $database['body']['$id'];
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::unique(),
'name' => 'Concurrent Collection',
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
'documentSecurity' => true,
]);
$collectionId = $collection['body']['$id'];
$this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 64,
'required' => true,
]);
Coroutine::sleep(1);
$creates = [
['name' => 'Doc A'],
['name' => 'Doc B'],
['name' => 'Doc C'],
['name' => 'Doc D'],
['name' => 'Doc E'],
['name' => 'Doc F'],
];
$expectedEvents = count($creates);
// Per-client receipts
$receivedEvents = array_fill(0, $clientCount, []);
// Launch receiver coroutines (one per client)
foreach ($clients as $idx => $client) {
Coroutine::create(function () use ($client, &$receivedEvents, $expectedEvents, $idx) {
$local = [];
for ($i = 0; $i < $expectedEvents; $i++) {
$event = json_decode($client->receive(), true);
$local[] = $event;
}
$receivedEvents[$idx] = $local;
});
}
// Create docs
foreach ($creates as $payload) {
$this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'documentId' => ID::unique(),
'data' => $payload,
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
}
// Wait for receivers to collect; timeout ~10s
$deadline = microtime(true) + 10;
while (microtime(true) < $deadline) {
$done = true;
foreach ($receivedEvents as $events) {
if (count($events) < $expectedEvents) {
$done = false;
break;
}
}
if ($done) {
break;
}
Coroutine::sleep(0.1);
}
$expectedNames = array_column($creates, 'name');
for ($c = 0; $c < $clientCount; $c++) {
$events = $receivedEvents[$c];
$this->assertCount($expectedEvents, $events, 'Unexpected event count on client '.$c);
$seen = [];
foreach ($events as $event) {
$this->assertEquals('event', $event['type']);
$this->assertArrayHasKey('payload', $event['data']);
$seen[] = $event['data']['payload']['name'] ?? '';
}
foreach ($expectedNames as $name) {
$this->assertContains($name, $seen);
}
}
foreach ($clients as $client) {
$client->close();
}
});
}
public function testChannelTablesDB()
{
$user = $this->getUser();