From 08bc6379bd741b3af31f10341be9ebd43acd1a3c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 28 Mar 2025 14:43:34 +1300 Subject: [PATCH 01/39] Remove redundant DSN parse --- app/worker.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/worker.php b/app/worker.php index 90496c0430..3c6d027aa5 100644 --- a/app/worker.php +++ b/app/worker.php @@ -89,13 +89,6 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, $database = new Database($adapter, $cache); - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); if (\in_array($dsn->getHost(), $sharedTables)) { From 3a2ff9790cd7d22fcf7ccd56a6aa3fa669332664 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 28 Mar 2025 16:42:26 +1300 Subject: [PATCH 02/39] CLI scope pool use --- app/cli.php | 29 +++--------- src/Appwrite/Platform/Tasks/ScheduleBase.php | 4 -- .../Platform/Tasks/ScheduleExecutions.php | 38 ++++++++-------- .../Platform/Tasks/ScheduleFunctions.php | 45 +++++++++---------- .../Platform/Tasks/ScheduleMessages.php | 21 +++++---- 5 files changed, 58 insertions(+), 79 deletions(-) diff --git a/app/cli.php b/app/cli.php index f080217365..d11b8f3c6b 100644 --- a/app/cli.php +++ b/app/cli.php @@ -14,6 +14,7 @@ use Utopia\Cache\Cache; use Utopia\CLI\CLI; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as PoolAdapter; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; @@ -63,12 +64,8 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) { $attempts++; try { // Prepare database connection - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource(); - - $dbForPlatform = new Database($dbAdapter, $cache); + $adapter = new PoolAdapter($pools->get('console')); + $dbForPlatform = new Database($adapter, $cache); $dbForPlatform ->setNamespace('_console') @@ -86,7 +83,6 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) { $ready = true; } catch (\Throwable $err) { Console::warning($err->getMessage()); - $pools->get('console')->reclaim(); sleep($sleep); } } while ($attempts < $maxAttempts && !$ready); @@ -136,12 +132,8 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform return $database; } - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); + $adapter = new PoolAdapter($pools->get($dsn->getHost())); + $database = new Database($adapter, $cache); $databases[$dsn->getHost()] = $database; $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -173,15 +165,8 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) { return $database; } - $dbAdapter = $pools - ->get('logs') - ->pop() - ->getResource(); - - $database = new Database( - $dbAdapter, - $cache - ); + $adapter = new PoolAdapter($pools->get('logs')); + $database = new Database($adapter, $cache); $database ->setSharedTables(true) diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index a3c36cb96e..69ad75316b 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -132,8 +132,6 @@ abstract class ScheduleBase extends Action $latestDocument = \end($results); } - $pools->reclaim(); - Console::success("{$total} resources were loaded in " . (\microtime(true) - $loadStart) . " seconds"); Console::success("Starting timers at " . DateTime::now()); @@ -198,8 +196,6 @@ abstract class ScheduleBase extends Action $lastSyncUpdate = $time; $timerEnd = \microtime(true); - $pools->reclaim(); - Console::log("Sync tick: {$total} schedules were updated in " . ($timerEnd - $timerStart) . " seconds"); }); diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index 7cd76b480d..3539efec58 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -6,6 +6,7 @@ use Appwrite\Event\Func; use Swoole\Coroutine as Co; use Utopia\Database\Database; use Utopia\Pools\Group; +use Utopia\Queue\Publisher; class ScheduleExecutions extends ScheduleBase { @@ -29,9 +30,6 @@ class ScheduleExecutions extends ScheduleBase protected function enqueueResources(Group $pools, Database $dbForPlatform, callable $getProjectDB): void { - $queue = $pools->get('publisher')->pop(); - $connection = $queue->getResource(); - $queueForFunctions = new Func($connection); $intervalEnd = (new \DateTime())->modify('+' . self::ENQUEUE_TIMER . ' seconds'); foreach ($this->schedules as $schedule) { @@ -59,21 +57,25 @@ class ScheduleExecutions extends ScheduleBase $this->updateProjectAccess($schedule['project'], $dbForPlatform); - \go(function () use ($queueForFunctions, $schedule, $delay, $data) { + \go(function () use ($schedule, $delay, $data, $pools) { Co::sleep($delay); - $queueForFunctions->setType('schedule') - // Set functionId instead of function as we don't have $dbForProject - // TODO: Refactor to use function instead of functionId - ->setFunctionId($schedule['resource']['functionId']) - ->setExecution($schedule['resource']) - ->setMethod($data['method'] ?? 'POST') - ->setPath($data['path'] ?? '/') - ->setHeaders($data['headers'] ?? []) - ->setBody($data['body'] ?? '') - ->setProject($schedule['project']) - ->setUserId($data['userId'] ?? '') - ->trigger(); + $pools->get('publisher')->use(function(Publisher $publisher) use ($schedule, $data) { + $queueForFunctions = new Func($publisher); + + $queueForFunctions->setType('schedule') + // Set functionId instead of function as we don't have $dbForProject + // TODO: Refactor to use function instead of functionId + ->setFunctionId($schedule['resource']['functionId']) + ->setExecution($schedule['resource']) + ->setMethod($data['method'] ?? 'POST') + ->setPath($data['path'] ?? '/') + ->setHeaders($data['headers'] ?? []) + ->setBody($data['body'] ?? '') + ->setProject($schedule['project']) + ->setUserId($data['userId'] ?? '') + ->trigger(); + }); }); $dbForPlatform->deleteDocument( @@ -82,8 +84,6 @@ class ScheduleExecutions extends ScheduleBase ); unset($this->schedules[$schedule['$internalId']]); - } - - $queue->reclaim(); +} } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 5b8e3027a7..f506cd3fd9 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -8,6 +8,7 @@ use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Pools\Group; +use Utopia\Queue\Publisher; class ScheduleFunctions extends ScheduleBase { @@ -73,31 +74,29 @@ class ScheduleFunctions extends ScheduleBase \go(function () use ($delay, $scheduleKeys, $pools, $dbForPlatform) { \sleep($delay); // in seconds - $queue = $pools->get('publisher')->pop(); - $connection = $queue->getResource(); - foreach ($scheduleKeys as $scheduleKey) { - // Ensure schedule was not deleted - if (!\array_key_exists($scheduleKey, $this->schedules)) { - return; + foreach ($scheduleKeys as $scheduleKey) { + // Ensure schedule was not deleted + if (!\array_key_exists($scheduleKey, $this->schedules)) { + return; + } + + $schedule = $this->schedules[$scheduleKey]; + + $this->updateProjectAccess($schedule['project'], $dbForPlatform); + + $pools->get('publisher')->use(function(Publisher $publisher) use ($schedule) { + $queueForFunctions = new Func($publisher); + + $queueForFunctions + ->setType('schedule') + ->setFunction($schedule['resource']) + ->setMethod('POST') + ->setPath('/') + ->setProject($schedule['project']) + ->trigger(); + }); } - - $schedule = $this->schedules[$scheduleKey]; - - $this->updateProjectAccess($schedule['project'], $dbForPlatform); - - $queueForFunctions = new Func($connection); - - $queueForFunctions - ->setType('schedule') - ->setFunction($schedule['resource']) - ->setMethod('POST') - ->setPath('/') - ->setProject($schedule['project']) - ->trigger(); - } - - $queue->reclaim(); }); } diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 201d5eab53..319e194b22 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -5,6 +5,7 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Messaging; use Utopia\Database\Database; use Utopia\Pools\Group; +use Utopia\Queue\Publisher; class ScheduleMessages extends ScheduleBase { @@ -41,25 +42,23 @@ class ScheduleMessages extends ScheduleBase } \go(function () use ($schedule, $pools, $dbForPlatform) { - $queue = $pools->get('publisher')->pop(); - $connection = $queue->getResource(); - $queueForMessaging = new Messaging($connection); + $pools->get('publisher')->use(function(Publisher $publisher) use ($schedule, $dbForPlatform) { + $queueForMessaging = new Messaging($publisher); - $this->updateProjectAccess($schedule['project'], $dbForPlatform); + $this->updateProjectAccess($schedule['project'], $dbForPlatform); - $queueForMessaging - ->setType(MESSAGE_SEND_TYPE_EXTERNAL) - ->setMessageId($schedule['resourceId']) - ->setProject($schedule['project']) - ->trigger(); + $queueForMessaging + ->setType(MESSAGE_SEND_TYPE_EXTERNAL) + ->setMessageId($schedule['resourceId']) + ->setProject($schedule['project']) + ->trigger(); + }); $dbForPlatform->deleteDocument( 'schedules', $schedule['$id'], ); - $queue->reclaim(); - unset($this->schedules[$schedule['$internalId']]); }); } From 5b843015b04d3084b3b7988b618a75315068e8fc Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 28 Mar 2025 16:43:17 +1300 Subject: [PATCH 03/39] Realtime scope pool use --- app/realtime.php | 132 +++++++++----------- src/Appwrite/Messaging/Adapter/Realtime.php | 32 ++--- 2 files changed, 69 insertions(+), 95 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index 86f9c85fdd..c3d9274836 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -5,6 +5,7 @@ use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Network\Validator\Origin; +use Appwrite\PubSub\Adapter as PubSubAdapter; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Swoole\Http\Request as SwooleRequest; @@ -19,6 +20,7 @@ use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as PoolAdapter; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -28,13 +30,15 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\DSN\DSN; use Utopia\Logger\Log; +use Utopia\Pools\Group; +use Utopia\Registry\Registry; use Utopia\System\System; use Utopia\Telemetry\Adapter\None as NoTelemetry; use Utopia\WebSocket\Adapter; use Utopia\WebSocket\Server; /** - * @var \Utopia\Registry\Registry $register + * @var Registry $register */ require_once __DIR__ . '/init.php'; @@ -46,17 +50,11 @@ if (!function_exists('getConsoleDB')) { { global $register; - /** @var \Utopia\Pools\Group $pools */ + /** @var Group $pools */ $pools = $register->get('pools'); - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource() - ; - - $database = new Database($dbAdapter, getCache()); - + $adapter = new PoolAdapter($pools->get('console')); + $database = new Database($adapter, getCache()); $database ->setNamespace('_console') ->setMetadata('host', \gethostname()) @@ -72,7 +70,7 @@ if (!function_exists('getProjectDB')) { { global $register; - /** @var \Utopia\Pools\Group $pools */ + /** @var Group $pools */ $pools = $register->get('pools'); if ($project->isEmpty() || $project->getId() === 'console') { @@ -86,11 +84,7 @@ if (!function_exists('getProjectDB')) { $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $adapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - + $adapter = new PoolAdapter($pools->get($dsn->getHost())); $database = new Database($adapter, getCache()); $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -121,7 +115,7 @@ if (!function_exists('getCache')) { { global $register; - $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ + $pools = $register->get('pools'); /** @var Group $pools */ $list = Config::getParam('pools-cache', []); $adapters = []; @@ -130,8 +124,7 @@ if (!function_exists('getCache')) { $adapters[] = $pools ->get($value) ->pop() - ->getResource() - ; + ->getResource(); } return new Cache(new Sharding($adapters)); @@ -273,7 +266,6 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume sleep(DATABASE_RECONNECT_SLEEP); } } while (true); - $register->get('pools')->reclaim(); }); /** @@ -299,9 +291,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume Authorization::skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); } catch (Throwable $th) { - call_user_func($logError, $th, "updateWorkerDocument"); - } finally { - $register->get('pools')->reclaim(); + $logError($th, "updateWorkerDocument"); } }); } @@ -370,8 +360,6 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, 'data' => $event['data'] ])); } - - $register->get('pools')->reclaim(); } } /** @@ -407,70 +395,66 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, } $start = time(); - /** @var \Appwrite\PubSub\Adapter $pubsub */ - $pubsub = $register->get('pools')->get('pubsub')->pop()->getResource(); - if ($pubsub->ping(true)) { - $attempts = 0; - Console::success('Pub/sub connection established (worker: ' . $workerId . ')'); - } else { - Console::error('Pub/sub failed (worker: ' . $workerId . ')'); - } + $register->get('pools')->get('pubsub')->use(function (PubSubAdapter $pubsub) use ($server, $workerId, $stats, $register, $realtime) { + if ($pubsub->ping(true)) { + $attempts = 0; + Console::success('Pub/sub connection established (worker: ' . $workerId . ')'); + } else { + Console::error('Pub/sub failed (worker: ' . $workerId . ')'); + } - $pubsub->subscribe(['realtime'], function (mixed $redis, string $channel, string $payload) use ($server, $workerId, $stats, $register, $realtime) { - $event = json_decode($payload, true); + $pubsub->subscribe(['realtime'], function (mixed $redis, string $channel, string $payload) use ($server, $workerId, $stats, $register, $realtime) { + $event = json_decode($payload, true); - if ($event['permissionsChanged'] && isset($event['userId'])) { - $projectId = $event['project']; - $userId = $event['userId']; + if ($event['permissionsChanged'] && isset($event['userId'])) { + $projectId = $event['project']; + $userId = $event['userId']; - if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) { - $connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId])); - $consoleDatabase = getConsoleDB(); - $project = Authorization::skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); - $database = getProjectDB($project); + if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) { + $connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId])); + $consoleDatabase = getConsoleDB(); + $project = Authorization::skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); + $database = getProjectDB($project); - $user = $database->getDocument('users', $userId); + $user = $database->getDocument('users', $userId); - $roles = Auth::getRoles($user); - $channels = $realtime->connections[$connection]['channels']; + $roles = Auth::getRoles($user); + $channels = $realtime->connections[$connection]['channels']; - $realtime->unsubscribe($connection); - $realtime->subscribe($projectId, $connection, $roles, $channels); - - $register->get('pools')->reclaim(); + $realtime->unsubscribe($connection); + $realtime->subscribe($projectId, $connection, $roles, $channels); + } } - } - $receivers = $realtime->getSubscribers($event); + $receivers = $realtime->getSubscribers($event); - if (App::isDevelopment() && !empty($receivers)) { - Console::log("[Debug][Worker {$workerId}] Receivers: " . count($receivers)); - Console::log("[Debug][Worker {$workerId}] Receivers Connection IDs: " . json_encode($receivers)); - Console::log("[Debug][Worker {$workerId}] Event: " . $payload); - } + if (App::isDevelopment() && !empty($receivers)) { + Console::log("[Debug][Worker {$workerId}] Receivers: " . count($receivers)); + Console::log("[Debug][Worker {$workerId}] Receivers Connection IDs: " . json_encode($receivers)); + Console::log("[Debug][Worker {$workerId}] Event: " . $payload); + } - $server->send( - $receivers, - json_encode([ - 'type' => 'event', - 'data' => $event['data'] - ]) - ); + $server->send( + $receivers, + json_encode([ + 'type' => 'event', + 'data' => $event['data'] + ]) + ); - if (($num = count($receivers)) > 0) { - $register->get('telemetry.messageSentCounter')->add($num); - $stats->incr($event['project'], 'messages', $num); - } + if (($num = count($receivers)) > 0) { + $register->get('telemetry.messageSentCounter')->add($num); + $stats->incr($event['project'], 'messages', $num); + } + }); }); } catch (Throwable $th) { - call_user_func($logError, $th, "pubSubConnection"); + $logError($th, "pubSubConnection"); Console::error('Pub/sub error: ' . $th->getMessage()); $attempts++; sleep(DATABASE_RECONNECT_SLEEP); continue; - } finally { - $register->get('pools')->reclaim(); } } @@ -572,7 +556,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $stats->incr($project->getId(), 'connections'); $stats->incr($project->getId(), 'connectionsTotal'); } catch (Throwable $th) { - call_user_func($logError, $th, "initServer"); + $logError($th, "initServer"); // Handle SQL error code is 'HY000' $code = $th->getCode(); @@ -596,8 +580,6 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::error('[Error] Code: ' . $response['data']['code']); Console::error('[Error] Message: ' . $response['data']['message']); } - } finally { - $register->get('pools')->reclaim(); } }); @@ -696,8 +678,6 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re if ($th->getCode() === 1008) { $server->close($connection, $th->getCode()); } - } finally { - $register->get('pools')->reclaim(); } }); diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index dceafacf6e..87cad2f681 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -2,13 +2,14 @@ namespace Appwrite\Messaging\Adapter; -use Appwrite\Messaging\Adapter; +use Appwrite\PubSub\Adapter as PubSubAdapter; +use Appwrite\Messaging\Adapter as MessagingAdapter; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; -class Realtime extends Adapter +class Realtime extends MessagingAdapter { /** * Connection Tree @@ -123,11 +124,12 @@ class Realtime extends Adapter * Sends an event to the Realtime Server * @param string $projectId * @param array $payload - * @param string $event + * @param array $events * @param array $channels * @param array $roles * @param array $options * @return void + * @throws \Exception */ public static function send(string $projectId, array $payload, array $events, array $channels, array $roles, array $options = []): void { @@ -139,10 +141,8 @@ class Realtime extends Adapter $userId = array_key_exists('userId', $options) ? $options['userId'] : null; global $register; - $pubsub = $register->get('pools')->get('pubsub')->pop(); - try { - /** @var \Appwrite\PubSub\Adapter $redis */ - $redis = $pubsub->getResource(); + + $register->get('pools')->get('pubsub')->use(fn (PubSubAdapter $redis) => $redis->publish('realtime', json_encode([ 'project' => $projectId, 'roles' => $roles, @@ -154,10 +154,8 @@ class Realtime extends Adapter 'timestamp' => DateTime::formatTz(DateTime::now()), 'payload' => $payload ] - ])); - } finally { - $pubsub->reclaim(); - } + ])) + ); } /** @@ -172,8 +170,9 @@ class Realtime extends Adapter * - 1,121.328 ms (±0.84%) | 1,000,000 Connections / 10,000,000 Subscriptions * * @param array $event + * @return int[]|string[] */ - public function getSubscribers(array $event) + public function getSubscribers(array $event): array { $receivers = []; @@ -227,7 +226,7 @@ class Realtime extends Adapter foreach ($channels as $key => $value) { switch (true) { - case strpos($key, 'account.') === 0: + case \str_starts_with($key, 'account.'): unset($channels[$key]); break; @@ -269,6 +268,7 @@ class Realtime extends Adapter $channels[] = 'account.' . $parts[1]; $roles = [Role::user(ID::custom($parts[1]))->toString()]; break; + case 'migrations': case 'rules': $channels[] = 'console'; $channels[] = 'projects.' . $project->getId(); @@ -349,12 +349,6 @@ class Realtime extends Adapter $roles = [Role::team($project->getAttribute('teamId'))->toString()]; } - break; - case 'migrations': - $channels[] = 'console'; - $channels[] = 'projects.' . $project->getId(); - $projectId = 'console'; - $roles = [Role::team($project->getAttribute('teamId'))->toString()]; break; } From ec2f3b69cc2b5554371cdfa03c5d083629b3205b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 28 Mar 2025 16:50:13 +1300 Subject: [PATCH 04/39] API scope pool use --- app/controllers/api/health.php | 195 +++++++++++++++---------------- app/controllers/api/projects.php | 3 +- app/http.php | 14 +-- composer.json | 6 +- composer.lock | 80 +++++++------ 5 files changed, 148 insertions(+), 150 deletions(-) diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index e5336067c8..c9b8d84a04 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -3,6 +3,7 @@ use Appwrite\ClamAV\Network; use Appwrite\Event\Event; use Appwrite\Extend\Exception; +use Appwrite\PubSub\Adapter as PubSubAdapter; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -10,6 +11,8 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\App; use Utopia\Config\Config; +use Utopia\Cache\Adapter as CacheAdapter; +use Utopia\Database\Adapter as DatabaseAdapter; use Utopia\Database\Document; use Utopia\Domains\Validator\PublicDomain; use Utopia\Pools\Group; @@ -33,8 +36,8 @@ App::get('/v1/health') ->label('sdk', new Method( namespace: 'health', name: 'get', - auth: [AuthType::KEY], description: '/docs/references/health/get.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -69,10 +72,10 @@ App::get('/v1/health/db') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getDB', description: '/docs/references/health/get-db.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -84,8 +87,8 @@ App::get('/v1/health/db') ->inject('response') ->inject('pools') ->action(function (Response $response, Group $pools) { - $output = []; + $failures = []; $configs = [ 'Console.DB' => Config::getParam('pools-console'), @@ -95,27 +98,27 @@ App::get('/v1/health/db') foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $adapter = $pools->get($database)->pop()->getResource(); + $pools->get($database)->use(function (DatabaseAdapter $adapter) use ($key, $database, &$output, &$failures) { + $checkStart = \microtime(true); - $checkStart = \microtime(true); - - if ($adapter->ping()) { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } else { - $failure[] = $database; - } - } catch (\Throwable $th) { - $failure[] = $database; + if ($adapter->ping()) { + $output[] = new Document([ + 'name' => $key . " ($database)", + 'status' => 'pass', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); + } else { + $failures[] = $database; + } + }); + } catch (\Throwable) { + $failures[] = $database; } } } - if (!empty($failure)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'DB failure on: ' . implode(", ", $failure)); + if (!empty($failures)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'DB failure on: ' . implode(", ", $failures)); } $response->dynamic(new Document([ @@ -129,10 +132,10 @@ App::get('/v1/health/cache') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getCache', description: '/docs/references/health/get-cache.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -144,44 +147,39 @@ App::get('/v1/health/cache') ->inject('response') ->inject('pools') ->action(function (Response $response, Group $pools) { - $output = []; + $failures = []; $configs = [ 'Cache' => Config::getParam('pools-cache'), ]; foreach ($configs as $key => $config) { - foreach ($config as $database) { + foreach ($config as $cache) { try { - /** @var \Utopia\Cache\Adapter $adapter */ - $adapter = $pools->get($database)->pop()->getResource(); + $pools->get($cache)->use(function (CacheAdapter $adapter) use ($key, $cache, &$output, &$failures) { + $checkStart = \microtime(true); - $checkStart = \microtime(true); - - if ($adapter->ping()) { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } else { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'fail', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } - } catch (\Throwable $th) { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'fail', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); + if ($adapter->ping()) { + $output[] = new Document([ + 'name' => $key . " ($cache)", + 'status' => 'pass', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); + } else { + $failures[] = $cache; + } + }); + } catch (\Throwable) { + $failures[] = $cache; } } } + if (!empty($failures)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Cache failure on: ' . implode(", ", $failures)); + } + $response->dynamic(new Document([ 'statuses' => $output, 'total' => count($output), @@ -193,10 +191,10 @@ App::get('/v1/health/pubsub') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getPubSub', description: '/docs/references/health/get-pubsub.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -208,44 +206,39 @@ App::get('/v1/health/pubsub') ->inject('response') ->inject('pools') ->action(function (Response $response, Group $pools) { - $output = []; + $failures = []; $configs = [ 'PubSub' => Config::getParam('pools-pubsub'), ]; foreach ($configs as $key => $config) { - foreach ($config as $database) { + foreach ($config as $pubsub) { try { - /** @var \Appwrite\PubSub\Adapter $adapter */ - $adapter = $pools->get($database)->pop()->getResource(); + $pools->get($pubsub)->use(function (PubSubAdapter $adapter) use ($key, $pubsub, &$output, &$failures) { + $checkStart = \microtime(true); - $checkStart = \microtime(true); - - if ($adapter->ping()) { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } else { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'fail', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } - } catch (\Throwable $th) { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'fail', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); + if ($adapter->ping()) { + $output[] = new Document([ + 'name' => $key . " ($pubsub)", + 'status' => 'pass', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); + } else { + $failures[] = $pubsub; + } + }); + } catch (\Throwable) { + $failures[] = $pubsub; } } } + if (!empty($failures)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Pubsub failure on: ' . implode(", ", $failures)); + } + $response->dynamic(new Document([ 'statuses' => $output, 'total' => count($output), @@ -257,10 +250,10 @@ App::get('/v1/health/time') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getTime', description: '/docs/references/health/get-time.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -320,10 +313,10 @@ App::get('/v1/health/queue/webhooks') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueWebhooks', description: '/docs/references/health/get-queue-webhooks.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -345,17 +338,17 @@ App::get('/v1/health/queue/webhooks') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/logs') ->desc('Get logs queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueLogs', description: '/docs/references/health/get-queue-logs.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -377,17 +370,17 @@ App::get('/v1/health/queue/logs') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/certificate') ->desc('Get the SSL certificate for a domain') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getCertificate', description: '/docs/references/health/get-certificate.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -433,17 +426,17 @@ App::get('/v1/health/certificate') 'validTo' => $certificatePayload['validTo_time_t'], 'signatureTypeSN' => $certificatePayload['signatureTypeSN'], ]), Response::MODEL_HEALTH_CERTIFICATE); - }, ['response']); + }); App::get('/v1/health/queue/certificates') ->desc('Get certificates queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueCertificates', description: '/docs/references/health/get-queue-certificates.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -465,17 +458,17 @@ App::get('/v1/health/queue/certificates') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/builds') ->desc('Get builds queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueBuilds', description: '/docs/references/health/get-queue-builds.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -497,17 +490,17 @@ App::get('/v1/health/queue/builds') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/databases') ->desc('Get databases queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueDatabases', description: '/docs/references/health/get-queue-databases.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -530,17 +523,17 @@ App::get('/v1/health/queue/databases') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/deletes') ->desc('Get deletes queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueDeletes', description: '/docs/references/health/get-queue-deletes.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -562,17 +555,17 @@ App::get('/v1/health/queue/deletes') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/mails') ->desc('Get mails queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueMails', description: '/docs/references/health/get-queue-mails.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -594,17 +587,17 @@ App::get('/v1/health/queue/mails') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/messaging') ->desc('Get messaging queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueMessaging', description: '/docs/references/health/get-queue-messaging.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -626,17 +619,17 @@ App::get('/v1/health/queue/messaging') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/migrations') ->desc('Get migrations queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueMigrations', description: '/docs/references/health/get-queue-migrations.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -658,17 +651,17 @@ App::get('/v1/health/queue/migrations') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/functions') ->desc('Get functions queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueFunctions', description: '/docs/references/health/get-queue-functions.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -690,17 +683,17 @@ App::get('/v1/health/queue/functions') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/stats-resources') ->desc('Get stats resources queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueStatsResources', description: '/docs/references/health/get-queue-stats-resources.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -729,10 +722,10 @@ App::get('/v1/health/queue/stats-usage') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueUsage', description: '/docs/references/health/get-queue-stats-usage.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -761,10 +754,10 @@ App::get('/v1/health/queue/stats-usage-dump') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getQueueStatsUsageDump', description: '/docs/references/health/get-queue-stats-usage-dump.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -793,10 +786,10 @@ App::get('/v1/health/storage/local') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getStorageLocal', description: '/docs/references/health/get-storage-local.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -842,10 +835,10 @@ App::get('/v1/health/storage') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getStorage', description: '/docs/references/health/get-storage.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -889,10 +882,10 @@ App::get('/v1/health/anti-virus') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getAntivirus', description: '/docs/references/health/get-storage-anti-virus.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -934,10 +927,10 @@ App::get('/v1/health/queue/failed/:name') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', name: 'getFailedJobs', description: '/docs/references/health/get-failed-queue-jobs.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 48d20cd17f..4577fede8f 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -24,6 +24,7 @@ use Utopia\App; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as PoolAdapter; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -205,7 +206,7 @@ App::post('/v1/projects') $dsn = new DSN('mysql://' . $dsn); } - $adapter = $pools->get($dsn->getHost())->pop()->getResource(); + $adapter = new PoolAdapter($pools->get($dsn->getHost())); $dbForProject = new Database($adapter, $cache); $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); $sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', '')); diff --git a/app/http.php b/app/http.php index 451a25a601..183fe361bb 100644 --- a/app/http.php +++ b/app/http.php @@ -14,6 +14,8 @@ use Utopia\App; use Utopia\Audit\Audit; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as PoolAdapter; +use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -170,7 +172,6 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co break; // exit loop on success } catch (\Exception $e) { Console::warning(" └── Database not ready. Retrying connection ({$attempts})..."); - $pools->reclaim(); if ($attempts >= $max) { throw new \Exception(' └── Failed to connect to database: ' . $e->getMessage()); } @@ -312,11 +313,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $cache = $app->getResource('cache'); foreach ($sharedTablesV2 as $hostname) { - $adapter = $pools - ->get($hostname) - ->pop() - ->getResource(); - + $adapter = new PoolAdapter($pools->get($hostname)); $dbForProject = (new Database($adapter, $cache)) ->setDatabase('appwrite') ->setSharedTables(true) @@ -326,7 +323,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg try { Console::success('[Setup] - Creating project database: ' . $hostname . '...'); $dbForProject->create(); - } catch (Duplicate) { + } catch (DuplicateException) { Console::success('[Setup] - Skip: metadata table already exists'); } @@ -352,7 +349,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg } } - $pools->reclaim(); Console::success('[Setup] - Server database init completed...'); }); @@ -484,8 +480,6 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool ]; $swooleResponse->end(\json_encode($output)); - } finally { - $pools->reclaim(); } }); diff --git a/composer.json b/composer.json index b8c5afa109..d106ab7a30 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "utopia-php/cache": "0.12.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.63.*", + "utopia-php/database": "0.64.*", "utopia-php/domains": "0.5.*", "utopia-php/dsn": "0.2.1", "utopia-php/framework": "0.33.*", @@ -63,9 +63,9 @@ "utopia-php/migration": "0.8.*", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "0.7.*", - "utopia-php/pools": "0.7.*", + "utopia-php/pools": "0.8.*", "utopia-php/preloader": "0.2.*", - "utopia-php/queue": "0.9.*", + "utopia-php/queue": "dev-feat-expose-consumer as 0.9.0", "utopia-php/registry": "0.5.*", "utopia-php/storage": "0.18.*", "utopia-php/swoole": "0.8.*", diff --git a/composer.lock b/composer.lock index eb29c765a0..ff80c4c16f 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": "5b3c46863e4571c838c30090ad96436c", + "content-hash": "05e7736b10b0785ca7f9246f732461fe", "packages": [ { "name": "adhocore/jwt", @@ -3497,16 +3497,16 @@ }, { "name": "utopia-php/database", - "version": "0.63.1", + "version": "0.64.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "ad191bf34151815f716f553796a363ff2b6ef7d3" + "reference": "3ec66840dc0bcd24ed1c8e0f6d90fd3a61316ff4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/ad191bf34151815f716f553796a363ff2b6ef7d3", - "reference": "ad191bf34151815f716f553796a363ff2b6ef7d3", + "url": "https://api.github.com/repos/utopia-php/database/zipball/3ec66840dc0bcd24ed1c8e0f6d90fd3a61316ff4", + "reference": "3ec66840dc0bcd24ed1c8e0f6d90fd3a61316ff4", "shasum": "" }, "require": { @@ -3514,7 +3514,8 @@ "ext-pdo": "*", "php": ">=8.1", "utopia-php/cache": "0.12.*", - "utopia-php/framework": "0.33.*" + "utopia-php/framework": "0.33.*", + "utopia-php/pools": "0.8.*" }, "require-dev": { "fakerphp/faker": "1.23.*", @@ -3546,9 +3547,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.63.1" + "source": "https://github.com/utopia-php/database/tree/0.64.0" }, - "time": "2025-03-27T04:58:07+00:00" + "time": "2025-03-28T01:35:56+00:00" }, { "name": "utopia-php/domains", @@ -3950,16 +3951,16 @@ }, { "name": "utopia-php/migration", - "version": "0.8.3", + "version": "0.8.4", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "85c2e14647b240b75be6b6b762e5b30e48fb8d8a" + "reference": "845fd04ccf5e0edb03c184b864e0596080a432b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/85c2e14647b240b75be6b6b762e5b30e48fb8d8a", - "reference": "85c2e14647b240b75be6b6b762e5b30e48fb8d8a", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/845fd04ccf5e0edb03c184b864e0596080a432b8", + "reference": "845fd04ccf5e0edb03c184b864e0596080a432b8", "shasum": "" }, "require": { @@ -3967,7 +3968,7 @@ "ext-curl": "*", "ext-openssl": "*", "php": ">=8.1", - "utopia-php/database": "0.63.*", + "utopia-php/database": "0.*.*", "utopia-php/dsn": "0.2.*", "utopia-php/framework": "0.33.*", "utopia-php/storage": "0.18.*" @@ -4000,9 +4001,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.8.3" + "source": "https://github.com/utopia-php/migration/tree/0.8.4" }, - "time": "2025-03-26T10:45:51+00:00" + "time": "2025-03-28T02:08:22+00:00" }, { "name": "utopia-php/orchestration", @@ -4106,16 +4107,16 @@ }, { "name": "utopia-php/pools", - "version": "0.7.0", + "version": "0.8.0", "source": { "type": "git", "url": "https://github.com/utopia-php/pools.git", - "reference": "ad64d45afda08ec8b29e2642a8d18075964d40bf" + "reference": "60733929dc328e7ea47e800579c8bbf0d49df5ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/ad64d45afda08ec8b29e2642a8d18075964d40bf", - "reference": "ad64d45afda08ec8b29e2642a8d18075964d40bf", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/60733929dc328e7ea47e800579c8bbf0d49df5ba", + "reference": "60733929dc328e7ea47e800579c8bbf0d49df5ba", "shasum": "" }, "require": { @@ -4152,9 +4153,9 @@ ], "support": { "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/0.7.0" + "source": "https://github.com/utopia-php/pools/tree/0.8.0" }, - "time": "2025-03-18T03:55:33+00:00" + "time": "2025-03-19T10:22:03+00:00" }, { "name": "utopia-php/preloader", @@ -4211,16 +4212,16 @@ }, { "name": "utopia-php/queue", - "version": "0.9.0", + "version": "dev-feat-expose-consumer", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "077075f1d57afa430f76c35ed3bf4616e0eee8e7" + "reference": "85315b9da9be1b119158d759185db59761f84401" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/077075f1d57afa430f76c35ed3bf4616e0eee8e7", - "reference": "077075f1d57afa430f76c35ed3bf4616e0eee8e7", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/85315b9da9be1b119158d759185db59761f84401", + "reference": "85315b9da9be1b119158d759185db59761f84401", "shasum": "" }, "require": { @@ -4270,9 +4271,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.9.0" + "source": "https://github.com/utopia-php/queue/tree/feat-expose-consumer" }, - "time": "2025-03-13T12:22:41+00:00" + "time": "2025-03-28T03:38:19+00:00" }, { "name": "utopia-php/registry", @@ -4775,16 +4776,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.40.10", + "version": "0.40.11", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "054ac96285caf4f77879087b2416a5ddb8263051" + "reference": "0ec5f4a60c15e33e208bc3444ba6148b1d0f0027" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/054ac96285caf4f77879087b2416a5ddb8263051", - "reference": "054ac96285caf4f77879087b2416a5ddb8263051", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/0ec5f4a60c15e33e208bc3444ba6148b1d0f0027", + "reference": "0ec5f4a60c15e33e208bc3444ba6148b1d0f0027", "shasum": "" }, "require": { @@ -4820,9 +4821,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.40.10" + "source": "https://github.com/appwrite/sdk-generator/tree/0.40.11" }, - "time": "2025-03-25T13:44:16+00:00" + "time": "2025-03-26T10:53:16+00:00" }, { "name": "doctrine/annotations", @@ -8132,9 +8133,18 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/queue", + "version": "dev-feat-expose-consumer", + "alias": "0.9.0", + "alias_normalized": "0.9.0.0" + } + ], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "utopia-php/queue": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From 906f774a90b0b0106c2a764a58c8f2565fbdd0f0 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 28 Mar 2025 16:50:34 +1300 Subject: [PATCH 05/39] Worker scope pool use --- app/init/resources.php | 38 ++++---------- app/worker.php | 51 +++++++------------ .../Platform/Workers/StatsUsageDump.php | 11 ++-- 3 files changed, 31 insertions(+), 69 deletions(-) diff --git a/app/init/resources.php b/app/init/resources.php index 4e53b24c06..3d3b6713f5 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -27,6 +27,7 @@ use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as PoolAdapter; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; @@ -325,12 +326,8 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); + $adapter = new PoolAdapter($pools->get($dsn->getHost())); + $database = new Database($adapter, $cache); $database ->setMetadata('host', \gethostname()) @@ -356,12 +353,8 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform }, ['pools', 'dbForPlatform', 'cache', 'project']); App::setResource('dbForPlatform', function (Group $pools, Cache $cache) { - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); + $adapter = new PoolAdapter($pools->get('console')); + $database = new Database($adapter, $cache); $database ->setNamespace('_console') @@ -374,7 +367,7 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache) { }, ['pools', 'cache']); App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools + $databases = []; return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) { if ($project->isEmpty() || $project->getId() === 'console') { @@ -416,12 +409,8 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform return $database; } - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); + $adapter = new PoolAdapter($pools->get($dsn->getHost())); + $database = new Database($adapter, $cache); $databases[$dsn->getHost()] = $database; $configure($database); @@ -437,15 +426,8 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) { return $database; } - $dbAdapter = $pools - ->get('logs') - ->pop() - ->getResource(); - - $database = new Database( - $dbAdapter, - $cache - ); + $adapter = new PoolAdapter($pools->get('logs')); + $database = new Database($adapter, $cache); $database ->setSharedTables(true) diff --git a/app/worker.php b/app/worker.php index 3c6d027aa5..17e416b761 100644 --- a/app/worker.php +++ b/app/worker.php @@ -24,6 +24,7 @@ use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as PoolAdapter; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -33,6 +34,7 @@ use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Platform\Service; use Utopia\Pools\Group; +use Utopia\Queue\Consumer; use Utopia\Queue\Message; use Utopia\Queue\Publisher; use Utopia\Queue\Server; @@ -46,15 +48,11 @@ Server::setResource('register', fn () => $register); Server::setResource('dbForPlatform', function (Cache $cache, Registry $register) { $pools = $register->get('pools'); - $database = $pools - ->get('console') - ->pop() - ->getResource(); + $adapter = new PoolAdapter($pools->get('console')); + $dbForPlatform = new Database($adapter, $cache); + $dbForPlatform->setNamespace('_console'); - $adapter = new Database($database, $cache); - $adapter->setNamespace('_console'); - - return $adapter; + return $dbForPlatform; }, ['cache', 'register']); Server::setResource('project', function (Message $message, Database $dbForPlatform) { @@ -82,11 +80,7 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $adapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - + $adapter = new PoolAdapter($pools->get($dsn->getHost())); $database = new Database($adapter, $cache); $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -143,12 +137,8 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf return $database; } - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); + $adapter = new PoolAdapter($pools->get($dsn->getHost())); + $database = new Database($adapter, $cache); $databases[$dsn->getHost()] = $database; @@ -179,16 +169,9 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database->setTenant($project->getInternalId()); return $database; } - - $dbAdapter = $pools - ->get('logs') - ->pop() - ->getResource(); - - $database = new Database( - $dbAdapter, - $cache - ); + + $adapter = new PoolAdapter($pools->get('logs')); + $database = new Database($adapter, $cache); $database ->setSharedTables(true) @@ -229,8 +212,7 @@ Server::setResource('cache', function (Registry $register) { $adapters[] = $pools ->get($value) ->pop() - ->getResource() - ; + ->getResource(); } return new Cache(new Sharding($adapters)); @@ -447,7 +429,7 @@ $worker ->shutdown() ->inject('pools') ->action(function (Group $pools) { - $pools->reclaim(); + $pools->get('consumer')->reclaim(); }); $worker @@ -457,8 +439,9 @@ $worker ->inject('log') ->inject('pools') ->inject('project') - ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($queueName) { - $pools->reclaim(); + ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($worker, $queueName) { + $pools->get('consumer')->reclaim(); + $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); if ($logger) { diff --git a/src/Appwrite/Platform/Workers/StatsUsageDump.php b/src/Appwrite/Platform/Workers/StatsUsageDump.php index 119a9e7288..526b1431fc 100644 --- a/src/Appwrite/Platform/Workers/StatsUsageDump.php +++ b/src/Appwrite/Platform/Workers/StatsUsageDump.php @@ -68,9 +68,9 @@ class StatsUsageDump extends Action ]; /** - * @var callable + * @var callable(Document): Database */ - protected mixed $getLogsDB; + protected $getLogsDB; protected array $periods = [ '1h' => 'Y-m-d H:00', @@ -127,7 +127,7 @@ class StatsUsageDump extends Action console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); try { - /** @var \Utopia\Database\Database $dbForProject */ + /** @var Database $dbForProject */ $dbForProject = $getProjectDB($project); foreach ($stats['keys'] ?? [] as $key => $value) { if ($value == 0) { @@ -344,8 +344,7 @@ class StatsUsageDump extends Action } } - /** @var \Utopia\Database\Database $dbForLogs*/ - $dbForLogs = call_user_func($this->getLogsDB, $project); + $dbForLogs = ($this->getLogsDB)($project); try { $dbForLogs->createOrUpdateDocumentsWithIncrease( @@ -357,7 +356,5 @@ class StatsUsageDump extends Action } catch (\Throwable $th) { Console::error($th->getMessage()); } - - $this->register->get('pools')->get('logs')->reclaim(); } } From a9152c0c2021ee6910a267a9e7313fac5e9ab757 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 28 Mar 2025 16:51:47 +1300 Subject: [PATCH 06/39] Doctor pool use --- src/Appwrite/Platform/Tasks/Doctor.php | 68 +++++++++++++------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index c43afea527..fba636028b 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -3,14 +3,17 @@ namespace Appwrite\Platform\Tasks; use Appwrite\ClamAV\Network; -use Appwrite\PubSub\Adapter; +use Appwrite\PubSub\Adapter as PubSubAdapter; +use PHPMailer\PHPMailer\PHPMailer; use Utopia\App; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Adapter as DatabaseAdapter; use Utopia\Domains\Domain; use Utopia\DSN\DSN; use Utopia\Logger\Logger; use Utopia\Platform\Action; +use Utopia\Pools\Group; use Utopia\Registry\Registry; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; @@ -76,9 +79,9 @@ class Doctor extends Action Console::log('🟢 Abuse protection is enabled'); } - $authWhitelistRoot = System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', null); - $authWhitelistEmails = System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null); - $authWhitelistIPs = System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null); + $authWhitelistRoot = System::getEnv('_APP_CONSOLE_WHITELIST_ROOT'); + $authWhitelistEmails = System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS'); + $authWhitelistIPs = System::getEnv('_APP_CONSOLE_WHITELIST_IPS'); if ( empty($authWhitelistRoot) @@ -114,19 +117,16 @@ class Doctor extends Action } else { Console::log('🟢 Logging adapter is enabled (' . $providerName . ')'); } - } catch (\Throwable $th) { + } catch (\Throwable) { Console::log('🔴 Logging adapter is misconfigured'); } \usleep(200 * 1000); // Sleep for 0.2 seconds - try { - Console::log("\n" . '[Connectivity]'); - } catch (\Throwable $th) { - //throw $th; - } + Console::log("\n" . '[Connectivity]'); - $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ + /** @var Group $pools */ + $pools = $register->get('pools'); $configs = [ 'Console.DB' => Config::getParam('pools-console'), @@ -136,20 +136,22 @@ class Doctor extends Action foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $adapter = $pools->get($database)->pop()->getResource(); - - if ($adapter->ping()) { - Console::success('🟢 ' . str_pad("{$key}({$database})", 50, '.') . 'connected'); - } else { - Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected'); - } - } catch (\Throwable $th) { + $pools->get($database)->use(function (DatabaseAdapter $adapter) use ($key, $database) { + if ($adapter->ping()) { + Console::success('🟢 ' . str_pad("{$key}({$database})", 50, '.') . 'connected'); + } else { + Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected'); + } + }); + } catch (\Throwable) { Console::error('🔴 ' . str_pad("{$key}.({$database})", 47, '.') . 'disconnected'); } } } - $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ + /** @var Group $pools */ + $pools = $register->get('pools'); + $configs = [ 'Cache' => Config::getParam('pools-cache'), 'Queue' => Config::getParam('pools-queue'), @@ -159,15 +161,14 @@ class Doctor extends Action foreach ($configs as $key => $config) { foreach ($config as $pool) { try { - /** @var Adapter $adapter */ - $adapter = $pools->get($pool)->pop()->getResource(); - - if ($adapter->ping()) { - Console::success('🟢 ' . str_pad("{$key}({$pool})", 50, '.') . 'connected'); - } else { - Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); - } - } catch (\Throwable $th) { + $pools->get($pool)->use(function (PubSubAdapter $adapter) use ($key, $pool) { + if ($adapter->ping()) { + Console::success('🟢 ' . str_pad("{$key}({$pool})", 50, '.') . 'connected'); + } else { + Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); + } + }); + } catch (\Throwable) { Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); } } @@ -185,13 +186,14 @@ class Doctor extends Action } else { Console::error('🔴 ' . str_pad("Antivirus", 47, '.') . 'disconnected'); } - } catch (\Throwable $th) { + } catch (\Throwable) { Console::error('🔴 ' . str_pad("Antivirus", 47, '.') . 'disconnected'); } } try { - $mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */ + /* @var PHPMailer $mail */ + $mail = $register->get('smtp'); $mail->addAddress('demo@example.com', 'Example.com'); $mail->Subject = 'Test SMTP Connection'; @@ -200,7 +202,7 @@ class Doctor extends Action $mail->send(); Console::success('🟢 ' . str_pad("SMTP", 50, '.') . 'connected'); - } catch (\Throwable $th) { + } catch (\Throwable) { Console::error('🔴 ' . str_pad("SMTP", 47, '.') . 'disconnected'); } @@ -274,7 +276,7 @@ class Doctor extends Action Console::error('Failed to check for a newer version' . "\n"); } } - } catch (\Throwable $th) { + } catch (\Throwable) { Console::error('Failed to check for a newer version' . "\n"); } } From 608bda0b4875b443e18f385364c30d7687b3291b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 2 Apr 2025 13:47:12 +1300 Subject: [PATCH 07/39] Update lock --- composer.json | 2 +- composer.lock | 39 +++++++++++++++------------------------ 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/composer.json b/composer.json index 22efc71754..3920351c06 100644 --- a/composer.json +++ b/composer.json @@ -65,7 +65,7 @@ "utopia-php/platform": "0.7.*", "utopia-php/pools": "0.8.*", "utopia-php/preloader": "0.2.*", - "utopia-php/queue": "dev-feat-expose-consumer as 0.9.0", + "utopia-php/queue": "0.9.*", "utopia-php/registry": "0.5.*", "utopia-php/storage": "0.18.*", "utopia-php/swoole": "0.8.*", diff --git a/composer.lock b/composer.lock index 0f1adefbf3..270fbca6a4 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": "05e7736b10b0785ca7f9246f732461fe", + "content-hash": "6a54c8bc4f9f14cd3883f55880864630", "packages": [ { "name": "adhocore/jwt", @@ -3497,16 +3497,16 @@ }, { "name": "utopia-php/database", - "version": "0.64.0", + "version": "0.64.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "3ec66840dc0bcd24ed1c8e0f6d90fd3a61316ff4" + "reference": "6530a8a6d3c1fe92d0f9a92f0f05eda698d92e0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/3ec66840dc0bcd24ed1c8e0f6d90fd3a61316ff4", - "reference": "3ec66840dc0bcd24ed1c8e0f6d90fd3a61316ff4", + "url": "https://api.github.com/repos/utopia-php/database/zipball/6530a8a6d3c1fe92d0f9a92f0f05eda698d92e0b", + "reference": "6530a8a6d3c1fe92d0f9a92f0f05eda698d92e0b", "shasum": "" }, "require": { @@ -3547,9 +3547,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.64.0" + "source": "https://github.com/utopia-php/database/tree/0.64.1" }, - "time": "2025-03-28T01:35:56+00:00" + "time": "2025-04-02T00:35:29+00:00" }, { "name": "utopia-php/domains", @@ -4212,16 +4212,16 @@ }, { "name": "utopia-php/queue", - "version": "dev-feat-expose-consumer", + "version": "0.9.1", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "85315b9da9be1b119158d759185db59761f84401" + "reference": "32b6f84c55aae761db5a5ae76cc91ca8dbc8bc32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/85315b9da9be1b119158d759185db59761f84401", - "reference": "85315b9da9be1b119158d759185db59761f84401", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/32b6f84c55aae761db5a5ae76cc91ca8dbc8bc32", + "reference": "32b6f84c55aae761db5a5ae76cc91ca8dbc8bc32", "shasum": "" }, "require": { @@ -4271,9 +4271,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/feat-expose-consumer" + "source": "https://github.com/utopia-php/queue/tree/0.9.1" }, - "time": "2025-03-28T03:38:19+00:00" + "time": "2025-03-28T19:49:36+00:00" }, { "name": "utopia-php/registry", @@ -8124,18 +8124,9 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [ - { - "package": "utopia-php/queue", - "version": "dev-feat-expose-consumer", - "alias": "0.9.0", - "alias_normalized": "0.9.0.0" - } - ], + "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "utopia-php/queue": 20 - }, + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { From b972e7a1d48380d137bb50601a41fe37cc807d49 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 15 Apr 2025 22:18:48 +1200 Subject: [PATCH 08/39] Stash --- app/init/registers.php | 4 +-- app/init/resources.php | 3 ++- src/Appwrite/Platform/Workers/Deletes.php | 5 +--- tests/resources/docker/docker-compose.yml | 32 +---------------------- 4 files changed, 6 insertions(+), 38 deletions(-) diff --git a/app/init/registers.php b/app/init/registers.php index 1ebcbc1691..14d177426c 100644 --- a/app/init/registers.php +++ b/app/init/registers.php @@ -215,13 +215,13 @@ $register->set('pools', function () { 'mysql', 'mariadb' => 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( + return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, [ PDO::ATTR_TIMEOUT => 3, // Seconds PDO::ATTR_PERSISTENT => false, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => true, PDO::ATTR_STRINGIFY_FETCHES => true - )); + ]); }); }, 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { diff --git a/app/init/resources.php b/app/init/resources.php index 3d3b6713f5..1e3981781c 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -420,7 +420,8 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform App::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database = null; - return function (?Document $project = null) use ($pools, $cache, $database) { + + return function (?Document $project = null) use ($pools, $cache, &$database) { if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') { $database->setTenant($project->getInternalId()); return $database; diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index a9b83976a4..304d52a179 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -58,10 +58,7 @@ class Deletes extends Action ->inject('executionRetention') ->inject('auditRetention') ->inject('log') - ->callback( - fn ($message, Document $project, Database $dbForPlatform, callable $getProjectDB, callable $getLogsDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, CertificatesAdapter $certificates, string $executionRetention, string $auditRetention, Log $log) => - $this->action($message, $project, $dbForPlatform, $getProjectDB, $getLogsDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $certificates, $executionRetention, $auditRetention, $log) - ); + ->callback([$this, 'action']); } /** diff --git a/tests/resources/docker/docker-compose.yml b/tests/resources/docker/docker-compose.yml index e549ac27a5..779d63d6ed 100644 --- a/tests/resources/docker/docker-compose.yml +++ b/tests/resources/docker/docker-compose.yml @@ -35,7 +35,7 @@ services: - VERSION=dev restart: unless-stopped ports: - - 9501:80 + - "9501:80" networks: - appwrite labels: @@ -52,15 +52,12 @@ services: - ./phpunit.xml:/usr/src/code/phpunit.xml - ./tests:/usr/src/code/tests - ./app:/usr/src/code/app - # - ./vendor:/usr/src/code/vendor - ./docs:/usr/src/code/docs - ./public:/usr/src/code/public - ./src:/usr/src/code/src - - ./debug:/tmp depends_on: - mariadb - redis - # - clamav environment: - _APP_COMPRESSION_MIN_SIZE_BYTES - _APP_ENV @@ -355,33 +352,6 @@ services: volumes: - appwrite-redis:/data:rw - # clamav: - # image: appwrite/clamav:1.2.0 - # container_name: appwrite-clamav - # restart: unless-stopped - # networks: - # - appwrite - # volumes: - # - appwrite-uploads:/storage/uploads - - - # redis-commander: - # image: rediscommander/redis-commander:latest - # restart: unless-stopped - # networks: - # - appwrite - # environment: - # - REDIS_HOSTS=redis - # ports: - # - "8081:8081" - - # webgrind: - # image: 'jokkedk/webgrind:latest' - # volumes: - # - './debug:/tmp' - # ports: - # - '3001:80' - networks: gateway: appwrite: From 4b8c68e5ed2e2b3e697671dae6c40c7f00dee2d0 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 17 Apr 2025 13:34:08 +1200 Subject: [PATCH 09/39] Update db --- composer.json | 2 +- composer.lock | 27 ++++++++++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index 5b13227d2b..d285e78eee 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "utopia-php/cache": "0.12.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.65.*", + "utopia-php/database": "dev-feat-pool-init as 0.65.0", "utopia-php/domains": "0.5.*", "utopia-php/dsn": "0.2.1", "utopia-php/framework": "0.33.*", diff --git a/composer.lock b/composer.lock index c8c1e3c192..3409d07c2a 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": "51ff891ef6cee8a3f8c4e5187b7fd479", + "content-hash": "979bd0bc7c65dc2e74eb2dc978741e5a", "packages": [ { "name": "adhocore/jwt", @@ -3497,16 +3497,16 @@ }, { "name": "utopia-php/database", - "version": "0.65.0", + "version": "dev-feat-pool-init", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "e589efdc5da1216523a758e8af358866d4fb563f" + "reference": "534af1baa78a46a0adc348672e3cc9ceeb4ac61e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/e589efdc5da1216523a758e8af358866d4fb563f", - "reference": "e589efdc5da1216523a758e8af358866d4fb563f", + "url": "https://api.github.com/repos/utopia-php/database/zipball/534af1baa78a46a0adc348672e3cc9ceeb4ac61e", + "reference": "534af1baa78a46a0adc348672e3cc9ceeb4ac61e", "shasum": "" }, "require": { @@ -3547,9 +3547,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.65.0" + "source": "https://github.com/utopia-php/database/tree/feat-pool-init" }, - "time": "2025-04-14T07:39:01+00:00" + "time": "2025-04-15T10:24:04+00:00" }, { "name": "utopia-php/domains", @@ -8124,9 +8124,18 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/database", + "version": "dev-feat-pool-init", + "alias": "0.65.0", + "alias_normalized": "0.65.0.0" + } + ], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "utopia-php/database": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From 03cae4c5eed263756f38a6873a328ffd3457ef7d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 17 Apr 2025 16:49:12 +1200 Subject: [PATCH 10/39] Pool use project create --- app/controllers/api/projects.php | 116 ++++++++++++++++--------------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 3b690059de..82b07759df 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -24,7 +24,8 @@ use Utopia\App; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as PoolAdapter; +use Utopia\Database\Adapter; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -222,73 +223,74 @@ App::post('/v1/projects') $sharedTables = $sharedTablesV1 || $sharedTablesV2; if (!$sharedTablesV2) { - $adapter = $pools->get($dsn->getHost())->pop()->getResource(); - $dbForProject = new Database($adapter, $cache); + $pools->get($dsn->getHost())->use(function (Adapter $adapter) use ($project, $dsn, $cache, $projectTables, $sharedTables, $sharedTablesV1) { + $dbForProject = new Database($adapter, $cache); - if ($sharedTables) { - $dbForProject - ->setSharedTables(true) - ->setTenant($sharedTablesV1 ? $project->getInternalId() : null) - ->setNamespace($dsn->getParam('namespace')); - } else { - $dbForProject - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } + if ($sharedTables) { + $dbForProject + ->setSharedTables(true) + ->setTenant($sharedTablesV1 ? $project->getInternalId() : null) + ->setNamespace($dsn->getParam('namespace')); + } else { + $dbForProject + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } - $create = true; + $create = true; - try { - $dbForProject->create(); - } catch (Duplicate) { - $create = false; - } + try { + $dbForProject->create(); + } catch (Duplicate) { + $create = false; + } - if ($create || $projectTables) { - $audit = new Audit($dbForProject); - $audit->setup(); - } + if ($create || $projectTables) { + $audit = new Audit($dbForProject); + $audit->setup(); + } - if (!$create && $sharedTablesV1) { - $attributes = \array_map(fn ($attribute) => new Document($attribute), Audit::ATTRIBUTES); - $indexes = \array_map(fn (array $index) => new Document($index), Audit::INDEXES); - $dbForProject->createDocument(Database::METADATA, new Document([ - '$id' => ID::custom('audit'), - '$permissions' => [Permission::create(Role::any())], - 'name' => 'audit', - 'attributes' => $attributes, - 'indexes' => $indexes, - 'documentSecurity' => true - ])); - } + if (!$create && $sharedTablesV1) { + $attributes = \array_map(fn ($attribute) => new Document($attribute), Audit::ATTRIBUTES); + $indexes = \array_map(fn (array $index) => new Document($index), Audit::INDEXES); + $dbForProject->createDocument(Database::METADATA, new Document([ + '$id' => ID::custom('audit'), + '$permissions' => [Permission::create(Role::any())], + 'name' => 'audit', + 'attributes' => $attributes, + 'indexes' => $indexes, + 'documentSecurity' => true + ])); + } - if ($create || $sharedTablesV1) { - /** @var array $collections */ - $collections = Config::getParam('collections', [])['projects'] ?? []; + if ($create || $sharedTablesV1) { + /** @var array $collections */ + $collections = Config::getParam('collections', [])['projects'] ?? []; - foreach ($collections as $key => $collection) { - if (($collection['$collection'] ?? '') !== Database::METADATA) { - continue; - } + foreach ($collections as $key => $collection) { + if (($collection['$collection'] ?? '') !== Database::METADATA) { + continue; + } - $attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']); - $indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']); + $attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']); + $indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']); - try { - $dbForProject->createCollection($key, $attributes, $indexes); - } catch (Duplicate) { - $dbForProject->createDocument(Database::METADATA, new Document([ - '$id' => ID::custom($key), - '$permissions' => [Permission::create(Role::any())], - 'name' => $key, - 'attributes' => $attributes, - 'indexes' => $indexes, - 'documentSecurity' => true - ])); + try { + $dbForProject->createCollection($key, $attributes, $indexes); + } catch (Duplicate) { + $dbForProject->createDocument(Database::METADATA, new Document([ + '$id' => ID::custom($key), + '$permissions' => [Permission::create(Role::any())], + 'name' => $key, + 'attributes' => $attributes, + 'indexes' => $indexes, + 'documentSecurity' => true + ])); + } } } - } + }); } // Hook allowing instant project mirroring during migration From 100870557b79adc5bb5e28ec333b74c10caa26d6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 17 Apr 2025 16:50:13 +1200 Subject: [PATCH 11/39] Use cache pool adapter --- app/cli.php | 14 +++++------ app/http.php | 10 +++++--- app/init/resources.php | 16 ++++++------ app/realtime.php | 12 ++++----- app/worker.php | 16 ++++++------ composer.json | 2 +- composer.lock | 55 +++++++++++++++++++++--------------------- 7 files changed, 60 insertions(+), 65 deletions(-) diff --git a/app/cli.php b/app/cli.php index 2ee9f0c92d..44fb72f7ca 100644 --- a/app/cli.php +++ b/app/cli.php @@ -11,11 +11,12 @@ use Appwrite\Platform\Appwrite; use Appwrite\Runtimes\Runtimes; use Executor\Executor; use Utopia\Cache\Adapter\Sharding; +use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Cache; use Utopia\CLI\CLI; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as PoolAdapter; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; @@ -42,10 +43,7 @@ CLI::setResource('cache', function ($pools) { $adapters = []; foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource(); + $adapters[] = new CachePool($pools->get($value)); } return new Cache(new Sharding($adapters)); @@ -65,7 +63,7 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) { $attempts++; try { // Prepare database connection - $adapter = new PoolAdapter($pools->get('console')); + $adapter = new DatabasePool($pools->get('console')); $dbForPlatform = new Database($adapter, $cache); $dbForPlatform @@ -133,7 +131,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform return $database; } - $adapter = new PoolAdapter($pools->get($dsn->getHost())); + $adapter = new DatabasePool($pools->get($dsn->getHost())); $database = new Database($adapter, $cache); $databases[$dsn->getHost()] = $database; $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -166,7 +164,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) { return $database; } - $adapter = new PoolAdapter($pools->get('logs')); + $adapter = new DatabasePool($pools->get('logs')); $database = new Database($adapter, $cache); $database diff --git a/app/http.php b/app/http.php index 183fe361bb..d3fa2089cc 100644 --- a/app/http.php +++ b/app/http.php @@ -14,7 +14,7 @@ use Utopia\App; use Utopia\Audit\Audit; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as PoolAdapter; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Database; use Utopia\Database\DateTime; @@ -163,7 +163,7 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co $sleep = 1; $attempts = 0; - do { + while (true) { try { $attempts++; $resource = $app->getResource($resourceKey); @@ -171,13 +171,15 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co $database = is_callable($resource) ? $resource() : $resource; break; // exit loop on success } catch (\Exception $e) { + \var_dump($e); + exit; Console::warning(" └── Database not ready. Retrying connection ({$attempts})..."); if ($attempts >= $max) { throw new \Exception(' └── Failed to connect to database: ' . $e->getMessage()); } sleep($sleep); } - } while ($attempts < $max); + } Console::success("[Setup] - $dbName database init started..."); @@ -313,7 +315,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $cache = $app->getResource('cache'); foreach ($sharedTablesV2 as $hostname) { - $adapter = new PoolAdapter($pools->get($hostname)); + $adapter = new DatabasePool($pools->get($hostname)); $dbForProject = (new Database($adapter, $cache)) ->setDatabase('appwrite') ->setSharedTables(true) diff --git a/app/init/resources.php b/app/init/resources.php index 27bc4acd75..4e7d219666 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -24,11 +24,12 @@ use Appwrite\Utopia\Request; use Executor\Executor; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; use Utopia\App; +use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as PoolAdapter; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; @@ -330,7 +331,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $adapter = new PoolAdapter($pools->get($dsn->getHost())); + $adapter = new DatabasePool($pools->get($dsn->getHost())); $database = new Database($adapter, $cache); $database @@ -357,7 +358,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform }, ['pools', 'dbForPlatform', 'cache', 'project']); App::setResource('dbForPlatform', function (Group $pools, Cache $cache) { - $adapter = new PoolAdapter($pools->get('console')); + $adapter = new DatabasePool($pools->get('console')); $database = new Database($adapter, $cache); $database @@ -413,7 +414,7 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform return $database; } - $adapter = new PoolAdapter($pools->get($dsn->getHost())); + $adapter = new DatabasePool($pools->get($dsn->getHost())); $database = new Database($adapter, $cache); $databases[$dsn->getHost()] = $database; $configure($database); @@ -431,7 +432,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) { return $database; } - $adapter = new PoolAdapter($pools->get('logs')); + $adapter = new DatabasePool($pools->get('logs')); $database = new Database($adapter, $cache); $database @@ -456,10 +457,7 @@ App::setResource('cache', function (Group $pools, Telemetry $telemetry) { $adapters = []; foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource(); + $adapters[] = new CachePool($pools->get($value)); } $cache = new Cache(new Sharding($adapters)); diff --git a/app/realtime.php b/app/realtime.php index c3d9274836..791a2ccc15 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -16,11 +16,12 @@ use Swoole\Timer; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; use Utopia\App; +use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as PoolAdapter; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -53,7 +54,7 @@ if (!function_exists('getConsoleDB')) { /** @var Group $pools */ $pools = $register->get('pools'); - $adapter = new PoolAdapter($pools->get('console')); + $adapter = new DatabasePool($pools->get('console')); $database = new Database($adapter, getCache()); $database ->setNamespace('_console') @@ -84,7 +85,7 @@ if (!function_exists('getProjectDB')) { $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $adapter = new PoolAdapter($pools->get($dsn->getHost())); + $adapter = new DatabasePool($pools->get($dsn->getHost())); $database = new Database($adapter, getCache()); $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -121,10 +122,7 @@ if (!function_exists('getCache')) { $adapters = []; foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource(); + $adapters[] = new CachePool($pools->get($value)); } return new Cache(new Sharding($adapters)); diff --git a/app/worker.php b/app/worker.php index 263754add4..7b70cba736 100644 --- a/app/worker.php +++ b/app/worker.php @@ -20,11 +20,12 @@ use Appwrite\Platform\Appwrite; use Executor\Executor; use Swoole\Runtime; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; +use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as PoolAdapter; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -48,7 +49,7 @@ Server::setResource('register', fn () => $register); Server::setResource('dbForPlatform', function (Cache $cache, Registry $register) { $pools = $register->get('pools'); - $adapter = new PoolAdapter($pools->get('console')); + $adapter = new DatabasePool($pools->get('console')); $dbForPlatform = new Database($adapter, $cache); $dbForPlatform->setNamespace('_console'); @@ -80,7 +81,7 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $adapter = new PoolAdapter($pools->get($dsn->getHost())); + $adapter = new DatabasePool($pools->get($dsn->getHost())); $database = new Database($adapter, $cache); $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -137,7 +138,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf return $database; } - $adapter = new PoolAdapter($pools->get($dsn->getHost())); + $adapter = new DatabasePool($pools->get($dsn->getHost())); $database = new Database($adapter, $cache); $databases[$dsn->getHost()] = $database; @@ -170,7 +171,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) { return $database; } - $adapter = new PoolAdapter($pools->get('logs')); + $adapter = new DatabasePool($pools->get('logs')); $database = new Database($adapter, $cache); $database @@ -209,10 +210,7 @@ Server::setResource('cache', function (Registry $register) { $adapters = []; foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource(); + $adapters[] = new CachePool($pools->get($value)); } return new Cache(new Sharding($adapters)); diff --git a/composer.json b/composer.json index d285e78eee..615d5abd62 100644 --- a/composer.json +++ b/composer.json @@ -48,7 +48,7 @@ "utopia-php/abuse": "0.52.*", "utopia-php/analytics": "0.10.*", "utopia-php/audit": "0.55.*", - "utopia-php/cache": "0.12.*", + "utopia-php/cache": "0.13.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", "utopia-php/database": "dev-feat-pool-init as 0.65.0", diff --git a/composer.lock b/composer.lock index 3409d07c2a..1d39c1b753 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": "979bd0bc7c65dc2e74eb2dc978741e5a", + "content-hash": "d56d7960edb985f497bcc816ac294652", "packages": [ { "name": "adhocore/jwt", @@ -3300,16 +3300,16 @@ }, { "name": "utopia-php/cache", - "version": "0.12.0", + "version": "0.13.0", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "646038f1d470b759c129348be8fc14da3c00bbd9" + "reference": "dee01dec33a211644d60f6cfa56b1b8176d3fae3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/646038f1d470b759c129348be8fc14da3c00bbd9", - "reference": "646038f1d470b759c129348be8fc14da3c00bbd9", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/dee01dec33a211644d60f6cfa56b1b8176d3fae3", + "reference": "dee01dec33a211644d60f6cfa56b1b8176d3fae3", "shasum": "" }, "require": { @@ -3317,6 +3317,7 @@ "ext-memcached": "*", "ext-redis": "*", "php": ">=8.0", + "utopia-php/pools": "0.8.*", "utopia-php/telemetry": "0.1.*" }, "require-dev": { @@ -3345,9 +3346,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.12.0" + "source": "https://github.com/utopia-php/cache/tree/0.13.0" }, - "time": "2025-02-25T09:09:21+00:00" + "time": "2025-04-17T04:20:26+00:00" }, { "name": "utopia-php/cli", @@ -3501,19 +3502,19 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "534af1baa78a46a0adc348672e3cc9ceeb4ac61e" + "reference": "3f03b9cb56f7d2f09bb9c17e5898d3222a285473" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/534af1baa78a46a0adc348672e3cc9ceeb4ac61e", - "reference": "534af1baa78a46a0adc348672e3cc9ceeb4ac61e", + "url": "https://api.github.com/repos/utopia-php/database/zipball/3f03b9cb56f7d2f09bb9c17e5898d3222a285473", + "reference": "3f03b9cb56f7d2f09bb9c17e5898d3222a285473", "shasum": "" }, "require": { "ext-mbstring": "*", "ext-pdo": "*", "php": ">=8.1", - "utopia-php/cache": "0.12.*", + "utopia-php/cache": "0.13.*", "utopia-php/framework": "0.33.*", "utopia-php/pools": "0.8.*" }, @@ -3549,7 +3550,7 @@ "issues": "https://github.com/utopia-php/database/issues", "source": "https://github.com/utopia-php/database/tree/feat-pool-init" }, - "time": "2025-04-15T10:24:04+00:00" + "time": "2025-04-17T04:27:21+00:00" }, { "name": "utopia-php/domains", @@ -4107,16 +4108,16 @@ }, { "name": "utopia-php/pools", - "version": "0.8.0", + "version": "0.8.2", "source": { "type": "git", "url": "https://github.com/utopia-php/pools.git", - "reference": "60733929dc328e7ea47e800579c8bbf0d49df5ba" + "reference": "05c67aba42eb68ac65489cc1e7fc5db83db2dd4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/60733929dc328e7ea47e800579c8bbf0d49df5ba", - "reference": "60733929dc328e7ea47e800579c8bbf0d49df5ba", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/05c67aba42eb68ac65489cc1e7fc5db83db2dd4d", + "reference": "05c67aba42eb68ac65489cc1e7fc5db83db2dd4d", "shasum": "" }, "require": { @@ -4153,9 +4154,9 @@ ], "support": { "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/0.8.0" + "source": "https://github.com/utopia-php/pools/tree/0.8.2" }, - "time": "2025-03-19T10:22:03+00:00" + "time": "2025-04-17T02:04:54+00:00" }, { "name": "utopia-php/preloader", @@ -4543,28 +4544,28 @@ }, { "name": "utopia-php/vcs", - "version": "0.9.4", + "version": "0.9.5", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a" + "reference": "055956545ca7ab4e8688df5de1df3e2833859793" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/1a8d280b176acc99ea8d9e7364b8767cbb206b4a", - "reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/055956545ca7ab4e8688df5de1df3e2833859793", + "reference": "055956545ca7ab4e8688df5de1df3e2833859793", "shasum": "" }, "require": { "adhocore/jwt": "^1.1", "php": ">=8.0", - "utopia-php/cache": "0.12.*", + "utopia-php/cache": "0.13.*", "utopia-php/framework": "0.*.*", "utopia-php/system": "0.9.*" }, "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", + "laravel/pint": "1.*.*", + "phpstan/phpstan": "1.*.*", "phpunit/phpunit": "^9.4" }, "type": "library", @@ -4587,9 +4588,9 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.9.4" + "source": "https://github.com/utopia-php/vcs/tree/0.9.5" }, - "time": "2025-03-13T10:09:45+00:00" + "time": "2025-04-17T04:38:49+00:00" }, { "name": "utopia-php/websocket", From 70364d7a07450ba3fad11d839deeee01ac207a47 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 17 Apr 2025 17:09:08 +1200 Subject: [PATCH 12/39] Format --- app/cli.php | 2 +- app/controllers/api/health.php | 2 +- app/controllers/api/projects.php | 1 - app/http.php | 2 +- app/worker.php | 3 +- src/Appwrite/Messaging/Adapter/Realtime.php | 5 ++- .../Platform/Tasks/ScheduleExecutions.php | 4 +- .../Platform/Tasks/ScheduleFunctions.php | 42 +++++++++---------- .../Platform/Tasks/ScheduleMessages.php | 2 +- 9 files changed, 31 insertions(+), 32 deletions(-) diff --git a/app/cli.php b/app/cli.php index 44fb72f7ca..1a370ff10a 100644 --- a/app/cli.php +++ b/app/cli.php @@ -10,8 +10,8 @@ use Appwrite\Event\StatsUsage; use Appwrite\Platform\Appwrite; use Appwrite\Runtimes\Runtimes; use Executor\Executor; -use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Adapter\Pool as CachePool; +use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\CLI; use Utopia\CLI\Console; diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 1ab123ec29..b038806013 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -10,8 +10,8 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\App; -use Utopia\Config\Config; use Utopia\Cache\Adapter as CacheAdapter; +use Utopia\Config\Config; use Utopia\Database\Adapter as DatabaseAdapter; use Utopia\Database\Document; use Utopia\Domains\Validator\PublicDomain; diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 82b07759df..75ee3edb5d 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -25,7 +25,6 @@ use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\Config\Config; use Utopia\Database\Adapter; -use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; diff --git a/app/http.php b/app/http.php index d3fa2089cc..8ee65dccf6 100644 --- a/app/http.php +++ b/app/http.php @@ -15,10 +15,10 @@ use Utopia\Audit\Audit; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Adapter\Pool as DatabasePool; -use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; +use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; diff --git a/app/worker.php b/app/worker.php index 7b70cba736..72ad5f3338 100644 --- a/app/worker.php +++ b/app/worker.php @@ -35,7 +35,6 @@ use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Platform\Service; use Utopia\Pools\Group; -use Utopia\Queue\Consumer; use Utopia\Queue\Message; use Utopia\Queue\Publisher; use Utopia\Queue\Server; @@ -170,7 +169,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database->setTenant($project->getInternalId()); return $database; } - + $adapter = new DatabasePool($pools->get('logs')); $database = new Database($adapter, $cache); diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 87cad2f681..1454f03f93 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -2,8 +2,8 @@ namespace Appwrite\Messaging\Adapter; -use Appwrite\PubSub\Adapter as PubSubAdapter; use Appwrite\Messaging\Adapter as MessagingAdapter; +use Appwrite\PubSub\Adapter as PubSubAdapter; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; @@ -142,7 +142,8 @@ class Realtime extends MessagingAdapter global $register; - $register->get('pools')->get('pubsub')->use(fn (PubSubAdapter $redis) => + $register->get('pools')->get('pubsub')->use( + fn (PubSubAdapter $redis) => $redis->publish('realtime', json_encode([ 'project' => $projectId, 'roles' => $roles, diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index 3539efec58..f1a4c3ed79 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -60,7 +60,7 @@ class ScheduleExecutions extends ScheduleBase \go(function () use ($schedule, $delay, $data, $pools) { Co::sleep($delay); - $pools->get('publisher')->use(function(Publisher $publisher) use ($schedule, $data) { + $pools->get('publisher')->use(function (Publisher $publisher) use ($schedule, $data) { $queueForFunctions = new Func($publisher); $queueForFunctions->setType('schedule') @@ -84,6 +84,6 @@ class ScheduleExecutions extends ScheduleBase ); unset($this->schedules[$schedule['$internalId']]); -} + } } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index f506cd3fd9..328c9f894c 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -75,28 +75,28 @@ class ScheduleFunctions extends ScheduleBase \sleep($delay); // in seconds - foreach ($scheduleKeys as $scheduleKey) { - // Ensure schedule was not deleted - if (!\array_key_exists($scheduleKey, $this->schedules)) { - return; - } - - $schedule = $this->schedules[$scheduleKey]; - - $this->updateProjectAccess($schedule['project'], $dbForPlatform); - - $pools->get('publisher')->use(function(Publisher $publisher) use ($schedule) { - $queueForFunctions = new Func($publisher); - - $queueForFunctions - ->setType('schedule') - ->setFunction($schedule['resource']) - ->setMethod('POST') - ->setPath('/') - ->setProject($schedule['project']) - ->trigger(); - }); + foreach ($scheduleKeys as $scheduleKey) { + // Ensure schedule was not deleted + if (!\array_key_exists($scheduleKey, $this->schedules)) { + return; } + + $schedule = $this->schedules[$scheduleKey]; + + $this->updateProjectAccess($schedule['project'], $dbForPlatform); + + $pools->get('publisher')->use(function (Publisher $publisher) use ($schedule) { + $queueForFunctions = new Func($publisher); + + $queueForFunctions + ->setType('schedule') + ->setFunction($schedule['resource']) + ->setMethod('POST') + ->setPath('/') + ->setProject($schedule['project']) + ->trigger(); + }); + } }); } diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 319e194b22..75b4a34b87 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -42,7 +42,7 @@ class ScheduleMessages extends ScheduleBase } \go(function () use ($schedule, $pools, $dbForPlatform) { - $pools->get('publisher')->use(function(Publisher $publisher) use ($schedule, $dbForPlatform) { + $pools->get('publisher')->use(function (Publisher $publisher) use ($schedule, $dbForPlatform) { $queueForMessaging = new Messaging($publisher); $this->updateProjectAccess($schedule['project'], $dbForPlatform); From 25a32663017ae995e74c26fdd233454366581f9c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 17 Apr 2025 22:25:36 +1200 Subject: [PATCH 13/39] Remove dump --- app/http.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/http.php b/app/http.php index 8ee65dccf6..b2af26a631 100644 --- a/app/http.php +++ b/app/http.php @@ -171,8 +171,6 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co $database = is_callable($resource) ? $resource() : $resource; break; // exit loop on success } catch (\Exception $e) { - \var_dump($e); - exit; Console::warning(" └── Database not ready. Retrying connection ({$attempts})..."); if ($attempts >= $max) { throw new \Exception(' └── Failed to connect to database: ' . $e->getMessage()); From fe26dcb50aa057340fe5eb087f9c3181a8e8b6ef Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 17 Apr 2025 22:27:20 +1200 Subject: [PATCH 14/39] Add publisher consumer pool adapter --- app/cli.php | 6 +++-- app/http.php | 3 ++- app/init/resources.php | 5 ++-- app/worker.php | 32 +++++++++++------------- composer.json | 6 ++--- composer.lock | 56 ++++++++++++++++++++++++------------------ 6 files changed, 58 insertions(+), 50 deletions(-) diff --git a/app/cli.php b/app/cli.php index f963d32d94..45361c2971 100644 --- a/app/cli.php +++ b/app/cli.php @@ -24,6 +24,7 @@ use Utopia\DSN\DSN; use Utopia\Logger\Log; use Utopia\Platform\Service; use Utopia\Pools\Group; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Publisher; use Utopia\Registry\Registry; use Utopia\System\System; @@ -158,6 +159,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database = null; + return function (?Document $project = null) use ($pools, $cache, $database) { if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') { $database->setTenant($project->getInternalId()); @@ -182,14 +184,14 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) { }; }, ['pools', 'cache']); -CLI::setResource('queueForStatsUsage', function (Connection $publisher) { +CLI::setResource('queueForStatsUsage', function (Publisher $publisher) { return new StatsUsage($publisher); }, ['publisher']); CLI::setResource('queueForStatsResources', function (Publisher $publisher) { return new StatsResources($publisher); }, ['publisher']); CLI::setResource('publisher', function (Group $pools) { - return $pools->get('publisher')->pop()->getResource(); + return new BrokerPool(publisher: $pools->get('publisher')); }, ['pools']); CLI::setResource('queueForFunctions', function (Publisher $publisher) { return new Func($publisher); diff --git a/app/http.php b/app/http.php index b2af26a631..79f3ebd464 100644 --- a/app/http.php +++ b/app/http.php @@ -463,6 +463,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool Console::error('[Error] Message: ' . $th->getMessage()); Console::error('[Error] File: ' . $th->getFile()); Console::error('[Error] Line: ' . $th->getLine()); + Console::error('[Error] Trace: ' . $th->getTraceAsString()); $swooleResponse->setStatusCode(500); @@ -484,7 +485,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool }); // Fetch domains every `DOMAIN_SYNC_TIMER` seconds and update in the memory -$http->on('Task', function () use ($register, $domains) { +$http->on(Constant::EVENT_TASK, function () use ($register, $domains) { $lastSyncUpdate = null; $pools = $register->get('pools'); App::setResource('pools', fn () => $pools); diff --git a/app/init/resources.php b/app/init/resources.php index 4e7d219666..7979e39e64 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -40,6 +40,7 @@ use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Pools\Group; use Utopia\Queue\Publisher; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Storage\Device; use Utopia\Storage\Device\AWS; use Utopia\Storage\Device\Backblaze; @@ -74,10 +75,10 @@ App::setResource('localeCodes', function () { // Queues App::setResource('publisher', function (Group $pools) { - return $pools->get('publisher')->pop()->getResource(); + return new BrokerPool(publisher: $pools->get('publisher')); }, ['pools']); App::setResource('consumer', function (Group $pools) { - return $pools->get('consumer')->pop()->getResource(); + return new BrokerPool(consumer: $pools->get('consumer')); }, ['pools']); App::setResource('queueForMessaging', function (Publisher $publisher) { return new Messaging($publisher); diff --git a/app/worker.php b/app/worker.php index 72ad5f3338..79f8fe0d85 100644 --- a/app/worker.php +++ b/app/worker.php @@ -35,6 +35,8 @@ use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Platform\Service; use Utopia\Pools\Group; +use Utopia\Queue\Adapter\Pool as QueuePool; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Message; use Utopia\Queue\Publisher; use Utopia\Queue\Server; @@ -42,7 +44,7 @@ use Utopia\Registry\Registry; use Utopia\System\System; Authorization::disable(); -Runtime::enableCoroutine(SWOOLE_HOOK_ALL); +Runtime::enableCoroutine(); Server::setResource('register', fn () => $register); @@ -239,11 +241,11 @@ Server::setResource('timelimit', function (\Redis $redis) { Server::setResource('log', fn () => new Log()); Server::setResource('publisher', function (Group $pools) { - return $pools->get('publisher')->pop()->getResource(); + return new BrokerPool(publisher: $pools->get('publisher')); }, ['pools']); Server::setResource('consumer', function (Group $pools) { - return $pools->get('consumer')->pop()->getResource(); + return new BrokerPool(consumer: $pools->get('consumer')); }, ['pools']); Server::setResource('queueForStatsUsage', function (Publisher $publisher) { @@ -408,25 +410,21 @@ try { * - _APP_WORKER_PER_CORE The number of worker processes per core (ignored if _APP_WORKERS_NUM is set) * - _APP_QUEUE_NAME The name of the queue to read for database events */ - $platform->init(Service::TYPE_WORKER, [ - 'workersNum' => System::getEnv('_APP_WORKERS_NUM', 1), - 'connection' => $pools->get('consumer')->pop()->getResource(), - 'workerName' => strtolower($workerName) ?? null, - 'queueName' => $queueName - ]); + $platform->init( + type: Service::TYPE_WORKER, + params: ['workerName' => strtolower($workerName) ?? null], + server: new Server(new QueuePool( + $pools->get('consumer'), + workerNum: System::getEnv('_APP_WORKERS_NUM', 1), + queue: $queueName + )) + ); } catch (\Throwable $e) { Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); } $worker = $platform->getWorker(); -$worker - ->shutdown() - ->inject('pools') - ->action(function (Group $pools) { - $pools->get('consumer')->reclaim(); - }); - $worker ->error() ->inject('error') @@ -435,8 +433,6 @@ $worker ->inject('pools') ->inject('project') ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($worker, $queueName) { - $pools->get('consumer')->reclaim(); - $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); if ($logger) { diff --git a/composer.json b/composer.json index 615d5abd62..5d91aa8116 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "utopia-php/cache": "0.13.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "dev-feat-pool-init as 0.65.0", + "utopia-php/database": "0.66.*", "utopia-php/domains": "0.5.*", "utopia-php/dsn": "0.2.1", "utopia-php/framework": "0.33.*", @@ -62,10 +62,10 @@ "utopia-php/messaging": "0.16.*", "utopia-php/migration": "0.8.*", "utopia-php/orchestration": "0.9.*", - "utopia-php/platform": "0.7.*", + "utopia-php/platform": "dev-feat-custom-server as 0.7.4", "utopia-php/pools": "0.8.*", "utopia-php/preloader": "0.2.*", - "utopia-php/queue": "0.9.*", + "utopia-php/queue": "dev-feat-pool-adapter as 0.9.1", "utopia-php/registry": "0.5.*", "utopia-php/storage": "0.18.*", "utopia-php/swoole": "0.8.*", diff --git a/composer.lock b/composer.lock index 1d39c1b753..9ad06f5317 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": "d56d7960edb985f497bcc816ac294652", + "content-hash": "127ac05400882555232b9d9a9d9e5c0e", "packages": [ { "name": "adhocore/jwt", @@ -3498,16 +3498,16 @@ }, { "name": "utopia-php/database", - "version": "dev-feat-pool-init", + "version": "0.66.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "3f03b9cb56f7d2f09bb9c17e5898d3222a285473" + "reference": "155bc1c0ec68e0224661b1e587e01c6451026031" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/3f03b9cb56f7d2f09bb9c17e5898d3222a285473", - "reference": "3f03b9cb56f7d2f09bb9c17e5898d3222a285473", + "url": "https://api.github.com/repos/utopia-php/database/zipball/155bc1c0ec68e0224661b1e587e01c6451026031", + "reference": "155bc1c0ec68e0224661b1e587e01c6451026031", "shasum": "" }, "require": { @@ -3548,9 +3548,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/feat-pool-init" + "source": "https://github.com/utopia-php/database/tree/0.66.1" }, - "time": "2025-04-17T04:27:21+00:00" + "time": "2025-04-17T07:23:15+00:00" }, { "name": "utopia-php/domains", @@ -4058,16 +4058,16 @@ }, { "name": "utopia-php/platform", - "version": "0.7.4", + "version": "dev-feat-custom-server", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "a5b93d8177702ec458c3af9137663133c012b71b" + "reference": "1afcdb43643531c82d5beaab30c2cfb5bf40ab52" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/a5b93d8177702ec458c3af9137663133c012b71b", - "reference": "a5b93d8177702ec458c3af9137663133c012b71b", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/1afcdb43643531c82d5beaab30c2cfb5bf40ab52", + "reference": "1afcdb43643531c82d5beaab30c2cfb5bf40ab52", "shasum": "" }, "require": { @@ -4102,9 +4102,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.7.4" + "source": "https://github.com/utopia-php/platform/tree/feat-custom-server" }, - "time": "2025-03-13T13:00:12+00:00" + "time": "2025-04-17T07:58:12+00:00" }, { "name": "utopia-php/pools", @@ -4213,16 +4213,16 @@ }, { "name": "utopia-php/queue", - "version": "0.9.1", + "version": "dev-feat-pool-adapter", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "32b6f84c55aae761db5a5ae76cc91ca8dbc8bc32" + "reference": "80cc1fb1053950f977f0d027bb4a7f7c91c1e794" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/32b6f84c55aae761db5a5ae76cc91ca8dbc8bc32", - "reference": "32b6f84c55aae761db5a5ae76cc91ca8dbc8bc32", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/80cc1fb1053950f977f0d027bb4a7f7c91c1e794", + "reference": "80cc1fb1053950f977f0d027bb4a7f7c91c1e794", "shasum": "" }, "require": { @@ -4231,6 +4231,7 @@ "utopia-php/cli": "0.15.*", "utopia-php/fetch": "0.4.*", "utopia-php/framework": "0.33.*", + "utopia-php/pools": "0.8.*", "utopia-php/telemetry": "0.1.*" }, "require-dev": { @@ -4272,9 +4273,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.9.1" + "source": "https://github.com/utopia-php/queue/tree/feat-pool-adapter" }, - "time": "2025-03-28T19:49:36+00:00" + "time": "2025-04-17T08:43:02+00:00" }, { "name": "utopia-php/registry", @@ -8127,15 +8128,22 @@ ], "aliases": [ { - "package": "utopia-php/database", - "version": "dev-feat-pool-init", - "alias": "0.65.0", - "alias_normalized": "0.65.0.0" + "package": "utopia-php/platform", + "version": "dev-feat-custom-server", + "alias": "0.7.4", + "alias_normalized": "0.7.4.0" + }, + { + "package": "utopia-php/queue", + "version": "dev-feat-pool-adapter", + "alias": "0.9.1", + "alias_normalized": "0.9.1.0" } ], "minimum-stability": "stable", "stability-flags": { - "utopia-php/database": 20 + "utopia-php/platform": 20, + "utopia-php/queue": 20 }, "prefer-stable": false, "prefer-lowest": false, From ec65071ae342eb5ec22c04aa2b6af477b701ea8f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 17 Apr 2025 23:59:43 +1200 Subject: [PATCH 15/39] Revert "Add publisher consumer pool adapter" This reverts commit fe26dcb50aa057340fe5eb087f9c3181a8e8b6ef. --- app/worker.php | 24 +++++++++++++++--------- composer.json | 2 +- composer.lock | 29 +++++++++++------------------ 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/app/worker.php b/app/worker.php index 79f8fe0d85..ddac70b8d3 100644 --- a/app/worker.php +++ b/app/worker.php @@ -410,21 +410,25 @@ try { * - _APP_WORKER_PER_CORE The number of worker processes per core (ignored if _APP_WORKERS_NUM is set) * - _APP_QUEUE_NAME The name of the queue to read for database events */ - $platform->init( - type: Service::TYPE_WORKER, - params: ['workerName' => strtolower($workerName) ?? null], - server: new Server(new QueuePool( - $pools->get('consumer'), - workerNum: System::getEnv('_APP_WORKERS_NUM', 1), - queue: $queueName - )) - ); + $platform->init(Service::TYPE_WORKER, [ + 'workersNum' => System::getEnv('_APP_WORKERS_NUM', 1), + 'connection' => $pools->get('consumer')->pop()->getResource(), + 'workerName' => strtolower($workerName) ?? null, + 'queueName' => $queueName + ]); } catch (\Throwable $e) { Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); } $worker = $platform->getWorker(); +$worker + ->shutdown() + ->inject('pools') + ->action(function (Group $pools) { + $pools->get('consumer')->reclaim(); + }); + $worker ->error() ->inject('error') @@ -433,6 +437,8 @@ $worker ->inject('pools') ->inject('project') ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($worker, $queueName) { + $pools->get('consumer')->reclaim(); + $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); if ($logger) { diff --git a/composer.json b/composer.json index 5d91aa8116..afeee6b8fc 100644 --- a/composer.json +++ b/composer.json @@ -62,7 +62,7 @@ "utopia-php/messaging": "0.16.*", "utopia-php/migration": "0.8.*", "utopia-php/orchestration": "0.9.*", - "utopia-php/platform": "dev-feat-custom-server as 0.7.4", + "utopia-php/platform": "0.7.*", "utopia-php/pools": "0.8.*", "utopia-php/preloader": "0.2.*", "utopia-php/queue": "dev-feat-pool-adapter as 0.9.1", diff --git a/composer.lock b/composer.lock index 9ad06f5317..5fd0538e06 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": "127ac05400882555232b9d9a9d9e5c0e", + "content-hash": "c8a1e0dfdd111062fe7a969e17ddc2b5", "packages": [ { "name": "adhocore/jwt", @@ -4058,16 +4058,16 @@ }, { "name": "utopia-php/platform", - "version": "dev-feat-custom-server", + "version": "0.7.4", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "1afcdb43643531c82d5beaab30c2cfb5bf40ab52" + "reference": "a5b93d8177702ec458c3af9137663133c012b71b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/1afcdb43643531c82d5beaab30c2cfb5bf40ab52", - "reference": "1afcdb43643531c82d5beaab30c2cfb5bf40ab52", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/a5b93d8177702ec458c3af9137663133c012b71b", + "reference": "a5b93d8177702ec458c3af9137663133c012b71b", "shasum": "" }, "require": { @@ -4102,9 +4102,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/feat-custom-server" + "source": "https://github.com/utopia-php/platform/tree/0.7.4" }, - "time": "2025-04-17T07:58:12+00:00" + "time": "2025-03-13T13:00:12+00:00" }, { "name": "utopia-php/pools", @@ -4217,12 +4217,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "80cc1fb1053950f977f0d027bb4a7f7c91c1e794" + "reference": "b192e36f646d3f5da80f70e3a69e0065b23a3612" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/80cc1fb1053950f977f0d027bb4a7f7c91c1e794", - "reference": "80cc1fb1053950f977f0d027bb4a7f7c91c1e794", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/b192e36f646d3f5da80f70e3a69e0065b23a3612", + "reference": "b192e36f646d3f5da80f70e3a69e0065b23a3612", "shasum": "" }, "require": { @@ -4275,7 +4275,7 @@ "issues": "https://github.com/utopia-php/queue/issues", "source": "https://github.com/utopia-php/queue/tree/feat-pool-adapter" }, - "time": "2025-04-17T08:43:02+00:00" + "time": "2025-04-17T11:55:59+00:00" }, { "name": "utopia-php/registry", @@ -8127,12 +8127,6 @@ } ], "aliases": [ - { - "package": "utopia-php/platform", - "version": "dev-feat-custom-server", - "alias": "0.7.4", - "alias_normalized": "0.7.4.0" - }, { "package": "utopia-php/queue", "version": "dev-feat-pool-adapter", @@ -8142,7 +8136,6 @@ ], "minimum-stability": "stable", "stability-flags": { - "utopia-php/platform": 20, "utopia-php/queue": 20 }, "prefer-stable": false, From 59c4536208c6fd66639e3f5b7d3d888f409e1ab8 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 18 Apr 2025 00:10:20 +1200 Subject: [PATCH 16/39] Format --- app/init/resources.php | 2 +- app/worker.php | 1 - composer.lock | 8 ++++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/init/resources.php b/app/init/resources.php index 7979e39e64..f48efbe177 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -39,8 +39,8 @@ use Utopia\DSN\DSN; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Pools\Group; -use Utopia\Queue\Publisher; use Utopia\Queue\Broker\Pool as BrokerPool; +use Utopia\Queue\Publisher; use Utopia\Storage\Device; use Utopia\Storage\Device\AWS; use Utopia\Storage\Device\Backblaze; diff --git a/app/worker.php b/app/worker.php index ddac70b8d3..1f540ca5b8 100644 --- a/app/worker.php +++ b/app/worker.php @@ -35,7 +35,6 @@ use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Platform\Service; use Utopia\Pools\Group; -use Utopia\Queue\Adapter\Pool as QueuePool; use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Message; use Utopia\Queue\Publisher; diff --git a/composer.lock b/composer.lock index 5fd0538e06..23793f10ac 100644 --- a/composer.lock +++ b/composer.lock @@ -4217,12 +4217,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "b192e36f646d3f5da80f70e3a69e0065b23a3612" + "reference": "13ad11e65cb29323c25a31a4bf93c4c1113709c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/b192e36f646d3f5da80f70e3a69e0065b23a3612", - "reference": "b192e36f646d3f5da80f70e3a69e0065b23a3612", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/13ad11e65cb29323c25a31a4bf93c4c1113709c0", + "reference": "13ad11e65cb29323c25a31a4bf93c4c1113709c0", "shasum": "" }, "require": { @@ -4275,7 +4275,7 @@ "issues": "https://github.com/utopia-php/queue/issues", "source": "https://github.com/utopia-php/queue/tree/feat-pool-adapter" }, - "time": "2025-04-17T11:55:59+00:00" + "time": "2025-04-17T11:58:10+00:00" }, { "name": "utopia-php/registry", From d9d15b0fbebfbece4c168af16399d862d4f839a5 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 18 Apr 2025 00:21:53 +1200 Subject: [PATCH 17/39] Update queue version --- composer.json | 2 +- composer.lock | 45 ++++++++++++++++++--------------------------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/composer.json b/composer.json index afeee6b8fc..904e2e1f33 100644 --- a/composer.json +++ b/composer.json @@ -65,7 +65,7 @@ "utopia-php/platform": "0.7.*", "utopia-php/pools": "0.8.*", "utopia-php/preloader": "0.2.*", - "utopia-php/queue": "dev-feat-pool-adapter as 0.9.1", + "utopia-php/queue": "0.10.*", "utopia-php/registry": "0.5.*", "utopia-php/storage": "0.18.*", "utopia-php/swoole": "0.8.*", diff --git a/composer.lock b/composer.lock index 23793f10ac..dd63d17463 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": "c8a1e0dfdd111062fe7a969e17ddc2b5", + "content-hash": "420f0840a4b644cd80f341409d13e799", "packages": [ { "name": "adhocore/jwt", @@ -4058,16 +4058,16 @@ }, { "name": "utopia-php/platform", - "version": "0.7.4", + "version": "0.7.5", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "a5b93d8177702ec458c3af9137663133c012b71b" + "reference": "8febd7b6e0c0f2cbd2f4289447bcca97496e4aaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/a5b93d8177702ec458c3af9137663133c012b71b", - "reference": "a5b93d8177702ec458c3af9137663133c012b71b", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/8febd7b6e0c0f2cbd2f4289447bcca97496e4aaf", + "reference": "8febd7b6e0c0f2cbd2f4289447bcca97496e4aaf", "shasum": "" }, "require": { @@ -4076,11 +4076,11 @@ "php": ">=8.0", "utopia-php/cli": "0.15.*", "utopia-php/framework": "0.33.*", - "utopia-php/queue": "0.9.*" + "utopia-php/queue": "0.10.*" }, "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" + "laravel/pint": "1.*", + "phpunit/phpunit": "9.*" }, "type": "library", "autoload": { @@ -4102,9 +4102,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.7.4" + "source": "https://github.com/utopia-php/platform/tree/0.7.5" }, - "time": "2025-03-13T13:00:12+00:00" + "time": "2025-04-17T12:20:16+00:00" }, { "name": "utopia-php/pools", @@ -4213,16 +4213,16 @@ }, { "name": "utopia-php/queue", - "version": "dev-feat-pool-adapter", + "version": "0.10.0", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "13ad11e65cb29323c25a31a4bf93c4c1113709c0" + "reference": "0eccc559168ea72241c39a4c482d868314666be1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/13ad11e65cb29323c25a31a4bf93c4c1113709c0", - "reference": "13ad11e65cb29323c25a31a4bf93c4c1113709c0", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/0eccc559168ea72241c39a4c482d868314666be1", + "reference": "0eccc559168ea72241c39a4c482d868314666be1", "shasum": "" }, "require": { @@ -4273,9 +4273,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/feat-pool-adapter" + "source": "https://github.com/utopia-php/queue/tree/0.10.0" }, - "time": "2025-04-17T11:58:10+00:00" + "time": "2025-04-17T12:15:52+00:00" }, { "name": "utopia-php/registry", @@ -8126,18 +8126,9 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [ - { - "package": "utopia-php/queue", - "version": "dev-feat-pool-adapter", - "alias": "0.9.1", - "alias_normalized": "0.9.1.0" - } - ], + "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "utopia-php/queue": 20 - }, + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { From a9e953d840164160f37ac343d8845d319853c4d3 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 28 Apr 2025 22:02:31 +1200 Subject: [PATCH 18/39] Lint --- src/Appwrite/Messaging/Adapter/Realtime.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 93298a2882..f5b98a3c86 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -8,7 +8,6 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; -use Utopia\Pools\Pool; class Realtime extends MessagingAdapter { From 38aae9e2c18fc748cb2426d919d38a24ebe5ea26 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 29 Apr 2025 14:07:48 +1200 Subject: [PATCH 19/39] Fix sync --- src/Appwrite/Platform/Workers/StatsUsage.php | 14 ++++++-------- src/Appwrite/Platform/Workers/StatsUsageDump.php | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index 66f285fcf5..82ae6162f6 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -325,7 +325,7 @@ class StatsUsage extends Action break; } } catch (Throwable $e) { - console::error("[reducer] " . " {DateTime::now()} " . " {$project->getInternalId()} " . " {$e->getMessage()}"); + Console::error("[reducer] " . " {DateTime::now()} " . " {$project->getInternalId()} " . " {$e->getMessage()}"); } } @@ -344,7 +344,7 @@ class StatsUsage extends Action continue; } - console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); + Console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); try { foreach ($stats['keys'] ?? [] as $key => $value) { @@ -381,7 +381,7 @@ class StatsUsage extends Action } } } catch (Exception $e) { - console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); + Console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); } } @@ -405,7 +405,7 @@ class StatsUsage extends Action } - protected function prepareForLogsDB(Document $project, Document $stat) + protected function prepareForLogsDB(Document $project, Document $stat): void { if (System::getEnv('_APP_STATS_USAGE_DUAL_WRITING', 'disabled') === 'disabled') { return; @@ -429,9 +429,8 @@ class StatsUsage extends Action Console::log('Dual Writing is disabled. Skipping...'); return; } - - $dbForLogs = call_user_func($this->getLogsDB); - $dbForLogs + + $dbForLogs = ($this->getLogsDB)() ->setTenant(null) ->setTenantPerDocument(true); @@ -446,6 +445,5 @@ class StatsUsage extends Action } catch (Throwable $th) { Console::error($th->getMessage()); } - $this->register->get('pools')->get('logs')->reclaim(); } } diff --git a/src/Appwrite/Platform/Workers/StatsUsageDump.php b/src/Appwrite/Platform/Workers/StatsUsageDump.php index ecf6faee78..77ec3f13e6 100644 --- a/src/Appwrite/Platform/Workers/StatsUsageDump.php +++ b/src/Appwrite/Platform/Workers/StatsUsageDump.php @@ -126,7 +126,7 @@ class StatsUsageDump extends Action continue; } - console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); + Console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); try { /** @var Database $dbForProject */ @@ -169,7 +169,7 @@ class StatsUsageDump extends Action } } } catch (\Exception $e) { - console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); + Console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); } } } From 43ae7c4b140a6ed4c32ea71fcdaa03d6d368c8b6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 29 Apr 2025 14:57:18 +1200 Subject: [PATCH 20/39] Format --- src/Appwrite/Platform/Workers/StatsUsage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index 82ae6162f6..60fab5d2ea 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -429,7 +429,7 @@ class StatsUsage extends Action Console::log('Dual Writing is disabled. Skipping...'); return; } - + $dbForLogs = ($this->getLogsDB)() ->setTenant(null) ->setTenantPerDocument(true); From 47c5c552674ef29782f72566fac22c57de86f7e7 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 29 Apr 2025 14:58:54 +1200 Subject: [PATCH 21/39] Remove redundant reclaims --- app/worker.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/worker.php b/app/worker.php index 1f540ca5b8..1ae2108a62 100644 --- a/app/worker.php +++ b/app/worker.php @@ -421,13 +421,6 @@ try { $worker = $platform->getWorker(); -$worker - ->shutdown() - ->inject('pools') - ->action(function (Group $pools) { - $pools->get('consumer')->reclaim(); - }); - $worker ->error() ->inject('error') @@ -436,8 +429,6 @@ $worker ->inject('pools') ->inject('project') ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($worker, $queueName) { - $pools->get('consumer')->reclaim(); - $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); if ($logger) { From eabf3993a05683a469c94356be94923769f847f0 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 29 Apr 2025 18:13:30 +1200 Subject: [PATCH 22/39] Use pool adapter on project create --- app/controllers/api/projects.php | 114 +++++++++++++++---------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index b998cc1e0c..e10fa736da 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -25,6 +25,7 @@ use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\Config\Config; use Utopia\Database\Adapter; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -224,74 +225,73 @@ App::post('/v1/projects') $sharedTables = $sharedTablesV1 || $sharedTablesV2; if (!$sharedTablesV2) { - $pools->get($dsn->getHost())->use(function (Adapter $adapter) use ($project, $dsn, $cache, $projectTables, $sharedTables, $sharedTablesV1) { - $dbForProject = new Database($adapter, $cache); + $adapter = new DatabasePool($pools->get($dsn->getHost())); + $dbForProject = new Database($adapter, $cache); - if ($sharedTables) { - $dbForProject - ->setSharedTables(true) - ->setTenant($sharedTablesV1 ? $project->getInternalId() : null) - ->setNamespace($dsn->getParam('namespace')); - } else { - $dbForProject - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } + if ($sharedTables) { + $dbForProject + ->setSharedTables(true) + ->setTenant($sharedTablesV1 ? $project->getInternalId() : null) + ->setNamespace($dsn->getParam('namespace')); + } else { + $dbForProject + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } - $create = true; + $create = true; - try { - $dbForProject->create(); - } catch (Duplicate) { - $create = false; - } + try { + $dbForProject->create(); + } catch (Duplicate) { + $create = false; + } - if ($create || $projectTables) { - $audit = new Audit($dbForProject); - $audit->setup(); - } + if ($create || $projectTables) { + $audit = new Audit($dbForProject); + $audit->setup(); + } - if (!$create && $sharedTablesV1) { - $attributes = \array_map(fn ($attribute) => new Document($attribute), Audit::ATTRIBUTES); - $indexes = \array_map(fn (array $index) => new Document($index), Audit::INDEXES); - $dbForProject->createDocument(Database::METADATA, new Document([ - '$id' => ID::custom('audit'), - '$permissions' => [Permission::create(Role::any())], - 'name' => 'audit', - 'attributes' => $attributes, - 'indexes' => $indexes, - 'documentSecurity' => true - ])); - } + if (!$create && $sharedTablesV1) { + $attributes = \array_map(fn ($attribute) => new Document($attribute), Audit::ATTRIBUTES); + $indexes = \array_map(fn (array $index) => new Document($index), Audit::INDEXES); + $dbForProject->createDocument(Database::METADATA, new Document([ + '$id' => ID::custom('audit'), + '$permissions' => [Permission::create(Role::any())], + 'name' => 'audit', + 'attributes' => $attributes, + 'indexes' => $indexes, + 'documentSecurity' => true + ])); + } - if ($create || $sharedTablesV1) { - /** @var array $collections */ - $collections = Config::getParam('collections', [])['projects'] ?? []; + if ($create || $sharedTablesV1) { + /** @var array $collections */ + $collections = Config::getParam('collections', [])['projects'] ?? []; - foreach ($collections as $key => $collection) { - if (($collection['$collection'] ?? '') !== Database::METADATA) { - continue; - } + foreach ($collections as $key => $collection) { + if (($collection['$collection'] ?? '') !== Database::METADATA) { + continue; + } - $attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']); - $indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']); + $attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']); + $indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']); - try { - $dbForProject->createCollection($key, $attributes, $indexes); - } catch (Duplicate) { - $dbForProject->createDocument(Database::METADATA, new Document([ - '$id' => ID::custom($key), - '$permissions' => [Permission::create(Role::any())], - 'name' => $key, - 'attributes' => $attributes, - 'indexes' => $indexes, - 'documentSecurity' => true - ])); - } + try { + $dbForProject->createCollection($key, $attributes, $indexes); + } catch (Duplicate) { + $dbForProject->createDocument(Database::METADATA, new Document([ + '$id' => ID::custom($key), + '$permissions' => [Permission::create(Role::any())], + 'name' => $key, + 'attributes' => $attributes, + 'indexes' => $indexes, + 'documentSecurity' => true + ])); } } - }); + } } // Hook allowing instant project mirroring during migration From 1d82c67d1f1a139a94614a699087a0d789d72e43 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 29 Apr 2025 21:28:13 +1200 Subject: [PATCH 23/39] Add pubsub pool adapter --- src/Appwrite/PubSub/Adapter/Pool.php | 46 ++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/Appwrite/PubSub/Adapter/Pool.php diff --git a/src/Appwrite/PubSub/Adapter/Pool.php b/src/Appwrite/PubSub/Adapter/Pool.php new file mode 100644 index 0000000000..15eb51f219 --- /dev/null +++ b/src/Appwrite/PubSub/Adapter/Pool.php @@ -0,0 +1,46 @@ +delegate(__FUNCTION__, \func_get_args()); + } + + public function subscribe($channels, $callback): void + { + $this->delegate(__FUNCTION__, \func_get_args()); + } + + public function publish($channel, $message): void + { + $this->delegate(__FUNCTION__, \func_get_args()); + } + + /** + * Forward method calls to the internal adapter instance via the pool. + * + * Required because __call() can't be used to implement abstract methods. + * + * @param string $method + * @param array $args + * @return mixed + * @throws DatabaseException + */ + public function delegate(string $method, array $args): mixed + { + return $this->pool->use(function (Adapter $adapter) use ($method, $args) { + return $adapter->{$method}(...$args); + }); + } +} \ No newline at end of file From acec1ab699828bd7077ea968636ae6b7b3115e92 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 29 Apr 2025 21:28:25 +1200 Subject: [PATCH 24/39] Use pool adapters in health api --- app/controllers/api/health.php | 75 ++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index acbd20618d..5c92e1a787 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -4,6 +4,7 @@ use Appwrite\ClamAV\Network; use Appwrite\Event\Event; use Appwrite\Extend\Exception; use Appwrite\PubSub\Adapter as PubSubAdapter; +use Appwrite\PubSub\Adapter\Pool as PubSubPool; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -11,8 +12,10 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\App; use Utopia\Cache\Adapter as CacheAdapter; +use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Config\Config; use Utopia\Database\Adapter as DatabaseAdapter; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Document; use Utopia\Domains\Validator\PublicDomain; use Utopia\Pools\Group; @@ -100,19 +103,19 @@ App::get('/v1/health/db') foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $pools->get($database)->use(function (DatabaseAdapter $adapter) use ($key, $database, &$output, &$failures) { - $checkStart = \microtime(true); + $adapter = new DatabasePool($pools->get($database)); - if ($adapter->ping()) { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } else { - $failures[] = $database; - } - }); + $checkStart = \microtime(true); + + if ($adapter->ping()) { + $output[] = new Document([ + 'name' => $key . " ($database)", + 'status' => 'pass', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); + } else { + $failures[] = $database; + } } catch (\Throwable) { $failures[] = $database; } @@ -160,19 +163,19 @@ App::get('/v1/health/cache') foreach ($configs as $key => $config) { foreach ($config as $cache) { try { - $pools->get($cache)->use(function (CacheAdapter $adapter) use ($key, $cache, &$output, &$failures) { - $checkStart = \microtime(true); + $adapter = new CachePool($pools->get($cache)); - if ($adapter->ping()) { - $output[] = new Document([ - 'name' => $key . " ($cache)", - 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } else { - $failures[] = $cache; - } - }); + $checkStart = \microtime(true); + + if ($adapter->ping()) { + $output[] = new Document([ + 'name' => $key . " ($cache)", + 'status' => 'pass', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); + } else { + $failures[] = $cache; + } } catch (\Throwable) { $failures[] = $cache; } @@ -220,19 +223,19 @@ App::get('/v1/health/pubsub') foreach ($configs as $key => $config) { foreach ($config as $pubsub) { try { - $pools->get($pubsub)->use(function (PubSubAdapter $adapter) use ($key, $pubsub, &$output, &$failures) { - $checkStart = \microtime(true); + $adapter = new PubSubPool($pools->get($pubsub)); - if ($adapter->ping()) { - $output[] = new Document([ - 'name' => $key . " ($pubsub)", - 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } else { - $failures[] = $pubsub; - } - }); + $checkStart = \microtime(true); + + if ($adapter->ping()) { + $output[] = new Document([ + 'name' => $key . " ($pubsub)", + 'status' => 'pass', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); + } else { + $failures[] = $pubsub; + } } catch (\Throwable) { $failures[] = $pubsub; } From 4d92af5ba6228b9df5f00e34a4a213583db6635c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 29 Apr 2025 21:28:40 +1200 Subject: [PATCH 25/39] Realtime pool adapters --- app/realtime.php | 95 +++++++++++---------- src/Appwrite/Messaging/Adapter/Realtime.php | 40 ++++----- 2 files changed, 69 insertions(+), 66 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index 791a2ccc15..55757f0db0 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -6,6 +6,7 @@ use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Network\Validator\Origin; use Appwrite\PubSub\Adapter as PubSubAdapter; +use Appwrite\PubSub\Adapter\Pool as PubSubPool; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Swoole\Http\Request as SwooleRequest; @@ -393,58 +394,58 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, } $start = time(); - $register->get('pools')->get('pubsub')->use(function (PubSubAdapter $pubsub) use ($server, $workerId, $stats, $register, $realtime) { - if ($pubsub->ping(true)) { - $attempts = 0; - Console::success('Pub/sub connection established (worker: ' . $workerId . ')'); - } else { - Console::error('Pub/sub failed (worker: ' . $workerId . ')'); + $pubsub = new PubSubPool($register->get('pools')->get('pubsub')); + + if ($pubsub->ping(true)) { + $attempts = 0; + Console::success('Pub/sub connection established (worker: ' . $workerId . ')'); + } else { + Console::error('Pub/sub failed (worker: ' . $workerId . ')'); + } + + $pubsub->subscribe(['realtime'], function (mixed $redis, string $channel, string $payload) use ($server, $workerId, $stats, $register, $realtime) { + $event = json_decode($payload, true); + + if ($event['permissionsChanged'] && isset($event['userId'])) { + $projectId = $event['project']; + $userId = $event['userId']; + + if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) { + $connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId])); + $consoleDatabase = getConsoleDB(); + $project = Authorization::skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); + $database = getProjectDB($project); + + $user = $database->getDocument('users', $userId); + + $roles = Auth::getRoles($user); + $channels = $realtime->connections[$connection]['channels']; + + $realtime->unsubscribe($connection); + $realtime->subscribe($projectId, $connection, $roles, $channels); + } } - $pubsub->subscribe(['realtime'], function (mixed $redis, string $channel, string $payload) use ($server, $workerId, $stats, $register, $realtime) { - $event = json_decode($payload, true); + $receivers = $realtime->getSubscribers($event); - if ($event['permissionsChanged'] && isset($event['userId'])) { - $projectId = $event['project']; - $userId = $event['userId']; + if (App::isDevelopment() && !empty($receivers)) { + Console::log("[Debug][Worker {$workerId}] Receivers: " . count($receivers)); + Console::log("[Debug][Worker {$workerId}] Receivers Connection IDs: " . json_encode($receivers)); + Console::log("[Debug][Worker {$workerId}] Event: " . $payload); + } - if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) { - $connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId])); - $consoleDatabase = getConsoleDB(); - $project = Authorization::skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); - $database = getProjectDB($project); + $server->send( + $receivers, + json_encode([ + 'type' => 'event', + 'data' => $event['data'] + ]) + ); - $user = $database->getDocument('users', $userId); - - $roles = Auth::getRoles($user); - $channels = $realtime->connections[$connection]['channels']; - - $realtime->unsubscribe($connection); - $realtime->subscribe($projectId, $connection, $roles, $channels); - } - } - - $receivers = $realtime->getSubscribers($event); - - if (App::isDevelopment() && !empty($receivers)) { - Console::log("[Debug][Worker {$workerId}] Receivers: " . count($receivers)); - Console::log("[Debug][Worker {$workerId}] Receivers Connection IDs: " . json_encode($receivers)); - Console::log("[Debug][Worker {$workerId}] Event: " . $payload); - } - - $server->send( - $receivers, - json_encode([ - 'type' => 'event', - 'data' => $event['data'] - ]) - ); - - if (($num = count($receivers)) > 0) { - $register->get('telemetry.messageSentCounter')->add($num); - $stats->incr($event['project'], 'messages', $num); - } - }); + if (($num = count($receivers)) > 0) { + $register->get('telemetry.messageSentCounter')->add($num); + $stats->incr($event['project'], 'messages', $num); + } }); } catch (Throwable $th) { $logError($th, "pubSubConnection"); diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index f5b98a3c86..ae6aa4d57c 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -3,7 +3,7 @@ namespace Appwrite\Messaging\Adapter; use Appwrite\Messaging\Adapter as MessagingAdapter; -use Appwrite\PubSub\Adapter as PubSubAdapter; +use Appwrite\PubSub\Adapter\Pool as PubSubPool; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; @@ -36,6 +36,13 @@ class Realtime extends MessagingAdapter */ public array $subscriptions = []; + private PubSubPool $redis; + + public function __construct() + { + $this->redis = new PubSubPool($register->get('pools')->get('pubsub')); + } + /** * Adds a subscription. * @@ -139,24 +146,19 @@ class Realtime extends MessagingAdapter $permissionsChanged = array_key_exists('permissionsChanged', $options) && $options['permissionsChanged']; $userId = array_key_exists('userId', $options) ? $options['userId'] : null; - - global $register; - - $register->get('pools')->get('pubsub')->use( - fn (PubSubAdapter $redis) => - $redis->publish('realtime', json_encode([ - 'project' => $projectId, - 'roles' => $roles, - 'permissionsChanged' => $permissionsChanged, - 'userId' => $userId, - 'data' => [ - 'events' => $events, - 'channels' => $channels, - 'timestamp' => DateTime::formatTz(DateTime::now()), - 'payload' => $payload - ] - ])) - ); + + $this->redis->publish('realtime', json_encode([ + 'project' => $projectId, + 'roles' => $roles, + 'permissionsChanged' => $permissionsChanged, + 'userId' => $userId, + 'data' => [ + 'events' => $events, + 'channels' => $channels, + 'timestamp' => DateTime::formatTz(DateTime::now()), + 'payload' => $payload + ] + ])); } /** From 02b1c822260162f66c25194d8108d88eb55ff11b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 29 Apr 2025 21:28:53 +1200 Subject: [PATCH 26/39] Remove all remaining pool usages --- src/Appwrite/Platform/Tasks/Doctor.php | 38 +++++++++++-------- .../Platform/Tasks/ScheduleExecutions.php | 33 ++++++++-------- .../Platform/Tasks/ScheduleFunctions.php | 24 ++++++------ .../Platform/Tasks/ScheduleMessages.php | 19 +++++----- 4 files changed, 60 insertions(+), 54 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index fba636028b..5263133eba 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -3,17 +3,19 @@ namespace Appwrite\Platform\Tasks; use Appwrite\ClamAV\Network; -use Appwrite\PubSub\Adapter as PubSubAdapter; +use Appwrite\PubSub\Adapter\Pool as PubSubPool; use PHPMailer\PHPMailer\PHPMailer; use Utopia\App; +use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter as DatabaseAdapter; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Domains\Domain; use Utopia\DSN\DSN; use Utopia\Logger\Logger; use Utopia\Platform\Action; use Utopia\Pools\Group; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Registry\Registry; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; @@ -136,13 +138,13 @@ class Doctor extends Action foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $pools->get($database)->use(function (DatabaseAdapter $adapter) use ($key, $database) { - if ($adapter->ping()) { - Console::success('🟢 ' . str_pad("{$key}({$database})", 50, '.') . 'connected'); - } else { - Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected'); - } - }); + $adapter = new DatabasePool($pools->get($database)); + + if ($adapter->ping()) { + Console::success('🟢 ' . str_pad("{$key}({$database})", 50, '.') . 'connected'); + } else { + Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected'); + } } catch (\Throwable) { Console::error('🔴 ' . str_pad("{$key}.({$database})", 47, '.') . 'disconnected'); } @@ -161,13 +163,17 @@ class Doctor extends Action foreach ($configs as $key => $config) { foreach ($config as $pool) { try { - $pools->get($pool)->use(function (PubSubAdapter $adapter) use ($key, $pool) { - if ($adapter->ping()) { - Console::success('🟢 ' . str_pad("{$key}({$pool})", 50, '.') . 'connected'); - } else { - Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); - } - }); + $adapter = match($key) { + 'Cache' => new CachePool($pools->get($pool)), + 'Queue' => new BrokerPool($pools->get($pool)), + 'PubSub' => new PubSubPool($pools->get($pool)), + }; + + if ($adapter->ping()) { + Console::success('🟢 ' . str_pad("{$key}({$pool})", 50, '.') . 'connected'); + } else { + Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); + } } catch (\Throwable) { Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); } diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index f1a4c3ed79..ffde4f6df8 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -3,9 +3,9 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Func; -use Swoole\Coroutine as Co; use Utopia\Database\Database; use Utopia\Pools\Group; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Publisher; class ScheduleExecutions extends ScheduleBase @@ -58,24 +58,23 @@ class ScheduleExecutions extends ScheduleBase $this->updateProjectAccess($schedule['project'], $dbForPlatform); \go(function () use ($schedule, $delay, $data, $pools) { - Co::sleep($delay); + \Co::sleep($delay); - $pools->get('publisher')->use(function (Publisher $publisher) use ($schedule, $data) { - $queueForFunctions = new Func($publisher); + $publisher = new BrokerPool($pools->get('publisher')); - $queueForFunctions->setType('schedule') - // Set functionId instead of function as we don't have $dbForProject - // TODO: Refactor to use function instead of functionId - ->setFunctionId($schedule['resource']['functionId']) - ->setExecution($schedule['resource']) - ->setMethod($data['method'] ?? 'POST') - ->setPath($data['path'] ?? '/') - ->setHeaders($data['headers'] ?? []) - ->setBody($data['body'] ?? '') - ->setProject($schedule['project']) - ->setUserId($data['userId'] ?? '') - ->trigger(); - }); + $queueForFunctions = new Func($publisher); + + $queueForFunctions->setType('schedule') + // Set functionId instead of function as we don't have $dbForProject + // TODO: Refactor to use function instead of functionId + ->setFunctionId($schedule['resource']['functionId']) + ->setExecution($schedule['resource']) + ->setMethod($data['method'] ?? 'POST') + ->setPath($data['path'] ?? '/') + ->setHeaders($data['headers'] ?? []) + ->setBody($data['body'] ?? '') + ->setProject($schedule['project']) + ->setUserId($data['userId'] ?? ''); }); $dbForPlatform->deleteDocument( diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 328c9f894c..9555d166b4 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -8,6 +8,7 @@ use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Pools\Group; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Publisher; class ScheduleFunctions extends ScheduleBase @@ -72,8 +73,7 @@ class ScheduleFunctions extends ScheduleBase foreach ($delayedExecutions as $delay => $scheduleKeys) { \go(function () use ($delay, $scheduleKeys, $pools, $dbForPlatform) { - \sleep($delay); // in seconds - + \Co::sleep($delay); // in seconds foreach ($scheduleKeys as $scheduleKey) { // Ensure schedule was not deleted @@ -85,17 +85,17 @@ class ScheduleFunctions extends ScheduleBase $this->updateProjectAccess($schedule['project'], $dbForPlatform); - $pools->get('publisher')->use(function (Publisher $publisher) use ($schedule) { - $queueForFunctions = new Func($publisher); + $publisher = new BrokerPool($pools->get('publisher')); - $queueForFunctions - ->setType('schedule') - ->setFunction($schedule['resource']) - ->setMethod('POST') - ->setPath('/') - ->setProject($schedule['project']) - ->trigger(); - }); + $queueForFunctions = new Func($publisher); + + $queueForFunctions + ->setType('schedule') + ->setFunction($schedule['resource']) + ->setMethod('POST') + ->setPath('/') + ->setProject($schedule['project']) + ->trigger(); } }); } diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 75b4a34b87..508c442359 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -5,6 +5,7 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Messaging; use Utopia\Database\Database; use Utopia\Pools\Group; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Publisher; class ScheduleMessages extends ScheduleBase @@ -42,17 +43,17 @@ class ScheduleMessages extends ScheduleBase } \go(function () use ($schedule, $pools, $dbForPlatform) { - $pools->get('publisher')->use(function (Publisher $publisher) use ($schedule, $dbForPlatform) { - $queueForMessaging = new Messaging($publisher); + $publisher = new BrokerPool($pools->get('publisher')); - $this->updateProjectAccess($schedule['project'], $dbForPlatform); + $queueForMessaging = new Messaging($publisher); - $queueForMessaging - ->setType(MESSAGE_SEND_TYPE_EXTERNAL) - ->setMessageId($schedule['resourceId']) - ->setProject($schedule['project']) - ->trigger(); - }); + $this->updateProjectAccess($schedule['project'], $dbForPlatform); + + $queueForMessaging + ->setType(MESSAGE_SEND_TYPE_EXTERNAL) + ->setMessageId($schedule['resourceId']) + ->setProject($schedule['project']) + ->trigger(); $dbForPlatform->deleteDocument( 'schedules', From e643e7a406e9cfe1c7281270a516b9b732e99308 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 29 Apr 2025 21:33:51 +1200 Subject: [PATCH 27/39] Lint --- app/controllers/api/health.php | 3 --- app/controllers/api/projects.php | 1 - app/realtime.php | 1 - src/Appwrite/Messaging/Adapter/Realtime.php | 2 +- src/Appwrite/Platform/Tasks/ScheduleExecutions.php | 1 - src/Appwrite/Platform/Tasks/ScheduleFunctions.php | 1 - src/Appwrite/Platform/Tasks/ScheduleMessages.php | 1 - src/Appwrite/PubSub/Adapter/Pool.php | 2 +- 8 files changed, 2 insertions(+), 10 deletions(-) diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 5c92e1a787..5fe2de5549 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -3,7 +3,6 @@ use Appwrite\ClamAV\Network; use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\PubSub\Adapter as PubSubAdapter; use Appwrite\PubSub\Adapter\Pool as PubSubPool; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,10 +10,8 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\App; -use Utopia\Cache\Adapter as CacheAdapter; use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Config\Config; -use Utopia\Database\Adapter as DatabaseAdapter; use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Document; use Utopia\Domains\Validator\PublicDomain; diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index e10fa736da..c4f0b6a9df 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -24,7 +24,6 @@ use Utopia\App; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\Config\Config; -use Utopia\Database\Adapter; use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; diff --git a/app/realtime.php b/app/realtime.php index 55757f0db0..5864943f40 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -5,7 +5,6 @@ use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Network\Validator\Origin; -use Appwrite\PubSub\Adapter as PubSubAdapter; use Appwrite\PubSub\Adapter\Pool as PubSubPool; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index ae6aa4d57c..476db8006a 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -146,7 +146,7 @@ class Realtime extends MessagingAdapter $permissionsChanged = array_key_exists('permissionsChanged', $options) && $options['permissionsChanged']; $userId = array_key_exists('userId', $options) ? $options['userId'] : null; - + $this->redis->publish('realtime', json_encode([ 'project' => $projectId, 'roles' => $roles, diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index ffde4f6df8..edb57f4df7 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -6,7 +6,6 @@ use Appwrite\Event\Func; use Utopia\Database\Database; use Utopia\Pools\Group; use Utopia\Queue\Broker\Pool as BrokerPool; -use Utopia\Queue\Publisher; class ScheduleExecutions extends ScheduleBase { diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 9555d166b4..641186fdf3 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -9,7 +9,6 @@ use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Pools\Group; use Utopia\Queue\Broker\Pool as BrokerPool; -use Utopia\Queue\Publisher; class ScheduleFunctions extends ScheduleBase { diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 508c442359..bbd3e0c8c3 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -6,7 +6,6 @@ use Appwrite\Event\Messaging; use Utopia\Database\Database; use Utopia\Pools\Group; use Utopia\Queue\Broker\Pool as BrokerPool; -use Utopia\Queue\Publisher; class ScheduleMessages extends ScheduleBase { diff --git a/src/Appwrite/PubSub/Adapter/Pool.php b/src/Appwrite/PubSub/Adapter/Pool.php index 15eb51f219..a498118dae 100644 --- a/src/Appwrite/PubSub/Adapter/Pool.php +++ b/src/Appwrite/PubSub/Adapter/Pool.php @@ -43,4 +43,4 @@ class Pool implements Adapter return $adapter->{$method}(...$args); }); } -} \ No newline at end of file +} From 5fc2c1ceb84f82fecaa38367d99b6492b3fa7d9e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 30 Apr 2025 10:34:24 +1200 Subject: [PATCH 28/39] Fix registry ref --- src/Appwrite/Messaging/Adapter/Realtime.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 476db8006a..18b0d94e7d 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -40,6 +40,7 @@ class Realtime extends MessagingAdapter public function __construct() { + global $register; $this->redis = new PubSubPool($register->get('pools')->get('pubsub')); } From f7296d80b241734a655de3fcaf8ffb1959d92192 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 30 Apr 2025 12:27:18 +1200 Subject: [PATCH 29/39] Fix function schedule sleep --- composer.lock | 24 +++++++++---------- .../Platform/Tasks/ScheduleFunctions.php | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/composer.lock b/composer.lock index fbe5efda36..573c822296 100644 --- a/composer.lock +++ b/composer.lock @@ -3498,16 +3498,16 @@ }, { "name": "utopia-php/database", - "version": "0.66.1", + "version": "0.66.2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "155bc1c0ec68e0224661b1e587e01c6451026031" + "reference": "944d1979c4ba572c746cad957fe34aead338ee34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/155bc1c0ec68e0224661b1e587e01c6451026031", - "reference": "155bc1c0ec68e0224661b1e587e01c6451026031", + "url": "https://api.github.com/repos/utopia-php/database/zipball/944d1979c4ba572c746cad957fe34aead338ee34", + "reference": "944d1979c4ba572c746cad957fe34aead338ee34", "shasum": "" }, "require": { @@ -3548,9 +3548,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.66.1" + "source": "https://github.com/utopia-php/database/tree/0.66.2" }, - "time": "2025-04-17T07:23:15+00:00" + "time": "2025-04-29T23:35:39+00:00" }, { "name": "utopia-php/domains", @@ -5233,16 +5233,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.0", + "version": "1.13.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414" + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", "shasum": "" }, "require": { @@ -5281,7 +5281,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" }, "funding": [ { @@ -5289,7 +5289,7 @@ "type": "tidelift" } ], - "time": "2025-02-12T12:17:51+00:00" + "time": "2025-04-29T12:36:36+00:00" }, { "name": "nikic/php-parser", diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 641186fdf3..11072170d7 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -72,7 +72,7 @@ class ScheduleFunctions extends ScheduleBase foreach ($delayedExecutions as $delay => $scheduleKeys) { \go(function () use ($delay, $scheduleKeys, $pools, $dbForPlatform) { - \Co::sleep($delay); // in seconds + \sleep($delay); // in seconds foreach ($scheduleKeys as $scheduleKey) { // Ensure schedule was not deleted From 43fb92ca83845da8adcbf5d57d0f9dc89a005768 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 30 Apr 2025 13:04:19 +1200 Subject: [PATCH 30/39] Set publisher on base --- src/Appwrite/Platform/Tasks/ScheduleBase.php | 5 +++++ src/Appwrite/Platform/Tasks/ScheduleExecutions.php | 5 +---- src/Appwrite/Platform/Tasks/ScheduleMessages.php | 4 +--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index 69ad75316b..7b78826491 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -12,6 +12,7 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; use Utopia\Pools\Group; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\System\System; use function Swoole\Coroutine\run; @@ -23,6 +24,8 @@ abstract class ScheduleBase extends Action protected array $schedules = []; + protected BrokerPool $publisher; + abstract public static function getName(): string; abstract public static function getSupportedResource(): string; abstract public static function getCollectionId(): string; @@ -61,6 +64,8 @@ abstract class ScheduleBase extends Action Console::title(\ucfirst(static::getSupportedResource()) . ' scheduler V1'); Console::success(APP_NAME . ' ' . \ucfirst(static::getSupportedResource()) . ' scheduler v1 has started'); + $this->publisher = new BrokerPool($pools->get('publisher')); + /** * Extract only necessary attributes to lower memory used. * diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index edb57f4df7..3524793e4f 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Func; use Utopia\Database\Database; use Utopia\Pools\Group; -use Utopia\Queue\Broker\Pool as BrokerPool; class ScheduleExecutions extends ScheduleBase { @@ -59,9 +58,7 @@ class ScheduleExecutions extends ScheduleBase \go(function () use ($schedule, $delay, $data, $pools) { \Co::sleep($delay); - $publisher = new BrokerPool($pools->get('publisher')); - - $queueForFunctions = new Func($publisher); + $queueForFunctions = new Func($this->publisher); $queueForFunctions->setType('schedule') // Set functionId instead of function as we don't have $dbForProject diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index bbd3e0c8c3..2aa248c073 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -42,9 +42,7 @@ class ScheduleMessages extends ScheduleBase } \go(function () use ($schedule, $pools, $dbForPlatform) { - $publisher = new BrokerPool($pools->get('publisher')); - - $queueForMessaging = new Messaging($publisher); + $queueForMessaging = new Messaging($this->publisher); $this->updateProjectAccess($schedule['project'], $dbForPlatform); From ea38a7f869ab1345352d4718d46b90ef3b9cc4e6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 30 Apr 2025 13:05:31 +1200 Subject: [PATCH 31/39] Coroutine sleep --- src/Appwrite/Platform/Tasks/ScheduleFunctions.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 11072170d7..12da0a689c 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -72,7 +72,7 @@ class ScheduleFunctions extends ScheduleBase foreach ($delayedExecutions as $delay => $scheduleKeys) { \go(function () use ($delay, $scheduleKeys, $pools, $dbForPlatform) { - \sleep($delay); // in seconds + \Co::sleep($delay); // in seconds foreach ($scheduleKeys as $scheduleKey) { // Ensure schedule was not deleted @@ -84,9 +84,7 @@ class ScheduleFunctions extends ScheduleBase $this->updateProjectAccess($schedule['project'], $dbForPlatform); - $publisher = new BrokerPool($pools->get('publisher')); - - $queueForFunctions = new Func($publisher); + $queueForFunctions = new Func($this->publisher); $queueForFunctions ->setType('schedule') From 2e2af32be3213ec009ffb5c301fbf013db63a6b5 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 30 Apr 2025 13:06:51 +1200 Subject: [PATCH 32/39] Fix missing trigger --- src/Appwrite/Platform/Tasks/ScheduleExecutions.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index 3524793e4f..f961037b2e 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -70,7 +70,8 @@ class ScheduleExecutions extends ScheduleBase ->setHeaders($data['headers'] ?? []) ->setBody($data['body'] ?? '') ->setProject($schedule['project']) - ->setUserId($data['userId'] ?? ''); + ->setUserId($data['userId'] ?? '') + ->trigger(); }); $dbForPlatform->deleteDocument( From ed0929416b4de77520daab287b1d2235fd2080b2 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 30 Apr 2025 13:13:14 +1200 Subject: [PATCH 33/39] Format --- src/Appwrite/Platform/Tasks/ScheduleFunctions.php | 1 - src/Appwrite/Platform/Tasks/ScheduleMessages.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 12da0a689c..f7a2f603e6 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -8,7 +8,6 @@ use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Pools\Group; -use Utopia\Queue\Broker\Pool as BrokerPool; class ScheduleFunctions extends ScheduleBase { diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 2aa248c073..6c24f81026 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Messaging; use Utopia\Database\Database; use Utopia\Pools\Group; -use Utopia\Queue\Broker\Pool as BrokerPool; class ScheduleMessages extends ScheduleBase { From 6e59859f5c3b5bd0aa5e9224eb3a92b0963f1046 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 30 Apr 2025 21:18:24 +1200 Subject: [PATCH 34/39] AI review fixes --- app/realtime.php | 52 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index 5864943f40..7e6fc0e311 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -51,6 +51,12 @@ if (!function_exists('getConsoleDB')) { { global $register; + static $database = null; + + if ($database !== null) { + return $database; + } + /** @var Group $pools */ $pools = $register->get('pools'); @@ -71,6 +77,12 @@ if (!function_exists('getProjectDB')) { { global $register; + static $databases = []; + + if (isset($databases[$project->getInternalId()])) { + return $databases[$project->getInternalId()]; + } + /** @var Group $pools */ $pools = $register->get('pools'); @@ -106,7 +118,7 @@ if (!function_exists('getProjectDB')) { ->setMetadata('host', \gethostname()) ->setMetadata('project', $project->getId()); - return $database; + return $databases[$project->getInternalId()] = $database; } } @@ -116,6 +128,12 @@ if (!function_exists('getCache')) { { global $register; + static $cache = null; + + if ($cache !== null) { + return $cache; + } + $pools = $register->get('pools'); /** @var Group $pools */ $list = Config::getParam('pools-cache', []); @@ -125,7 +143,7 @@ if (!function_exists('getCache')) { $adapters[] = new CachePool($pools->get($value)); } - return new Cache(new Sharding($adapters)); + return $cache = new Cache(new Sharding($adapters)); } } @@ -133,6 +151,12 @@ if (!function_exists('getCache')) { if (!function_exists('getRedis')) { function getRedis(): \Redis { + static $redis = null; + + if ($redis !== null) { + return $redis; + } + $host = System::getEnv('_APP_REDIS_HOST', 'localhost'); $port = System::getEnv('_APP_REDIS_PORT', 6379); $pass = System::getEnv('_APP_REDIS_PASS', ''); @@ -151,21 +175,39 @@ if (!function_exists('getRedis')) { if (!function_exists('getTimelimit')) { function getTimelimit(): TimeLimitRedis { - return new TimeLimitRedis("", 0, 1, getRedis()); + static $timelimit = null; + + if ($timelimit !== null) { + return $timelimit; + } + + return $timelimit = new TimeLimitRedis("", 0, 1, getRedis()); } } if (!function_exists('getRealtime')) { function getRealtime(): Realtime { - return new Realtime(); + static $realtime = null; + + if ($realtime !== null) { + return $realtime; + } + + return $realtime = new Realtime(); } } if (!function_exists('getTelemetry')) { function getTelemetry(int $workerId): Utopia\Telemetry\Adapter { - return new NoTelemetry(); + static $telemetry = null; + + if ($telemetry !== null) { + return $telemetry; + } + + return $telemetry = new NoTelemetry(); } } From c759f9ec11faafe7e744f294fc3493906e8de7c4 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 30 Apr 2025 23:24:54 +1200 Subject: [PATCH 35/39] Revert "Feat pool adapter" --- app/cli.php | 41 ++++-- app/controllers/api/health.php | 137 +++++++++--------- app/controllers/api/projects.php | 3 +- app/http.php | 21 ++- app/init/registers.php | 4 +- app/init/resources.php | 52 ++++--- app/realtime.php | 110 ++++++-------- app/worker.php | 69 ++++++--- composer.json | 4 +- composer.lock | 90 ++++++------ src/Appwrite/Event/Event.php | 7 + src/Appwrite/Messaging/Adapter/Realtime.php | 31 ++-- src/Appwrite/Platform/Tasks/Doctor.php | 50 +++---- src/Appwrite/Platform/Tasks/ScheduleBase.php | 9 +- .../Platform/Tasks/ScheduleExecutions.php | 12 +- .../Platform/Tasks/ScheduleFunctions.php | 9 +- .../Platform/Tasks/ScheduleMessages.php | 6 +- src/Appwrite/Platform/Workers/Deletes.php | 5 +- src/Appwrite/Platform/Workers/StatsUsage.php | 12 +- .../Platform/Workers/StatsUsageDump.php | 15 +- src/Appwrite/PubSub/Adapter/Pool.php | 46 ------ tests/resources/docker/docker-compose.yml | 32 +++- 22 files changed, 415 insertions(+), 350 deletions(-) delete mode 100644 src/Appwrite/PubSub/Adapter/Pool.php diff --git a/app/cli.php b/app/cli.php index 45361c2971..3a4fb844a1 100644 --- a/app/cli.php +++ b/app/cli.php @@ -10,13 +10,11 @@ use Appwrite\Event\StatsUsage; use Appwrite\Platform\Appwrite; use Appwrite\Runtimes\Runtimes; use Executor\Executor; -use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\CLI; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; @@ -24,7 +22,6 @@ use Utopia\DSN\DSN; use Utopia\Logger\Log; use Utopia\Platform\Service; use Utopia\Pools\Group; -use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Publisher; use Utopia\Registry\Registry; use Utopia\System\System; @@ -44,7 +41,10 @@ CLI::setResource('cache', function ($pools) { $adapters = []; foreach ($list as $value) { - $adapters[] = new CachePool($pools->get($value)); + $adapters[] = $pools + ->get($value) + ->pop() + ->getResource(); } return new Cache(new Sharding($adapters)); @@ -64,8 +64,12 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) { $attempts++; try { // Prepare database connection - $adapter = new DatabasePool($pools->get('console')); - $dbForPlatform = new Database($adapter, $cache); + $dbAdapter = $pools + ->get('console') + ->pop() + ->getResource(); + + $dbForPlatform = new Database($dbAdapter, $cache); $dbForPlatform ->setNamespace('_console') @@ -83,6 +87,7 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) { $ready = true; } catch (\Throwable $err) { Console::warning($err->getMessage()); + $pools->get('console')->reclaim(); sleep($sleep); } } while ($attempts < $maxAttempts && !$ready); @@ -132,8 +137,12 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform return $database; } - $adapter = new DatabasePool($pools->get($dsn->getHost())); - $database = new Database($adapter, $cache); + $dbAdapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); $databases[$dsn->getHost()] = $database; $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -159,15 +168,21 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database = null; - return function (?Document $project = null) use ($pools, $cache, $database) { if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') { $database->setTenant($project->getInternalId()); return $database; } - $adapter = new DatabasePool($pools->get('logs')); - $database = new Database($adapter, $cache); + $dbAdapter = $pools + ->get('logs') + ->pop() + ->getResource(); + + $database = new Database( + $dbAdapter, + $cache + ); $database ->setSharedTables(true) @@ -184,14 +199,14 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) { }; }, ['pools', 'cache']); -CLI::setResource('queueForStatsUsage', function (Publisher $publisher) { +CLI::setResource('queueForStatsUsage', function (Connection $publisher) { return new StatsUsage($publisher); }, ['publisher']); CLI::setResource('queueForStatsResources', function (Publisher $publisher) { return new StatsResources($publisher); }, ['publisher']); CLI::setResource('publisher', function (Group $pools) { - return new BrokerPool(publisher: $pools->get('publisher')); + return $pools->get('publisher')->pop()->getResource(); }, ['pools']); CLI::setResource('queueForFunctions', function (Publisher $publisher) { return new Func($publisher); diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 5fe2de5549..3602ab8ff9 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -3,16 +3,13 @@ use Appwrite\ClamAV\Network; use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\PubSub\Adapter\Pool as PubSubPool; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\App; -use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Document; use Utopia\Domains\Validator\PublicDomain; use Utopia\Pools\Group; @@ -37,8 +34,8 @@ App::get('/v1/health') namespace: 'health', group: 'health', name: 'get', - description: '/docs/references/health/get.md', auth: [AuthType::KEY], + description: '/docs/references/health/get.md', responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -73,11 +70,11 @@ App::get('/v1/health/db') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'health', name: 'getDB', description: '/docs/references/health/get-db.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -89,8 +86,8 @@ App::get('/v1/health/db') ->inject('response') ->inject('pools') ->action(function (Response $response, Group $pools) { + $output = []; - $failures = []; $configs = [ 'Console.DB' => Config::getParam('pools-console'), @@ -100,7 +97,7 @@ App::get('/v1/health/db') foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $adapter = new DatabasePool($pools->get($database)); + $adapter = $pools->get($database)->pop()->getResource(); $checkStart = \microtime(true); @@ -111,16 +108,16 @@ App::get('/v1/health/db') 'ping' => \round((\microtime(true) - $checkStart) / 1000) ]); } else { - $failures[] = $database; + $failure[] = $database; } - } catch (\Throwable) { - $failures[] = $database; + } catch (\Throwable $th) { + $failure[] = $database; } } } - if (!empty($failures)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'DB failure on: ' . implode(", ", $failures)); + if (!empty($failure)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'DB failure on: ' . implode(", ", $failure)); } $response->dynamic(new Document([ @@ -134,11 +131,11 @@ App::get('/v1/health/cache') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'health', name: 'getCache', description: '/docs/references/health/get-cache.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -150,39 +147,44 @@ App::get('/v1/health/cache') ->inject('response') ->inject('pools') ->action(function (Response $response, Group $pools) { + $output = []; - $failures = []; $configs = [ 'Cache' => Config::getParam('pools-cache'), ]; foreach ($configs as $key => $config) { - foreach ($config as $cache) { + foreach ($config as $database) { try { - $adapter = new CachePool($pools->get($cache)); + /** @var \Utopia\Cache\Adapter $adapter */ + $adapter = $pools->get($database)->pop()->getResource(); $checkStart = \microtime(true); if ($adapter->ping()) { $output[] = new Document([ - 'name' => $key . " ($cache)", + 'name' => $key . " ($database)", 'status' => 'pass', 'ping' => \round((\microtime(true) - $checkStart) / 1000) ]); } else { - $failures[] = $cache; + $output[] = new Document([ + 'name' => $key . " ($database)", + 'status' => 'fail', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); } - } catch (\Throwable) { - $failures[] = $cache; + } catch (\Throwable $th) { + $output[] = new Document([ + 'name' => $key . " ($database)", + 'status' => 'fail', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); } } } - if (!empty($failures)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Cache failure on: ' . implode(", ", $failures)); - } - $response->dynamic(new Document([ 'statuses' => $output, 'total' => count($output), @@ -194,11 +196,11 @@ App::get('/v1/health/pubsub') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'health', name: 'getPubSub', description: '/docs/references/health/get-pubsub.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -210,39 +212,44 @@ App::get('/v1/health/pubsub') ->inject('response') ->inject('pools') ->action(function (Response $response, Group $pools) { + $output = []; - $failures = []; $configs = [ 'PubSub' => Config::getParam('pools-pubsub'), ]; foreach ($configs as $key => $config) { - foreach ($config as $pubsub) { + foreach ($config as $database) { try { - $adapter = new PubSubPool($pools->get($pubsub)); + /** @var \Appwrite\PubSub\Adapter $adapter */ + $adapter = $pools->get($database)->pop()->getResource(); $checkStart = \microtime(true); if ($adapter->ping()) { $output[] = new Document([ - 'name' => $key . " ($pubsub)", + 'name' => $key . " ($database)", 'status' => 'pass', 'ping' => \round((\microtime(true) - $checkStart) / 1000) ]); } else { - $failures[] = $pubsub; + $output[] = new Document([ + 'name' => $key . " ($database)", + 'status' => 'fail', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); } - } catch (\Throwable) { - $failures[] = $pubsub; + } catch (\Throwable $th) { + $output[] = new Document([ + 'name' => $key . " ($database)", + 'status' => 'fail', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); } } } - if (!empty($failures)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Pubsub failure on: ' . implode(", ", $failures)); - } - $response->dynamic(new Document([ 'statuses' => $output, 'total' => count($output), @@ -254,11 +261,11 @@ App::get('/v1/health/time') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'health', name: 'getTime', description: '/docs/references/health/get-time.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -318,11 +325,11 @@ App::get('/v1/health/queue/webhooks') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueWebhooks', description: '/docs/references/health/get-queue-webhooks.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -344,18 +351,18 @@ App::get('/v1/health/queue/webhooks') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }); + }, ['response']); App::get('/v1/health/queue/logs') ->desc('Get logs queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueLogs', description: '/docs/references/health/get-queue-logs.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -377,18 +384,18 @@ App::get('/v1/health/queue/logs') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }); + }, ['response']); App::get('/v1/health/certificate') ->desc('Get the SSL certificate for a domain') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'health', name: 'getCertificate', description: '/docs/references/health/get-certificate.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -434,18 +441,18 @@ App::get('/v1/health/certificate') 'validTo' => $certificatePayload['validTo_time_t'], 'signatureTypeSN' => $certificatePayload['signatureTypeSN'], ]), Response::MODEL_HEALTH_CERTIFICATE); - }); + }, ['response']); App::get('/v1/health/queue/certificates') ->desc('Get certificates queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueCertificates', description: '/docs/references/health/get-queue-certificates.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -467,18 +474,18 @@ App::get('/v1/health/queue/certificates') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }); + }, ['response']); App::get('/v1/health/queue/builds') ->desc('Get builds queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueBuilds', description: '/docs/references/health/get-queue-builds.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -500,18 +507,18 @@ App::get('/v1/health/queue/builds') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }); + }, ['response']); App::get('/v1/health/queue/databases') ->desc('Get databases queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueDatabases', description: '/docs/references/health/get-queue-databases.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -534,18 +541,18 @@ App::get('/v1/health/queue/databases') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }); + }, ['response']); App::get('/v1/health/queue/deletes') ->desc('Get deletes queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueDeletes', description: '/docs/references/health/get-queue-deletes.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -567,18 +574,18 @@ App::get('/v1/health/queue/deletes') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }); + }, ['response']); App::get('/v1/health/queue/mails') ->desc('Get mails queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueMails', description: '/docs/references/health/get-queue-mails.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -600,18 +607,18 @@ App::get('/v1/health/queue/mails') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }); + }, ['response']); App::get('/v1/health/queue/messaging') ->desc('Get messaging queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueMessaging', description: '/docs/references/health/get-queue-messaging.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -633,18 +640,18 @@ App::get('/v1/health/queue/messaging') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }); + }, ['response']); App::get('/v1/health/queue/migrations') ->desc('Get migrations queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueMigrations', description: '/docs/references/health/get-queue-migrations.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -666,18 +673,18 @@ App::get('/v1/health/queue/migrations') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }); + }, ['response']); App::get('/v1/health/queue/functions') ->desc('Get functions queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueFunctions', description: '/docs/references/health/get-queue-functions.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -699,18 +706,18 @@ App::get('/v1/health/queue/functions') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }); + }, ['response']); App::get('/v1/health/queue/stats-resources') ->desc('Get stats resources queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueStatsResources', description: '/docs/references/health/get-queue-stats-resources.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -739,11 +746,11 @@ App::get('/v1/health/queue/stats-usage') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueUsage', description: '/docs/references/health/get-queue-stats-usage.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -772,11 +779,11 @@ App::get('/v1/health/storage/local') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'storage', name: 'getStorageLocal', description: '/docs/references/health/get-storage-local.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -822,11 +829,11 @@ App::get('/v1/health/storage') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'storage', name: 'getStorage', description: '/docs/references/health/get-storage.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -870,11 +877,11 @@ App::get('/v1/health/anti-virus') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'health', name: 'getAntivirus', description: '/docs/references/health/get-storage-anti-virus.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -916,11 +923,11 @@ App::get('/v1/health/queue/failed/:name') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( + auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getFailedJobs', description: '/docs/references/health/get-failed-queue-jobs.md', - auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index c4f0b6a9df..839a51a764 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -24,7 +24,6 @@ use Utopia\App; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -224,7 +223,7 @@ App::post('/v1/projects') $sharedTables = $sharedTablesV1 || $sharedTablesV2; if (!$sharedTablesV2) { - $adapter = new DatabasePool($pools->get($dsn->getHost())); + $adapter = $pools->get($dsn->getHost())->pop()->getResource(); $dbForProject = new Database($adapter, $cache); if ($sharedTables) { diff --git a/app/http.php b/app/http.php index 4d78837a8a..4fc6ea6825 100644 --- a/app/http.php +++ b/app/http.php @@ -14,11 +14,9 @@ use Utopia\App; use Utopia\Audit\Audit; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; -use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -169,7 +167,7 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co $sleep = 1; $attempts = 0; - while (true) { + do { try { $attempts++; $resource = $app->getResource($resourceKey); @@ -178,12 +176,13 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co break; // exit loop on success } catch (\Exception $e) { Console::warning(" └── Database not ready. Retrying connection ({$attempts})..."); + $pools->reclaim(); if ($attempts >= $max) { throw new \Exception(' └── Failed to connect to database: ' . $e->getMessage()); } sleep($sleep); } - } + } while ($attempts < $max); Console::success("[Setup] - $dbName database init started..."); @@ -319,7 +318,11 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $cache = $app->getResource('cache'); foreach ($sharedTablesV2 as $hostname) { - $adapter = new DatabasePool($pools->get($hostname)); + $adapter = $pools + ->get($hostname) + ->pop() + ->getResource(); + $dbForProject = (new Database($adapter, $cache)) ->setDatabase('appwrite') ->setSharedTables(true) @@ -329,7 +332,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg try { Console::success('[Setup] - Creating project database: ' . $hostname . '...'); $dbForProject->create(); - } catch (DuplicateException) { + } catch (Duplicate) { Console::success('[Setup] - Skip: metadata table already exists'); } @@ -355,6 +358,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg } } + $pools->reclaim(); Console::success('[Setup] - Server database init completed...'); }); @@ -469,7 +473,6 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool Console::error('[Error] Message: ' . $th->getMessage()); Console::error('[Error] File: ' . $th->getFile()); Console::error('[Error] Line: ' . $th->getLine()); - Console::error('[Error] Trace: ' . $th->getTraceAsString()); $swooleResponse->setStatusCode(500); @@ -487,11 +490,13 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool ]; $swooleResponse->end(\json_encode($output)); + } finally { + $pools->reclaim(); } }); // Fetch domains every `DOMAIN_SYNC_TIMER` seconds and update in the memory -$http->on(Constant::EVENT_TASK, function () use ($register, $domains) { +$http->on('Task', function () use ($register, $domains) { $lastSyncUpdate = null; $pools = $register->get('pools'); App::setResource('pools', fn () => $pools); diff --git a/app/init/registers.php b/app/init/registers.php index 14d177426c..1ebcbc1691 100644 --- a/app/init/registers.php +++ b/app/init/registers.php @@ -215,13 +215,13 @@ $register->set('pools', function () { 'mysql', 'mariadb' => 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, [ + return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( PDO::ATTR_TIMEOUT => 3, // Seconds PDO::ATTR_PERSISTENT => false, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => true, PDO::ATTR_STRINGIFY_FETCHES => true - ]); + )); }); }, 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { diff --git a/app/init/resources.php b/app/init/resources.php index f48efbe177..c719a47344 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -24,12 +24,10 @@ use Appwrite\Utopia\Request; use Executor\Executor; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; use Utopia\App; -use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; @@ -39,7 +37,6 @@ use Utopia\DSN\DSN; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Pools\Group; -use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Publisher; use Utopia\Storage\Device; use Utopia\Storage\Device\AWS; @@ -75,10 +72,10 @@ App::setResource('localeCodes', function () { // Queues App::setResource('publisher', function (Group $pools) { - return new BrokerPool(publisher: $pools->get('publisher')); + return $pools->get('publisher')->pop()->getResource(); }, ['pools']); App::setResource('consumer', function (Group $pools) { - return new BrokerPool(consumer: $pools->get('consumer')); + return $pools->get('consumer')->pop()->getResource(); }, ['pools']); App::setResource('queueForMessaging', function (Publisher $publisher) { return new Messaging($publisher); @@ -332,8 +329,12 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $adapter = new DatabasePool($pools->get($dsn->getHost())); - $database = new Database($adapter, $cache); + $dbAdapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); $database ->setMetadata('host', \gethostname()) @@ -359,8 +360,12 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform }, ['pools', 'dbForPlatform', 'cache', 'project']); App::setResource('dbForPlatform', function (Group $pools, Cache $cache) { - $adapter = new DatabasePool($pools->get('console')); - $database = new Database($adapter, $cache); + $dbAdapter = $pools + ->get('console') + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); $database ->setNamespace('_console') @@ -373,7 +378,7 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache) { }, ['pools', 'cache']); App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) { - $databases = []; + $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) { if ($project->isEmpty() || $project->getId() === 'console') { @@ -415,8 +420,12 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform return $database; } - $adapter = new DatabasePool($pools->get($dsn->getHost())); - $database = new Database($adapter, $cache); + $dbAdapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); $databases[$dsn->getHost()] = $database; $configure($database); @@ -426,15 +435,21 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform App::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database = null; - - return function (?Document $project = null) use ($pools, $cache, &$database) { + return function (?Document $project = null) use ($pools, $cache, $database) { if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') { $database->setTenant($project->getInternalId()); return $database; } - $adapter = new DatabasePool($pools->get('logs')); - $database = new Database($adapter, $cache); + $dbAdapter = $pools + ->get('logs') + ->pop() + ->getResource(); + + $database = new Database( + $dbAdapter, + $cache + ); $database ->setSharedTables(true) @@ -458,7 +473,10 @@ App::setResource('cache', function (Group $pools, Telemetry $telemetry) { $adapters = []; foreach ($list as $value) { - $adapters[] = new CachePool($pools->get($value)); + $adapters[] = $pools + ->get($value) + ->pop() + ->getResource(); } $cache = new Cache(new Sharding($adapters)); diff --git a/app/realtime.php b/app/realtime.php index 7e6fc0e311..86f9c85fdd 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -5,7 +5,6 @@ use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Network\Validator\Origin; -use Appwrite\PubSub\Adapter\Pool as PubSubPool; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Swoole\Http\Request as SwooleRequest; @@ -16,12 +15,10 @@ use Swoole\Timer; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; use Utopia\App; -use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -31,15 +28,13 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\DSN\DSN; use Utopia\Logger\Log; -use Utopia\Pools\Group; -use Utopia\Registry\Registry; use Utopia\System\System; use Utopia\Telemetry\Adapter\None as NoTelemetry; use Utopia\WebSocket\Adapter; use Utopia\WebSocket\Server; /** - * @var Registry $register + * @var \Utopia\Registry\Registry $register */ require_once __DIR__ . '/init.php'; @@ -51,17 +46,17 @@ if (!function_exists('getConsoleDB')) { { global $register; - static $database = null; - - if ($database !== null) { - return $database; - } - - /** @var Group $pools */ + /** @var \Utopia\Pools\Group $pools */ $pools = $register->get('pools'); - $adapter = new DatabasePool($pools->get('console')); - $database = new Database($adapter, getCache()); + $dbAdapter = $pools + ->get('console') + ->pop() + ->getResource() + ; + + $database = new Database($dbAdapter, getCache()); + $database ->setNamespace('_console') ->setMetadata('host', \gethostname()) @@ -77,13 +72,7 @@ if (!function_exists('getProjectDB')) { { global $register; - static $databases = []; - - if (isset($databases[$project->getInternalId()])) { - return $databases[$project->getInternalId()]; - } - - /** @var Group $pools */ + /** @var \Utopia\Pools\Group $pools */ $pools = $register->get('pools'); if ($project->isEmpty() || $project->getId() === 'console') { @@ -97,7 +86,11 @@ if (!function_exists('getProjectDB')) { $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $adapter = new DatabasePool($pools->get($dsn->getHost())); + $adapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + $database = new Database($adapter, getCache()); $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -118,7 +111,7 @@ if (!function_exists('getProjectDB')) { ->setMetadata('host', \gethostname()) ->setMetadata('project', $project->getId()); - return $databases[$project->getInternalId()] = $database; + return $database; } } @@ -128,22 +121,20 @@ if (!function_exists('getCache')) { { global $register; - static $cache = null; - - if ($cache !== null) { - return $cache; - } - - $pools = $register->get('pools'); /** @var Group $pools */ + $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ $list = Config::getParam('pools-cache', []); $adapters = []; foreach ($list as $value) { - $adapters[] = new CachePool($pools->get($value)); + $adapters[] = $pools + ->get($value) + ->pop() + ->getResource() + ; } - return $cache = new Cache(new Sharding($adapters)); + return new Cache(new Sharding($adapters)); } } @@ -151,12 +142,6 @@ if (!function_exists('getCache')) { if (!function_exists('getRedis')) { function getRedis(): \Redis { - static $redis = null; - - if ($redis !== null) { - return $redis; - } - $host = System::getEnv('_APP_REDIS_HOST', 'localhost'); $port = System::getEnv('_APP_REDIS_PORT', 6379); $pass = System::getEnv('_APP_REDIS_PASS', ''); @@ -175,39 +160,21 @@ if (!function_exists('getRedis')) { if (!function_exists('getTimelimit')) { function getTimelimit(): TimeLimitRedis { - static $timelimit = null; - - if ($timelimit !== null) { - return $timelimit; - } - - return $timelimit = new TimeLimitRedis("", 0, 1, getRedis()); + return new TimeLimitRedis("", 0, 1, getRedis()); } } if (!function_exists('getRealtime')) { function getRealtime(): Realtime { - static $realtime = null; - - if ($realtime !== null) { - return $realtime; - } - - return $realtime = new Realtime(); + return new Realtime(); } } if (!function_exists('getTelemetry')) { function getTelemetry(int $workerId): Utopia\Telemetry\Adapter { - static $telemetry = null; - - if ($telemetry !== null) { - return $telemetry; - } - - return $telemetry = new NoTelemetry(); + return new NoTelemetry(); } } @@ -306,6 +273,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume sleep(DATABASE_RECONNECT_SLEEP); } } while (true); + $register->get('pools')->reclaim(); }); /** @@ -331,7 +299,9 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume Authorization::skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); } catch (Throwable $th) { - $logError($th, "updateWorkerDocument"); + call_user_func($logError, $th, "updateWorkerDocument"); + } finally { + $register->get('pools')->reclaim(); } }); } @@ -400,6 +370,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, 'data' => $event['data'] ])); } + + $register->get('pools')->reclaim(); } } /** @@ -435,8 +407,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, } $start = time(); - $pubsub = new PubSubPool($register->get('pools')->get('pubsub')); - + /** @var \Appwrite\PubSub\Adapter $pubsub */ + $pubsub = $register->get('pools')->get('pubsub')->pop()->getResource(); if ($pubsub->ping(true)) { $attempts = 0; Console::success('Pub/sub connection established (worker: ' . $workerId . ')'); @@ -464,6 +436,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime->unsubscribe($connection); $realtime->subscribe($projectId, $connection, $roles, $channels); + + $register->get('pools')->reclaim(); } } @@ -489,12 +463,14 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, } }); } catch (Throwable $th) { - $logError($th, "pubSubConnection"); + call_user_func($logError, $th, "pubSubConnection"); Console::error('Pub/sub error: ' . $th->getMessage()); $attempts++; sleep(DATABASE_RECONNECT_SLEEP); continue; + } finally { + $register->get('pools')->reclaim(); } } @@ -596,7 +572,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $stats->incr($project->getId(), 'connections'); $stats->incr($project->getId(), 'connectionsTotal'); } catch (Throwable $th) { - $logError($th, "initServer"); + call_user_func($logError, $th, "initServer"); // Handle SQL error code is 'HY000' $code = $th->getCode(); @@ -620,6 +596,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::error('[Error] Code: ' . $response['data']['code']); Console::error('[Error] Message: ' . $response['data']['message']); } + } finally { + $register->get('pools')->reclaim(); } }); @@ -718,6 +696,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re if ($th->getCode() === 1008) { $server->close($connection, $th->getCode()); } + } finally { + $register->get('pools')->reclaim(); } }); diff --git a/app/worker.php b/app/worker.php index 1ae2108a62..232e0b3684 100644 --- a/app/worker.php +++ b/app/worker.php @@ -20,12 +20,10 @@ use Appwrite\Platform\Appwrite; use Executor\Executor; use Swoole\Runtime; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; -use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -35,7 +33,6 @@ use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Platform\Service; use Utopia\Pools\Group; -use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Message; use Utopia\Queue\Publisher; use Utopia\Queue\Server; @@ -43,17 +40,21 @@ use Utopia\Registry\Registry; use Utopia\System\System; Authorization::disable(); -Runtime::enableCoroutine(); +Runtime::enableCoroutine(SWOOLE_HOOK_ALL); Server::setResource('register', fn () => $register); Server::setResource('dbForPlatform', function (Cache $cache, Registry $register) { $pools = $register->get('pools'); - $adapter = new DatabasePool($pools->get('console')); - $dbForPlatform = new Database($adapter, $cache); - $dbForPlatform->setNamespace('_console'); + $database = $pools + ->get('console') + ->pop() + ->getResource(); - return $dbForPlatform; + $adapter = new Database($database, $cache); + $adapter->setNamespace('_console'); + + return $adapter; }, ['cache', 'register']); Server::setResource('project', function (Message $message, Database $dbForPlatform) { @@ -81,9 +82,20 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $adapter = new DatabasePool($pools->get($dsn->getHost())); + $adapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + $database = new Database($adapter, $cache); + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); if (\in_array($dsn->getHost(), $sharedTables)) { @@ -138,8 +150,12 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf return $database; } - $adapter = new DatabasePool($pools->get($dsn->getHost())); - $database = new Database($adapter, $cache); + $dbAdapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); $databases[$dsn->getHost()] = $database; @@ -171,8 +187,15 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) { return $database; } - $adapter = new DatabasePool($pools->get('logs')); - $database = new Database($adapter, $cache); + $dbAdapter = $pools + ->get('logs') + ->pop() + ->getResource(); + + $database = new Database( + $dbAdapter, + $cache + ); $database ->setSharedTables(true) @@ -210,7 +233,11 @@ Server::setResource('cache', function (Registry $register) { $adapters = []; foreach ($list as $value) { - $adapters[] = new CachePool($pools->get($value)); + $adapters[] = $pools + ->get($value) + ->pop() + ->getResource() + ; } return new Cache(new Sharding($adapters)); @@ -240,11 +267,11 @@ Server::setResource('timelimit', function (\Redis $redis) { Server::setResource('log', fn () => new Log()); Server::setResource('publisher', function (Group $pools) { - return new BrokerPool(publisher: $pools->get('publisher')); + return $pools->get('publisher')->pop()->getResource(); }, ['pools']); Server::setResource('consumer', function (Group $pools) { - return new BrokerPool(consumer: $pools->get('consumer')); + return $pools->get('consumer')->pop()->getResource(); }, ['pools']); Server::setResource('queueForStatsUsage', function (Publisher $publisher) { @@ -421,6 +448,13 @@ try { $worker = $platform->getWorker(); +$worker + ->shutdown() + ->inject('pools') + ->action(function (Group $pools) { + $pools->reclaim(); + }); + $worker ->error() ->inject('error') @@ -428,7 +462,8 @@ $worker ->inject('log') ->inject('pools') ->inject('project') - ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($worker, $queueName) { + ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($queueName) { + $pools->reclaim(); $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); if ($logger) { diff --git a/composer.json b/composer.json index 816f1cbe36..46a2679d65 100644 --- a/composer.json +++ b/composer.json @@ -48,7 +48,7 @@ "utopia-php/abuse": "0.52.*", "utopia-php/analytics": "0.10.*", "utopia-php/audit": "0.55.*", - "utopia-php/cache": "0.13.*", + "utopia-php/cache": "0.12.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", "utopia-php/database": "0.66.*", @@ -65,7 +65,7 @@ "utopia-php/platform": "0.7.*", "utopia-php/pools": "0.8.*", "utopia-php/preloader": "0.2.*", - "utopia-php/queue": "0.10.*", + "utopia-php/queue": "0.9.*", "utopia-php/registry": "0.5.*", "utopia-php/storage": "0.18.*", "utopia-php/swoole": "0.8.*", diff --git a/composer.lock b/composer.lock index 573c822296..5a0fea3b26 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": "43dc7ad0d25793a58604b9d4d40abff4", + "content-hash": "f8a8967327763c5b85721583bd78c1de", "packages": [ { "name": "adhocore/jwt", @@ -3300,16 +3300,16 @@ }, { "name": "utopia-php/cache", - "version": "0.13.0", + "version": "0.12.0", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "dee01dec33a211644d60f6cfa56b1b8176d3fae3" + "reference": "646038f1d470b759c129348be8fc14da3c00bbd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/dee01dec33a211644d60f6cfa56b1b8176d3fae3", - "reference": "dee01dec33a211644d60f6cfa56b1b8176d3fae3", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/646038f1d470b759c129348be8fc14da3c00bbd9", + "reference": "646038f1d470b759c129348be8fc14da3c00bbd9", "shasum": "" }, "require": { @@ -3317,7 +3317,6 @@ "ext-memcached": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/pools": "0.8.*", "utopia-php/telemetry": "0.1.*" }, "require-dev": { @@ -3346,9 +3345,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.13.0" + "source": "https://github.com/utopia-php/cache/tree/0.12.0" }, - "time": "2025-04-17T04:20:26+00:00" + "time": "2025-02-25T09:09:21+00:00" }, { "name": "utopia-php/cli", @@ -3498,23 +3497,23 @@ }, { "name": "utopia-php/database", - "version": "0.66.2", + "version": "0.66.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "944d1979c4ba572c746cad957fe34aead338ee34" + "reference": "67d2ab418efba31dc76b3564cf043e2b3f98d027" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/944d1979c4ba572c746cad957fe34aead338ee34", - "reference": "944d1979c4ba572c746cad957fe34aead338ee34", + "url": "https://api.github.com/repos/utopia-php/database/zipball/67d2ab418efba31dc76b3564cf043e2b3f98d027", + "reference": "67d2ab418efba31dc76b3564cf043e2b3f98d027", "shasum": "" }, "require": { "ext-mbstring": "*", "ext-pdo": "*", "php": ">=8.1", - "utopia-php/cache": "0.13.*", + "utopia-php/cache": "0.12.*", "utopia-php/framework": "0.33.*", "utopia-php/pools": "0.8.*" }, @@ -3548,9 +3547,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.66.2" + "source": "https://github.com/utopia-php/database/tree/0.66.0" }, - "time": "2025-04-29T23:35:39+00:00" + "time": "2025-04-16T07:10:27+00:00" }, { "name": "utopia-php/domains", @@ -4058,16 +4057,16 @@ }, { "name": "utopia-php/platform", - "version": "0.7.5", + "version": "0.7.4", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "8febd7b6e0c0f2cbd2f4289447bcca97496e4aaf" + "reference": "a5b93d8177702ec458c3af9137663133c012b71b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/8febd7b6e0c0f2cbd2f4289447bcca97496e4aaf", - "reference": "8febd7b6e0c0f2cbd2f4289447bcca97496e4aaf", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/a5b93d8177702ec458c3af9137663133c012b71b", + "reference": "a5b93d8177702ec458c3af9137663133c012b71b", "shasum": "" }, "require": { @@ -4076,11 +4075,11 @@ "php": ">=8.0", "utopia-php/cli": "0.15.*", "utopia-php/framework": "0.33.*", - "utopia-php/queue": "0.10.*" + "utopia-php/queue": "0.9.*" }, "require-dev": { - "laravel/pint": "1.*", - "phpunit/phpunit": "9.*" + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" }, "type": "library", "autoload": { @@ -4102,9 +4101,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.7.5" + "source": "https://github.com/utopia-php/platform/tree/0.7.4" }, - "time": "2025-04-17T12:20:16+00:00" + "time": "2025-03-13T13:00:12+00:00" }, { "name": "utopia-php/pools", @@ -4213,16 +4212,16 @@ }, { "name": "utopia-php/queue", - "version": "0.10.0", + "version": "0.9.1", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "0eccc559168ea72241c39a4c482d868314666be1" + "reference": "32b6f84c55aae761db5a5ae76cc91ca8dbc8bc32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/0eccc559168ea72241c39a4c482d868314666be1", - "reference": "0eccc559168ea72241c39a4c482d868314666be1", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/32b6f84c55aae761db5a5ae76cc91ca8dbc8bc32", + "reference": "32b6f84c55aae761db5a5ae76cc91ca8dbc8bc32", "shasum": "" }, "require": { @@ -4231,7 +4230,6 @@ "utopia-php/cli": "0.15.*", "utopia-php/fetch": "0.4.*", "utopia-php/framework": "0.33.*", - "utopia-php/pools": "0.8.*", "utopia-php/telemetry": "0.1.*" }, "require-dev": { @@ -4273,9 +4271,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.10.0" + "source": "https://github.com/utopia-php/queue/tree/0.9.1" }, - "time": "2025-04-17T12:15:52+00:00" + "time": "2025-03-28T19:49:36+00:00" }, { "name": "utopia-php/registry", @@ -4545,28 +4543,28 @@ }, { "name": "utopia-php/vcs", - "version": "0.9.5", + "version": "0.9.4", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "055956545ca7ab4e8688df5de1df3e2833859793" + "reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/055956545ca7ab4e8688df5de1df3e2833859793", - "reference": "055956545ca7ab4e8688df5de1df3e2833859793", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/1a8d280b176acc99ea8d9e7364b8767cbb206b4a", + "reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a", "shasum": "" }, "require": { "adhocore/jwt": "^1.1", "php": ">=8.0", - "utopia-php/cache": "0.13.*", + "utopia-php/cache": "0.12.*", "utopia-php/framework": "0.*.*", "utopia-php/system": "0.9.*" }, "require-dev": { - "laravel/pint": "1.*.*", - "phpstan/phpstan": "1.*.*", + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", "phpunit/phpunit": "^9.4" }, "type": "library", @@ -4589,9 +4587,9 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.9.5" + "source": "https://github.com/utopia-php/vcs/tree/0.9.4" }, - "time": "2025-04-17T04:38:49+00:00" + "time": "2025-03-13T10:09:45+00:00" }, { "name": "utopia-php/websocket", @@ -5233,16 +5231,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.1", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" + "reference": "024473a478be9df5fdaca2c793f2232fe788e414" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414", "shasum": "" }, "require": { @@ -5281,7 +5279,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" }, "funding": [ { @@ -5289,7 +5287,7 @@ "type": "tidelift" } ], - "time": "2025-04-29T12:36:36+00:00" + "time": "2025-02-12T12:17:51+00:00" }, { "name": "nikic/php-parser", diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 2c735ef2d4..d699a45417 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -286,6 +286,13 @@ class Event return $this; } + public function setParamSensitive(string $key): self + { + $this->sensitive[$key] = true; + + return $this; + } + /** * Get param of event. * diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 18b0d94e7d..1963bdedd6 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -2,14 +2,14 @@ namespace Appwrite\Messaging\Adapter; -use Appwrite\Messaging\Adapter as MessagingAdapter; -use Appwrite\PubSub\Adapter\Pool as PubSubPool; +use Appwrite\Messaging\Adapter; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; +use Utopia\Pools\Pool; -class Realtime extends MessagingAdapter +class Realtime extends Adapter { /** * Connection Tree @@ -36,12 +36,12 @@ class Realtime extends MessagingAdapter */ public array $subscriptions = []; - private PubSubPool $redis; + private Pool $pubsubPool; public function __construct() { global $register; - $this->redis = new PubSubPool($register->get('pools')->get('pubsub')); + $this->pubsubPool = $register->get('pools')->get('pubsub'); } /** @@ -132,12 +132,11 @@ class Realtime extends MessagingAdapter * Sends an event to the Realtime Server * @param string $projectId * @param array $payload - * @param array $events + * @param string $event * @param array $channels * @param array $roles * @param array $options * @return void - * @throws \Exception */ public function send(string $projectId, array $payload, array $events, array $channels, array $roles, array $options = []): void { @@ -148,7 +147,7 @@ class Realtime extends MessagingAdapter $permissionsChanged = array_key_exists('permissionsChanged', $options) && $options['permissionsChanged']; $userId = array_key_exists('userId', $options) ? $options['userId'] : null; - $this->redis->publish('realtime', json_encode([ + $message = [ 'project' => $projectId, 'roles' => $roles, 'permissionsChanged' => $permissionsChanged, @@ -159,7 +158,9 @@ class Realtime extends MessagingAdapter 'timestamp' => DateTime::formatTz(DateTime::now()), 'payload' => $payload ] - ])); + ]; + + $this->pubsubPool->use(fn (\Appwrite\PubSub\Adapter $pubsub) => $pubsub->publish('realtime', json_encode($message))); } /** @@ -174,9 +175,8 @@ class Realtime extends MessagingAdapter * - 1,121.328 ms (±0.84%) | 1,000,000 Connections / 10,000,000 Subscriptions * * @param array $event - * @return int[]|string[] */ - public function getSubscribers(array $event): array + public function getSubscribers(array $event) { $receivers = []; @@ -230,7 +230,7 @@ class Realtime extends MessagingAdapter foreach ($channels as $key => $value) { switch (true) { - case \str_starts_with($key, 'account.'): + case strpos($key, 'account.') === 0: unset($channels[$key]); break; @@ -272,7 +272,6 @@ class Realtime extends MessagingAdapter $channels[] = 'account.' . $parts[1]; $roles = [Role::user(ID::custom($parts[1]))->toString()]; break; - case 'migrations': case 'rules': $channels[] = 'console'; $channels[] = 'projects.' . $project->getId(); @@ -353,6 +352,12 @@ class Realtime extends MessagingAdapter $roles = [Role::team($project->getAttribute('teamId'))->toString()]; } + break; + case 'migrations': + $channels[] = 'console'; + $channels[] = 'projects.' . $project->getId(); + $projectId = 'console'; + $roles = [Role::team($project->getAttribute('teamId'))->toString()]; break; } diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index 5263133eba..c43afea527 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -3,19 +3,14 @@ namespace Appwrite\Platform\Tasks; use Appwrite\ClamAV\Network; -use Appwrite\PubSub\Adapter\Pool as PubSubPool; -use PHPMailer\PHPMailer\PHPMailer; +use Appwrite\PubSub\Adapter; use Utopia\App; -use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Domains\Domain; use Utopia\DSN\DSN; use Utopia\Logger\Logger; use Utopia\Platform\Action; -use Utopia\Pools\Group; -use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Registry\Registry; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; @@ -81,9 +76,9 @@ class Doctor extends Action Console::log('🟢 Abuse protection is enabled'); } - $authWhitelistRoot = System::getEnv('_APP_CONSOLE_WHITELIST_ROOT'); - $authWhitelistEmails = System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS'); - $authWhitelistIPs = System::getEnv('_APP_CONSOLE_WHITELIST_IPS'); + $authWhitelistRoot = System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', null); + $authWhitelistEmails = System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null); + $authWhitelistIPs = System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null); if ( empty($authWhitelistRoot) @@ -119,16 +114,19 @@ class Doctor extends Action } else { Console::log('🟢 Logging adapter is enabled (' . $providerName . ')'); } - } catch (\Throwable) { + } catch (\Throwable $th) { Console::log('🔴 Logging adapter is misconfigured'); } \usleep(200 * 1000); // Sleep for 0.2 seconds - Console::log("\n" . '[Connectivity]'); + try { + Console::log("\n" . '[Connectivity]'); + } catch (\Throwable $th) { + //throw $th; + } - /** @var Group $pools */ - $pools = $register->get('pools'); + $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ $configs = [ 'Console.DB' => Config::getParam('pools-console'), @@ -138,22 +136,20 @@ class Doctor extends Action foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $adapter = new DatabasePool($pools->get($database)); + $adapter = $pools->get($database)->pop()->getResource(); if ($adapter->ping()) { Console::success('🟢 ' . str_pad("{$key}({$database})", 50, '.') . 'connected'); } else { Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected'); } - } catch (\Throwable) { + } catch (\Throwable $th) { Console::error('🔴 ' . str_pad("{$key}.({$database})", 47, '.') . 'disconnected'); } } } - /** @var Group $pools */ - $pools = $register->get('pools'); - + $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ $configs = [ 'Cache' => Config::getParam('pools-cache'), 'Queue' => Config::getParam('pools-queue'), @@ -163,18 +159,15 @@ class Doctor extends Action foreach ($configs as $key => $config) { foreach ($config as $pool) { try { - $adapter = match($key) { - 'Cache' => new CachePool($pools->get($pool)), - 'Queue' => new BrokerPool($pools->get($pool)), - 'PubSub' => new PubSubPool($pools->get($pool)), - }; + /** @var Adapter $adapter */ + $adapter = $pools->get($pool)->pop()->getResource(); if ($adapter->ping()) { Console::success('🟢 ' . str_pad("{$key}({$pool})", 50, '.') . 'connected'); } else { Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); } - } catch (\Throwable) { + } catch (\Throwable $th) { Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); } } @@ -192,14 +185,13 @@ class Doctor extends Action } else { Console::error('🔴 ' . str_pad("Antivirus", 47, '.') . 'disconnected'); } - } catch (\Throwable) { + } catch (\Throwable $th) { Console::error('🔴 ' . str_pad("Antivirus", 47, '.') . 'disconnected'); } } try { - /* @var PHPMailer $mail */ - $mail = $register->get('smtp'); + $mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */ $mail->addAddress('demo@example.com', 'Example.com'); $mail->Subject = 'Test SMTP Connection'; @@ -208,7 +200,7 @@ class Doctor extends Action $mail->send(); Console::success('🟢 ' . str_pad("SMTP", 50, '.') . 'connected'); - } catch (\Throwable) { + } catch (\Throwable $th) { Console::error('🔴 ' . str_pad("SMTP", 47, '.') . 'disconnected'); } @@ -282,7 +274,7 @@ class Doctor extends Action Console::error('Failed to check for a newer version' . "\n"); } } - } catch (\Throwable) { + } catch (\Throwable $th) { Console::error('Failed to check for a newer version' . "\n"); } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index 7b78826491..a3c36cb96e 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -12,7 +12,6 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; use Utopia\Pools\Group; -use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\System\System; use function Swoole\Coroutine\run; @@ -24,8 +23,6 @@ abstract class ScheduleBase extends Action protected array $schedules = []; - protected BrokerPool $publisher; - abstract public static function getName(): string; abstract public static function getSupportedResource(): string; abstract public static function getCollectionId(): string; @@ -64,8 +61,6 @@ abstract class ScheduleBase extends Action Console::title(\ucfirst(static::getSupportedResource()) . ' scheduler V1'); Console::success(APP_NAME . ' ' . \ucfirst(static::getSupportedResource()) . ' scheduler v1 has started'); - $this->publisher = new BrokerPool($pools->get('publisher')); - /** * Extract only necessary attributes to lower memory used. * @@ -137,6 +132,8 @@ abstract class ScheduleBase extends Action $latestDocument = \end($results); } + $pools->reclaim(); + Console::success("{$total} resources were loaded in " . (\microtime(true) - $loadStart) . " seconds"); Console::success("Starting timers at " . DateTime::now()); @@ -201,6 +198,8 @@ abstract class ScheduleBase extends Action $lastSyncUpdate = $time; $timerEnd = \microtime(true); + $pools->reclaim(); + Console::log("Sync tick: {$total} schedules were updated in " . ($timerEnd - $timerStart) . " seconds"); }); diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index f961037b2e..7cd76b480d 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Func; +use Swoole\Coroutine as Co; use Utopia\Database\Database; use Utopia\Pools\Group; @@ -28,6 +29,9 @@ class ScheduleExecutions extends ScheduleBase protected function enqueueResources(Group $pools, Database $dbForPlatform, callable $getProjectDB): void { + $queue = $pools->get('publisher')->pop(); + $connection = $queue->getResource(); + $queueForFunctions = new Func($connection); $intervalEnd = (new \DateTime())->modify('+' . self::ENQUEUE_TIMER . ' seconds'); foreach ($this->schedules as $schedule) { @@ -55,10 +59,8 @@ class ScheduleExecutions extends ScheduleBase $this->updateProjectAccess($schedule['project'], $dbForPlatform); - \go(function () use ($schedule, $delay, $data, $pools) { - \Co::sleep($delay); - - $queueForFunctions = new Func($this->publisher); + \go(function () use ($queueForFunctions, $schedule, $delay, $data) { + Co::sleep($delay); $queueForFunctions->setType('schedule') // Set functionId instead of function as we don't have $dbForProject @@ -81,5 +83,7 @@ class ScheduleExecutions extends ScheduleBase unset($this->schedules[$schedule['$internalId']]); } + + $queue->reclaim(); } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index f7a2f603e6..5b8e3027a7 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -71,7 +71,10 @@ class ScheduleFunctions extends ScheduleBase foreach ($delayedExecutions as $delay => $scheduleKeys) { \go(function () use ($delay, $scheduleKeys, $pools, $dbForPlatform) { - \Co::sleep($delay); // in seconds + \sleep($delay); // in seconds + + $queue = $pools->get('publisher')->pop(); + $connection = $queue->getResource(); foreach ($scheduleKeys as $scheduleKey) { // Ensure schedule was not deleted @@ -83,7 +86,7 @@ class ScheduleFunctions extends ScheduleBase $this->updateProjectAccess($schedule['project'], $dbForPlatform); - $queueForFunctions = new Func($this->publisher); + $queueForFunctions = new Func($connection); $queueForFunctions ->setType('schedule') @@ -93,6 +96,8 @@ class ScheduleFunctions extends ScheduleBase ->setProject($schedule['project']) ->trigger(); } + + $queue->reclaim(); }); } diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 6c24f81026..201d5eab53 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -41,7 +41,9 @@ class ScheduleMessages extends ScheduleBase } \go(function () use ($schedule, $pools, $dbForPlatform) { - $queueForMessaging = new Messaging($this->publisher); + $queue = $pools->get('publisher')->pop(); + $connection = $queue->getResource(); + $queueForMessaging = new Messaging($connection); $this->updateProjectAccess($schedule['project'], $dbForPlatform); @@ -56,6 +58,8 @@ class ScheduleMessages extends ScheduleBase $schedule['$id'], ); + $queue->reclaim(); + unset($this->schedules[$schedule['$internalId']]); }); } diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 427772a6e0..a61db63de6 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -61,7 +61,10 @@ class Deletes extends Action ->inject('executionRetention') ->inject('auditRetention') ->inject('log') - ->callback([$this, 'action']); + ->callback( + fn ($message, Document $project, Database $dbForPlatform, callable $getProjectDB, callable $getLogsDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, CertificatesAdapter $certificates, Executor $executor, string $executionRetention, string $auditRetention, Log $log) => + $this->action($message, $project, $dbForPlatform, $getProjectDB, $getLogsDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $certificates, $executor, $executionRetention, $auditRetention, $log) + ); } /** diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index 60fab5d2ea..66f285fcf5 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -325,7 +325,7 @@ class StatsUsage extends Action break; } } catch (Throwable $e) { - Console::error("[reducer] " . " {DateTime::now()} " . " {$project->getInternalId()} " . " {$e->getMessage()}"); + console::error("[reducer] " . " {DateTime::now()} " . " {$project->getInternalId()} " . " {$e->getMessage()}"); } } @@ -344,7 +344,7 @@ class StatsUsage extends Action continue; } - Console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); + console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); try { foreach ($stats['keys'] ?? [] as $key => $value) { @@ -381,7 +381,7 @@ class StatsUsage extends Action } } } catch (Exception $e) { - Console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); + console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); } } @@ -405,7 +405,7 @@ class StatsUsage extends Action } - protected function prepareForLogsDB(Document $project, Document $stat): void + protected function prepareForLogsDB(Document $project, Document $stat) { if (System::getEnv('_APP_STATS_USAGE_DUAL_WRITING', 'disabled') === 'disabled') { return; @@ -430,7 +430,8 @@ class StatsUsage extends Action return; } - $dbForLogs = ($this->getLogsDB)() + $dbForLogs = call_user_func($this->getLogsDB); + $dbForLogs ->setTenant(null) ->setTenantPerDocument(true); @@ -445,5 +446,6 @@ class StatsUsage extends Action } catch (Throwable $th) { Console::error($th->getMessage()); } + $this->register->get('pools')->get('logs')->reclaim(); } } diff --git a/src/Appwrite/Platform/Workers/StatsUsageDump.php b/src/Appwrite/Platform/Workers/StatsUsageDump.php index 77ec3f13e6..b9d486e0d8 100644 --- a/src/Appwrite/Platform/Workers/StatsUsageDump.php +++ b/src/Appwrite/Platform/Workers/StatsUsageDump.php @@ -70,9 +70,9 @@ class StatsUsageDump extends Action ]; /** - * @var callable(Document): Database + * @var callable */ - protected $getLogsDB; + protected mixed $getLogsDB; protected array $periods = [ '1h' => 'Y-m-d H:00', @@ -126,10 +126,10 @@ class StatsUsageDump extends Action continue; } - Console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); + console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); try { - /** @var Database $dbForProject */ + /** @var \Utopia\Database\Database $dbForProject */ $dbForProject = $getProjectDB($project); foreach ($stats['keys'] ?? [] as $key => $value) { if ($value == 0) { @@ -169,7 +169,7 @@ class StatsUsageDump extends Action } } } catch (\Exception $e) { - Console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); + console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); } } } @@ -190,7 +190,8 @@ class StatsUsageDump extends Action } } - $dbForLogs = ($this->getLogsDB)($project); + /** @var \Utopia\Database\Database $dbForLogs*/ + $dbForLogs = call_user_func($this->getLogsDB, $project); try { $dbForLogs->createOrUpdateDocumentsWithIncrease( @@ -202,5 +203,7 @@ class StatsUsageDump extends Action } catch (\Throwable $th) { Console::error($th->getMessage()); } + + $this->register->get('pools')->get('logs')->reclaim(); } } diff --git a/src/Appwrite/PubSub/Adapter/Pool.php b/src/Appwrite/PubSub/Adapter/Pool.php deleted file mode 100644 index a498118dae..0000000000 --- a/src/Appwrite/PubSub/Adapter/Pool.php +++ /dev/null @@ -1,46 +0,0 @@ -delegate(__FUNCTION__, \func_get_args()); - } - - public function subscribe($channels, $callback): void - { - $this->delegate(__FUNCTION__, \func_get_args()); - } - - public function publish($channel, $message): void - { - $this->delegate(__FUNCTION__, \func_get_args()); - } - - /** - * Forward method calls to the internal adapter instance via the pool. - * - * Required because __call() can't be used to implement abstract methods. - * - * @param string $method - * @param array $args - * @return mixed - * @throws DatabaseException - */ - public function delegate(string $method, array $args): mixed - { - return $this->pool->use(function (Adapter $adapter) use ($method, $args) { - return $adapter->{$method}(...$args); - }); - } -} diff --git a/tests/resources/docker/docker-compose.yml b/tests/resources/docker/docker-compose.yml index 779d63d6ed..e549ac27a5 100644 --- a/tests/resources/docker/docker-compose.yml +++ b/tests/resources/docker/docker-compose.yml @@ -35,7 +35,7 @@ services: - VERSION=dev restart: unless-stopped ports: - - "9501:80" + - 9501:80 networks: - appwrite labels: @@ -52,12 +52,15 @@ services: - ./phpunit.xml:/usr/src/code/phpunit.xml - ./tests:/usr/src/code/tests - ./app:/usr/src/code/app + # - ./vendor:/usr/src/code/vendor - ./docs:/usr/src/code/docs - ./public:/usr/src/code/public - ./src:/usr/src/code/src + - ./debug:/tmp depends_on: - mariadb - redis + # - clamav environment: - _APP_COMPRESSION_MIN_SIZE_BYTES - _APP_ENV @@ -352,6 +355,33 @@ services: volumes: - appwrite-redis:/data:rw + # clamav: + # image: appwrite/clamav:1.2.0 + # container_name: appwrite-clamav + # restart: unless-stopped + # networks: + # - appwrite + # volumes: + # - appwrite-uploads:/storage/uploads + + + # redis-commander: + # image: rediscommander/redis-commander:latest + # restart: unless-stopped + # networks: + # - appwrite + # environment: + # - REDIS_HOSTS=redis + # ports: + # - "8081:8081" + + # webgrind: + # image: 'jokkedk/webgrind:latest' + # volumes: + # - './debug:/tmp' + # ports: + # - '3001:80' + networks: gateway: appwrite: From d137b41c0b970d033aa5722e08d55ed2d3e7f72e Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 1 May 2025 11:22:19 +0530 Subject: [PATCH 36/39] add: expose source report for cloud. --- src/Appwrite/Platform/Workers/Migrations.php | 25 +++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index 4939dc8143..db156d7798 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -34,6 +34,13 @@ class Migrations extends Action protected Document $project; + /** + * Accessed on cloud for telemetry. + * + * @type array + */ + private array $sourceReport = []; + /** * @var callable */ @@ -101,7 +108,7 @@ class Migrations extends Action $source = $migration->getAttribute('source'); $credentials = $migration->getAttribute('credentials'); - return match ($source) { + $migrationSource = match ($source) { Firebase::getName() => new Firebase( json_decode($credentials['serviceAccount'], true), ), @@ -130,6 +137,10 @@ class Migrations extends Action ), default => throw new \Exception('Invalid source type'), }; + + $this->sourceReport = $migrationSource->report(); + + return $migrationSource; } /** @@ -381,4 +392,16 @@ class Migrations extends Action } } } + + /** + * Returns a report of resources in the source. + * + * Should be called after `processSource()` to ensure the data is populated. + * + * @return array Resource type mapped to their counts. + */ + protected function getSourceReport(): array + { + return $this->sourceReport; + } } From 606fea5871a9499a6a3fb5284e14c1f1af606ba4 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 1 May 2025 11:31:51 +0530 Subject: [PATCH 37/39] address comments. --- src/Appwrite/Platform/Workers/Migrations.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index db156d7798..3e8317e5e6 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -35,9 +35,9 @@ class Migrations extends Action protected Document $project; /** - * Accessed on cloud for telemetry. + * Cached for performance. * - * @type array + * @var array */ private array $sourceReport = []; From 8c7ccfcc126820e71eff67d221e00c13a68ca85c Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 1 May 2025 11:37:58 +0530 Subject: [PATCH 38/39] address comments. --- src/Appwrite/Platform/Workers/Migrations.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index 3e8317e5e6..f2d7293c66 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -256,8 +256,6 @@ class Migrations extends Action $source = $this->processSource($migration); $destination = $this->processDestination($migration, $tempAPIKey); - $source->report(); - $transfer = new Transfer( $source, $destination From dcf11df193453b0c09febc9f2c2509edca5c8536 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 1 May 2025 12:16:07 +0530 Subject: [PATCH 39/39] remove: method and expose the variable itself. --- src/Appwrite/Platform/Workers/Migrations.php | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index f2d7293c66..d342875369 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -39,7 +39,7 @@ class Migrations extends Action * * @var array */ - private array $sourceReport = []; + protected array $sourceReport = []; /** * @var callable @@ -390,16 +390,4 @@ class Migrations extends Action } } } - - /** - * Returns a report of resources in the source. - * - * Should be called after `processSource()` to ensure the data is populated. - * - * @return array Resource type mapped to their counts. - */ - protected function getSourceReport(): array - { - return $this->sourceReport; - } }