From 860f077787aba9fcf35e0ff37c348f63b2ca8c4c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 16 Nov 2022 12:50:37 +1300 Subject: [PATCH 1/6] Use separate registry entry for worker pools --- app/init.php | 172 ++++++++++++++++++++++++++++++++++++++++++++++- app/realtime.php | 24 +++---- composer.json | 2 +- composer.lock | 27 +++----- 4 files changed, 192 insertions(+), 33 deletions(-) diff --git a/app/init.php b/app/init.php index af87960325..ba7d2c7a03 100644 --- a/app/init.php +++ b/app/init.php @@ -497,7 +497,7 @@ $register->set('logger', function () { $adapter = new $classname($providerConfig); return new Logger($adapter); }); -$register->set('pools', function ($size = APP_DEFAULT_POOL_SIZE) { +$register->set('pools', function () { $group = new Group(); $fallbackForDB = AppwriteURL::unparse([ @@ -621,7 +621,175 @@ $register->set('pools', function ($size = APP_DEFAULT_POOL_SIZE) { break; } - $pool = new Pool($name, $size, function () use ($type, $resource, $dsn) { + $pool = new Pool($name, APP_DEFAULT_POOL_SIZE, function () use ($type, $resource, $dsn) { + // Get Adapter + $adapter = null; + + switch ($type) { + case 'database': + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($resource()), + 'mysql' => new MySQL($resource()), + default => null + }; + + $adapter->setDefaultDatabase($dsn->getDatabase()); + + break; + case 'queue': + $adapter = $resource(); + break; + case 'pubsub': + $adapter = $resource(); + break; + case 'cache': + $adapter = match ($dsn->getScheme()) { + 'redis' => new RedisCache($resource()), + default => null + }; + break; + + default: + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); + break; + } + + return $adapter; + }); + + $group->add($pool); + } + + Config::setParam('pools-' . $key, $config); + } + + return $group; +}); +$register->set('workerPools', function () { + $group = new Group(); + + $fallbackForDB = AppwriteURL::unparse([ + 'scheme' => 'mariadb', + 'host' => App::getEnv('_APP_DB_HOST', 'mariadb'), + 'port' => App::getEnv('_APP_DB_PORT', '3306'), + 'user' => App::getEnv('_APP_DB_USER', ''), + 'pass' => App::getEnv('_APP_DB_PASS', ''), + ]); + $fallbackForRedis = AppwriteURL::unparse([ + 'scheme' => 'redis', + 'host' => App::getEnv('_APP_REDIS_HOST', 'redis'), + 'port' => App::getEnv('_APP_REDIS_PORT', '6379'), + 'user' => App::getEnv('_APP_REDIS_USER', ''), + 'pass' => App::getEnv('_APP_REDIS_PASS', ''), + ]); + + $connections = [ + 'console' => [ + 'type' => 'database', + 'dsns' => App::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), + 'multiple' => false, + 'schemes' => ['mariadb', 'mysql'], + ], + 'database' => [ + 'type' => 'database', + 'dsns' => App::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), + 'multiple' => true, + 'schemes' => ['mariadb', 'mysql'], + ], + 'queue' => [ + 'type' => 'queue', + 'dsns' => App::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'pubsub' => [ + 'type' => 'pubsub', + 'dsns' => App::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'cache' => [ + 'type' => 'cache', + 'dsns' => App::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), + 'multiple' => true, + 'schemes' => ['redis'], + ], + ]; + + foreach ($connections as $key => $connection) { + $type = $connection['type'] ?? ''; + $dsns = $connection['dsns'] ?? ''; + $multipe = $connection['multiple'] ?? false; + $schemes = $connection['schemes'] ?? []; + $config = []; + $dsns = explode(',', $connection['dsns'] ?? ''); + + foreach ($dsns as &$dsn) { + $dsn = explode('=', $dsn); + $name = ($multipe) ? $key . '_' . $dsn[0] : $key; + $dsn = $dsn[1] ?? ''; + $config[] = $name; + + if (empty($dsn)) { + //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); + continue; + } + + $dsn = new DSN($dsn); + $dsnHost = $dsn->getHost(); + $dsnPort = $dsn->getPort(); + $dsnUser = $dsn->getUser(); + $dsnPass = $dsn->getPassword(); + $dsnScheme = $dsn->getScheme(); + $dsnDatabase = $dsn->getDatabase(); + + if (!in_array($dsnScheme, $schemes)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); + } + + /** + * Get Resource + * + * Creation could be reused accross connection types like database, cache, queue, etc. + * + * Resource assignment to an adapter will happen below. + */ + + switch ($dsnScheme) { + case 'mysql': + case 'mariadb': + $resource = function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { + return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { + return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( + PDO::ATTR_TIMEOUT => 3, // Seconds + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_ERRMODE => App::isDevelopment() ? PDO::ERRMODE_WARNING : PDO::ERRMODE_SILENT, // If in production mode, warnings are not displayed + PDO::ATTR_EMULATE_PREPARES => true, + PDO::ATTR_STRINGIFY_FETCHES => true + )); + }); + }; + break; + case 'redis': + $resource = function () use ($dsnHost, $dsnPort, $dsnPass) { + $redis = new Redis(); + @$redis->pconnect($dsnHost, (int)$dsnPort); + if ($dsnPass) { + $redis->auth($dsnPass); + } + $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); + + return $redis; + }; + break; + + default: + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid scheme"); + break; + } + + $pool = new Pool($name, getWorkerPoolSize(), function () use ($type, $resource, $dsn) { // Get Adapter $adapter = null; diff --git a/app/realtime.php b/app/realtime.php index 25c0fee858..22c74168dc 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -37,7 +37,7 @@ function getConsoleDB(): Database global $register; /** @var \Utopia\Pools\Group $pools */ - $pools = $register->get('pools', args: [getWorkerPoolSize()]); + $pools = $register->get('workerPools'); $dbAdapter = $pools ->get('console') @@ -57,7 +57,7 @@ function getProjectDB(Document $project): Database global $register; /** @var \Utopia\Pools\Group $pools */ - $pools = $register->get('pools', args: [getWorkerPoolSize()]); + $pools = $register->get('workerPools'); if ($project->isEmpty() || $project->getId() === 'console') { return getConsoleDB(); @@ -79,7 +79,7 @@ function getCache(): Cache { global $register; - $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ + $pools = $register->get('workerPools'); /** @var \Utopia\Pools\Group $pools */ $list = Config::getParam('pools-cache', []); $adapters = []; @@ -187,7 +187,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume sleep(DATABASE_RECONNECT_SLEEP); } } while (true); - $register->get('pools')->reclaim(); + $register->get('workerPools')->reclaim(); }); /** @@ -213,7 +213,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume } catch (\Throwable $th) { call_user_func($logError, $th, "updateWorkerDocument"); } finally { - $register->get('pools')->reclaim(); + $register->get('workerPools')->reclaim(); } }); }); @@ -274,7 +274,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, ])); } - $register->get('pools')->reclaim(); + $register->get('workerPools')->reclaim(); } /** * Sending test message for SDK E2E tests every 5 seconds. @@ -309,7 +309,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, } $start = time(); - $redis = $register->get('pools')->get('pubsub')->pop()->getResource(); /** @var Redis $redis */ + $redis = $register->get('workerPools')->get('pubsub')->pop()->getResource(); /** @var Redis $redis */ $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); if ($redis->ping(true)) { @@ -338,7 +338,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime->subscribe($projectId, $connection, $roles, $realtime->connections[$connection]['channels']); - $register->get('pools')->reclaim(); + $register->get('workerPools')->reclaim(); } } @@ -370,7 +370,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, sleep(DATABASE_RECONNECT_SLEEP); continue; } finally { - $register->get('pools')->reclaim(); + $register->get('workerPools')->reclaim(); } } @@ -384,7 +384,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::info("Connection open (user: {$connection})"); - App::setResource('pools', fn() => $register->get('pools')); + App::setResource('pools', fn() => $register->get('workerPools')); App::setResource('request', fn() => $request); App::setResource('response', fn() => $response); @@ -480,7 +480,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::error('[Error] Message: ' . $response['data']['message']); } } finally { - $register->get('pools')->reclaim(); + $register->get('workerPools')->reclaim(); } }); @@ -575,7 +575,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re $server->close($connection, $th->getCode()); } } finally { - $register->get('pools')->reclaim(); + $register->get('workerPools')->reclaim(); } }); diff --git a/composer.json b/composer.json index 36aed0d96b..3dd374b8e1 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,7 @@ "utopia-php/platform": "0.3.*", "utopia-php/pools": "0.4.*", "utopia-php/preloader": "0.2.*", - "utopia-php/registry": "dev-feat-allow-params as 0.5.0", + "utopia-php/registry": "0.5.0", "utopia-php/storage": "0.11.*", "utopia-php/swoole": "0.5.*", "utopia-php/websocket": "0.1.0", diff --git a/composer.lock b/composer.lock index f042279e4f..2e39916a0c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b125039c64ae4cbe0d2a1b57322d0ebe", + "content-hash": "bf3e2ed6ee8e49ab74af97b368b89a63", "packages": [ { "name": "adhocore/jwt", @@ -2359,16 +2359,16 @@ }, { "name": "utopia-php/registry", - "version": "dev-feat-allow-params", + "version": "0.5.0", "source": { "type": "git", "url": "https://github.com/utopia-php/registry.git", - "reference": "6c571f8f4127094b3af8909d1b595fd6b937255d" + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/registry/zipball/6c571f8f4127094b3af8909d1b595fd6b937255d", - "reference": "6c571f8f4127094b3af8909d1b595fd6b937255d", + "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", "shasum": "" }, "require": { @@ -2405,9 +2405,9 @@ ], "support": { "issues": "https://github.com/utopia-php/registry/issues", - "source": "https://github.com/utopia-php/registry/tree/feat-allow-params" + "source": "https://github.com/utopia-php/registry/tree/0.5.0" }, - "time": "2022-11-09T02:23:35+00:00" + "time": "2021-03-10T10:45:22+00:00" }, { "name": "utopia-php/storage", @@ -5215,18 +5215,9 @@ "time": "2022-09-28T08:42:51+00:00" } ], - "aliases": [ - { - "package": "utopia-php/registry", - "version": "dev-feat-allow-params", - "alias": "0.5.0", - "alias_normalized": "0.5.0.0" - } - ], + "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "utopia-php/registry": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { From c46ba06c51a4b698a6e5c38ff89fa03cadf4b89e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 16 Nov 2022 19:57:12 +1300 Subject: [PATCH 2/6] Update env var name --- .env | 2 +- CHANGES.md | 2 +- app/config/variables.php | 2 +- app/views/install/compose.phtml | 6 +++--- docker-compose.yml | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.env b/.env index d116bf3dde..e5ee0947d2 100644 --- a/.env +++ b/.env @@ -23,7 +23,7 @@ _APP_DB_SCHEMA=appwrite _APP_DB_USER=user _APP_DB_PASS=password _APP_DB_ROOT_PASS=rootsecretpassword -_APP_DB_MAX_CONNECTIONS=251 +_APP_CONNECTIONS_MAX_CONNECTIONS=251 _APP_CONNECTIONS_DB_PROJECT=db_fra1_02=mariadb://user:password@mariadb:3306/appwrite _APP_CONNECTIONS_DB_CONSOLE=db_fra1_01=mariadb://user:password@mariadb:3306/appwrite _APP_CONNECTIONS_CACHE=redis_fra1_01=redis://redis:6379 diff --git a/CHANGES.md b/CHANGES.md index 62c0203d12..cece10bf9c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,7 @@ # TBD - Replace Appwrite executor with OpenRuntimes Executor [#4650](https://github.com/appwrite/appwrite/pull/4650) -- Add `_APP_DB_MAX_CONNECTIONS` env var [#4673](https://github.com/appwrite/appwrite/pull/4673) +- Add `_APP_CONNECTIONS_MAX_CONNECTIONS` env var [#4673](https://github.com/appwrite/appwrite/pull/4673) - Update Traefik 2.7 -> 2.9 [#4673](https://github.com/appwrite/appwrite/pull/4673) - Increase Traefik TCP + file limits [#4673](https://github.com/appwrite/appwrite/pull/4673) diff --git a/app/config/variables.php b/app/config/variables.php index 96937d9ba5..bffba33d62 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -307,7 +307,7 @@ return [ 'filter' => 'password' ], [ - 'name' => '_APP_DB_MAX_CONNECTIONS', + 'name' => '_APP_CONNECTIONS_MAX_CONNECTIONS', 'description' => 'MariaDB server maximum connections.', 'introduction' => 'TBD', 'default' => 251, diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 0b83a12729..d45061a0bd 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -102,7 +102,7 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_MAX_CONNECTIONS + - _APP_CONNECTIONS_MAX_CONNECTIONS - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -197,7 +197,7 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_MAX_CONNECTIONS + - _APP_CONNECTIONS_MAX_CONNECTIONS - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -622,7 +622,7 @@ services: - MYSQL_DATABASE=${_APP_DB_SCHEMA} - MYSQL_USER=${_APP_DB_USER} - MYSQL_PASSWORD=${_APP_DB_PASS} - command: 'mysqld --innodb-flush-method=fsync --max_connections=${_APP_DB_MAX_CONNECTIONS}' + command: 'mysqld --innodb-flush-method=fsync --max_connections=${_APP_CONNECTIONS_MAX_CONNECTIONS}' redis: image: redis:7.0.4-alpine diff --git a/docker-compose.yml b/docker-compose.yml index 182d35cb41..b597f8a0a2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -123,7 +123,7 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_MAX_CONNECTIONS + - _APP_CONNECTIONS_MAX_CONNECTIONS - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -228,7 +228,7 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_MAX_CONNECTIONS + - _APP_CONNECTIONS_MAX_CONNECTIONS - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -741,7 +741,7 @@ services: - MYSQL_DATABASE=${_APP_DB_SCHEMA} - MYSQL_USER=${_APP_DB_USER} - MYSQL_PASSWORD=${_APP_DB_PASS} - command: 'mysqld --innodb-flush-method=fsync --max_connections=${_APP_DB_MAX_CONNECTIONS}' + command: 'mysqld --innodb-flush-method=fsync --max_connections=${_APP_CONNECTIONS_MAX_CONNECTIONS}' # smtp: # image: appwrite/smtp:1.2.0 From 750dcc2a568494d10ed166f1df6ad73fdaa36982 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 16 Nov 2022 20:00:20 +1300 Subject: [PATCH 3/6] Comment out variable --- app/config/variables.php | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/app/config/variables.php b/app/config/variables.php index bffba33d62..2a77eee41d 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -315,24 +315,24 @@ return [ 'question' => '', 'filter' => '' ], - [ - 'name' => '_APP_CONNECTIONS_DB_PROJECT', - 'description' => 'A list of comma-separated key value pairs representing Project DBs where key is the database name and value is the DSN connection string.', - 'introduction' => 'TBD', - 'default' => 'db_fra1_01=mysql://user:password@mariadb:3306/appwrite', - 'required' => true, - 'question' => '', - 'filter' => '' - ], - [ - 'name' => '_APP_CONNECTIONS_DB_CONSOLE', - 'description' => 'A key value pair representing the Console DB where key is the database name and value is the DSN connection string.', - 'introduction' => 'TBD', - 'default' => 'db_fra1_01=mysql://user:password@mariadb:3306/appwrite', - 'required' => true, - 'question' => '', - 'filter' => '' - ] +// [ +// 'name' => '_APP_CONNECTIONS_DB_PROJECT', +// 'description' => 'A list of comma-separated key value pairs representing Project DBs where key is the database name and value is the DSN connection string.', +// 'introduction' => 'TBD', +// 'default' => 'db_fra1_01=mysql://user:password@mariadb:3306/appwrite', +// 'required' => true, +// 'question' => '', +// 'filter' => '' +// ], +// [ +// 'name' => '_APP_CONNECTIONS_DB_CONSOLE', +// 'description' => 'A key value pair representing the Console DB where key is the database name and value is the DSN connection string.', +// 'introduction' => 'TBD', +// 'default' => 'db_fra1_01=mysql://user:password@mariadb:3306/appwrite', +// 'required' => true, +// 'question' => '', +// 'filter' => '' +// ] ], ], [ From 87bc593570742707d2843d28b196a557313190d0 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 16 Nov 2022 20:18:59 +1300 Subject: [PATCH 4/6] Pull calculation into pools callback --- app/init.php | 194 +++------------------------------------------------ 1 file changed, 8 insertions(+), 186 deletions(-) diff --git a/app/init.php b/app/init.php index ba7d2c7a03..c921cb9c6c 100644 --- a/app/init.php +++ b/app/init.php @@ -97,7 +97,6 @@ const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 501; const APP_VERSION_STABLE = '1.0.3'; -const APP_DEFAULT_POOL_SIZE = 64; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; @@ -548,173 +547,16 @@ $register->set('pools', function () { ], ]; - foreach ($connections as $key => $connection) { - $type = $connection['type'] ?? ''; - $dsns = $connection['dsns'] ?? ''; - $multipe = $connection['multiple'] ?? false; - $schemes = $connection['schemes'] ?? []; - $config = []; - $dsns = explode(',', $connection['dsns'] ?? ''); + $instances = 2; // REST, Realtime + $workerCount = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)); + $maxConnections = App::getenv('_APP_CONNECTIONS_MAX_CONNECTIONS', 251); + $instanceConnections = $maxConnections / $instances; - foreach ($dsns as &$dsn) { - $dsn = explode('=', $dsn); - $name = ($multipe) ? $key . '_' . $dsn[0] : $key; - $dsn = $dsn[1] ?? ''; - $config[] = $name; - - if (empty($dsn)) { - //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); - continue; - } - - $dsn = new DSN($dsn); - $dsnHost = $dsn->getHost(); - $dsnPort = $dsn->getPort(); - $dsnUser = $dsn->getUser(); - $dsnPass = $dsn->getPassword(); - $dsnScheme = $dsn->getScheme(); - $dsnDatabase = $dsn->getDatabase(); - - if (!in_array($dsnScheme, $schemes)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); - } - - /** - * Get Resource - * - * Creation could be reused accross connection types like database, cache, queue, etc. - * - * Resource assignment to an adapter will happen below. - */ - - switch ($dsnScheme) { - case 'mysql': - case 'mariadb': - $resource = function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( - PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_ERRMODE => App::isDevelopment() ? PDO::ERRMODE_WARNING : PDO::ERRMODE_SILENT, // If in production mode, warnings are not displayed - PDO::ATTR_EMULATE_PREPARES => true, - PDO::ATTR_STRINGIFY_FETCHES => true - )); - }); - }; - break; - case 'redis': - $resource = function () use ($dsnHost, $dsnPort, $dsnPass) { - $redis = new Redis(); - @$redis->pconnect($dsnHost, (int)$dsnPort); - if ($dsnPass) { - $redis->auth($dsnPass); - } - $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); - - return $redis; - }; - break; - - default: - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid scheme"); - break; - } - - $pool = new Pool($name, APP_DEFAULT_POOL_SIZE, function () use ($type, $resource, $dsn) { - // Get Adapter - $adapter = null; - - switch ($type) { - case 'database': - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($resource()), - 'mysql' => new MySQL($resource()), - default => null - }; - - $adapter->setDefaultDatabase($dsn->getDatabase()); - - break; - case 'queue': - $adapter = $resource(); - break; - case 'pubsub': - $adapter = $resource(); - break; - case 'cache': - $adapter = match ($dsn->getScheme()) { - 'redis' => new RedisCache($resource()), - default => null - }; - break; - - default: - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); - break; - } - - return $adapter; - }); - - $group->add($pool); - } - - Config::setParam('pools-' . $key, $config); + if ($workerCount > $instanceConnections) { + throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); } - return $group; -}); -$register->set('workerPools', function () { - $group = new Group(); - - $fallbackForDB = AppwriteURL::unparse([ - 'scheme' => 'mariadb', - 'host' => App::getEnv('_APP_DB_HOST', 'mariadb'), - 'port' => App::getEnv('_APP_DB_PORT', '3306'), - 'user' => App::getEnv('_APP_DB_USER', ''), - 'pass' => App::getEnv('_APP_DB_PASS', ''), - ]); - $fallbackForRedis = AppwriteURL::unparse([ - 'scheme' => 'redis', - 'host' => App::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => App::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => App::getEnv('_APP_REDIS_USER', ''), - 'pass' => App::getEnv('_APP_REDIS_PASS', ''), - ]); - - $connections = [ - 'console' => [ - 'type' => 'database', - 'dsns' => App::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), - 'multiple' => false, - 'schemes' => ['mariadb', 'mysql'], - ], - 'database' => [ - 'type' => 'database', - 'dsns' => App::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), - 'multiple' => true, - 'schemes' => ['mariadb', 'mysql'], - ], - 'queue' => [ - 'type' => 'queue', - 'dsns' => App::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'pubsub' => [ - 'type' => 'pubsub', - 'dsns' => App::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'cache' => [ - 'type' => 'cache', - 'dsns' => App::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), - 'multiple' => true, - 'schemes' => ['redis'], - ], - ]; + $poolSize = (int)($instanceConnections / $workerCount); foreach ($connections as $key => $connection) { $type = $connection['type'] ?? ''; @@ -789,7 +631,7 @@ $register->set('workerPools', function () { break; } - $pool = new Pool($name, getWorkerPoolSize(), function () use ($type, $resource, $dsn) { + $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { // Get Adapter $adapter = null; @@ -1291,26 +1133,6 @@ function getDevice($root): Device } } -/** - * Get database connection pool size for worker processes. - * - * @return int - * @throws \Exception - */ -function getWorkerPoolSize(): int -{ - $reservedConnections = APP_DEFAULT_POOL_SIZE; // Pool of default size is reserved for the HTTP API - $workerCount = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)); - $maxConnections = App::getenv('_APP_DB_MAX_CONNECTIONS', 251); - $workerConnections = $maxConnections - $reservedConnections; - - if ($workerCount > $workerConnections) { - throw new \Exception('Worker pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); - } - - return (int)($workerConnections / $workerCount); -} - App::setResource('mode', function ($request) { /** @var Appwrite\Utopia\Request $request */ From c4bbf08d523f8453c9135aeb9c2984a3221903c1 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 16 Nov 2022 20:20:08 +1300 Subject: [PATCH 5/6] Revert realtime from workerPools -> pools --- app/realtime.php | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index 22c74168dc..54469ebf77 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -37,7 +37,7 @@ function getConsoleDB(): Database global $register; /** @var \Utopia\Pools\Group $pools */ - $pools = $register->get('workerPools'); + $pools = $register->get('pools'); $dbAdapter = $pools ->get('console') @@ -57,7 +57,7 @@ function getProjectDB(Document $project): Database global $register; /** @var \Utopia\Pools\Group $pools */ - $pools = $register->get('workerPools'); + $pools = $register->get('pools'); if ($project->isEmpty() || $project->getId() === 'console') { return getConsoleDB(); @@ -79,7 +79,7 @@ function getCache(): Cache { global $register; - $pools = $register->get('workerPools'); /** @var \Utopia\Pools\Group $pools */ + $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ $list = Config::getParam('pools-cache', []); $adapters = []; @@ -187,7 +187,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume sleep(DATABASE_RECONNECT_SLEEP); } } while (true); - $register->get('workerPools')->reclaim(); + $register->get('pools')->reclaim(); }); /** @@ -213,7 +213,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume } catch (\Throwable $th) { call_user_func($logError, $th, "updateWorkerDocument"); } finally { - $register->get('workerPools')->reclaim(); + $register->get('pools')->reclaim(); } }); }); @@ -274,7 +274,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, ])); } - $register->get('workerPools')->reclaim(); + $register->get('pools')->reclaim(); } /** * Sending test message for SDK E2E tests every 5 seconds. @@ -309,7 +309,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, } $start = time(); - $redis = $register->get('workerPools')->get('pubsub')->pop()->getResource(); /** @var Redis $redis */ + $redis = $register->get('pools')->get('pubsub')->pop()->getResource(); /** @var Redis $redis */ $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); if ($redis->ping(true)) { @@ -338,7 +338,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime->subscribe($projectId, $connection, $roles, $realtime->connections[$connection]['channels']); - $register->get('workerPools')->reclaim(); + $register->get('pools')->reclaim(); } } @@ -370,7 +370,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, sleep(DATABASE_RECONNECT_SLEEP); continue; } finally { - $register->get('workerPools')->reclaim(); + $register->get('pools')->reclaim(); } } @@ -384,7 +384,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::info("Connection open (user: {$connection})"); - App::setResource('pools', fn() => $register->get('workerPools')); + App::setResource('pools', fn() => $register->get('pools')); App::setResource('request', fn() => $request); App::setResource('response', fn() => $response); @@ -480,7 +480,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::error('[Error] Message: ' . $response['data']['message']); } } finally { - $register->get('workerPools')->reclaim(); + $register->get('pools')->reclaim(); } }); @@ -575,7 +575,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re $server->close($connection, $th->getCode()); } } finally { - $register->get('workerPools')->reclaim(); + $register->get('pools')->reclaim(); } }); From cf4be5109479f0cf95d17afda4ca38420273c30f Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 16 Nov 2022 10:43:26 +0000 Subject: [PATCH 6/6] feat: review comments --- .env | 2 +- CHANGES.md | 2 +- app/config/variables.php | 2 +- app/init.php | 2 +- app/views/install/compose.phtml | 6 +++--- docker-compose.yml | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.env b/.env index e5ee0947d2..11a2b4c7aa 100644 --- a/.env +++ b/.env @@ -23,7 +23,7 @@ _APP_DB_SCHEMA=appwrite _APP_DB_USER=user _APP_DB_PASS=password _APP_DB_ROOT_PASS=rootsecretpassword -_APP_CONNECTIONS_MAX_CONNECTIONS=251 +_APP_CONNECTIONS_MAX=251 _APP_CONNECTIONS_DB_PROJECT=db_fra1_02=mariadb://user:password@mariadb:3306/appwrite _APP_CONNECTIONS_DB_CONSOLE=db_fra1_01=mariadb://user:password@mariadb:3306/appwrite _APP_CONNECTIONS_CACHE=redis_fra1_01=redis://redis:6379 diff --git a/CHANGES.md b/CHANGES.md index cece10bf9c..d111ba226c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,7 @@ # TBD - Replace Appwrite executor with OpenRuntimes Executor [#4650](https://github.com/appwrite/appwrite/pull/4650) -- Add `_APP_CONNECTIONS_MAX_CONNECTIONS` env var [#4673](https://github.com/appwrite/appwrite/pull/4673) +- Add `_APP_CONNECTIONS_MAX` env var [#4673](https://github.com/appwrite/appwrite/pull/4673) - Update Traefik 2.7 -> 2.9 [#4673](https://github.com/appwrite/appwrite/pull/4673) - Increase Traefik TCP + file limits [#4673](https://github.com/appwrite/appwrite/pull/4673) diff --git a/app/config/variables.php b/app/config/variables.php index 2a77eee41d..5c80efa0ef 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -307,7 +307,7 @@ return [ 'filter' => 'password' ], [ - 'name' => '_APP_CONNECTIONS_MAX_CONNECTIONS', + 'name' => '_APP_CONNECTIONS_MAX', 'description' => 'MariaDB server maximum connections.', 'introduction' => 'TBD', 'default' => 251, diff --git a/app/init.php b/app/init.php index c921cb9c6c..ac30673de8 100644 --- a/app/init.php +++ b/app/init.php @@ -549,7 +549,7 @@ $register->set('pools', function () { $instances = 2; // REST, Realtime $workerCount = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)); - $maxConnections = App::getenv('_APP_CONNECTIONS_MAX_CONNECTIONS', 251); + $maxConnections = App::getenv('_APP_CONNECTIONS_MAX', 251); $instanceConnections = $maxConnections / $instances; if ($workerCount > $instanceConnections) { diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index d45061a0bd..331b23adb4 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -102,7 +102,7 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_CONNECTIONS_MAX_CONNECTIONS + - _APP_CONNECTIONS_MAX - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -197,7 +197,7 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_CONNECTIONS_MAX_CONNECTIONS + - _APP_CONNECTIONS_MAX - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -622,7 +622,7 @@ services: - MYSQL_DATABASE=${_APP_DB_SCHEMA} - MYSQL_USER=${_APP_DB_USER} - MYSQL_PASSWORD=${_APP_DB_PASS} - command: 'mysqld --innodb-flush-method=fsync --max_connections=${_APP_CONNECTIONS_MAX_CONNECTIONS}' + command: 'mysqld --innodb-flush-method=fsync --max_connections=${_APP_CONNECTIONS_MAX}' redis: image: redis:7.0.4-alpine diff --git a/docker-compose.yml b/docker-compose.yml index b597f8a0a2..a924e6d84a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -123,7 +123,7 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_CONNECTIONS_MAX_CONNECTIONS + - _APP_CONNECTIONS_MAX - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -228,7 +228,7 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_CONNECTIONS_MAX_CONNECTIONS + - _APP_CONNECTIONS_MAX - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -741,7 +741,7 @@ services: - MYSQL_DATABASE=${_APP_DB_SCHEMA} - MYSQL_USER=${_APP_DB_USER} - MYSQL_PASSWORD=${_APP_DB_PASS} - command: 'mysqld --innodb-flush-method=fsync --max_connections=${_APP_CONNECTIONS_MAX_CONNECTIONS}' + command: 'mysqld --innodb-flush-method=fsync --max_connections=${_APP_CONNECTIONS_MAX}' # smtp: # image: appwrite/smtp:1.2.0