From fc0fd2f6ac0f0ff8af9bf741b1a53b89c6e214f6 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 13 Apr 2026 17:10:38 +0530 Subject: [PATCH] fix: scope graphql resources to resolver coroutine --- src/Appwrite/GraphQL/ResolverLock.php | 17 ++++++++++++++ src/Appwrite/GraphQL/Resolvers.php | 33 ++++++++++----------------- 2 files changed, 29 insertions(+), 21 deletions(-) create mode 100644 src/Appwrite/GraphQL/ResolverLock.php diff --git a/src/Appwrite/GraphQL/ResolverLock.php b/src/Appwrite/GraphQL/ResolverLock.php new file mode 100644 index 0000000000..24d6d249b0 --- /dev/null +++ b/src/Appwrite/GraphQL/ResolverLock.php @@ -0,0 +1,17 @@ +channel = new Channel(1); + } +} diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index 9e409b39ae..cde649bb02 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -6,9 +6,7 @@ use Appwrite\GraphQL\Exception as GQLException; use Appwrite\Promises\Swoole; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use stdClass; use Swoole\Coroutine; -use Swoole\Coroutine\Channel; use Utopia\DI\Container; use Utopia\Http\Exception; use Utopia\Http\Http; @@ -31,23 +29,17 @@ class Resolvers /** * Get the request-scoped lock shared by GraphQL resolver coroutines * for the current HTTP request. - * - * @return stdClass{channel: Channel, owner: int|null, depth: int} */ - private static function getLock(Http $utopia): stdClass + private static function getLock(Http $utopia): ResolverLock { $container = self::getResolverContainer($utopia); if (!$container->has('graphql:lock')) { - $lock = new stdClass(); - $lock->channel = new Channel(1); - $lock->owner = null; - $lock->depth = 0; + $lock = new ResolverLock(); $container->set('graphql:lock', static fn () => $lock); } - /** @var stdClass{channel: Channel, owner: int|null, depth: int} $lock */ $lock = $container->get('graphql:lock'); return $lock; @@ -56,10 +48,8 @@ class Resolvers /** * Acquire the request-scoped resolver lock. Re-entering from the * same coroutine only increments depth to avoid self-deadlock. - * - * @param stdClass{channel: Channel, owner: int|null, depth: int} $lock */ - private static function acquireLock(stdClass $lock): void + private static function acquireLock(ResolverLock $lock): void { $cid = Coroutine::getCid(); @@ -75,10 +65,8 @@ class Resolvers /** * Release the request-scoped resolver lock. - * - * @param stdClass{channel: Channel, owner: int|null, depth: int} $lock */ - private static function releaseLock(stdClass $lock): void + private static function releaseLock(ResolverLock $lock): void { if ($lock->owner !== Coroutine::getCid()) { return; @@ -93,6 +81,7 @@ class Resolvers $lock->owner = null; $lock->channel->pop(); } + /** * Create a resolver for a given API {@see Route}. * @@ -330,7 +319,7 @@ class Resolvers * @param Http $utopia * @param Request $request * @param Response $response - * @param stdClass{channel: Channel, owner: int|null, depth: int} $lock + * @param ResolverLock $lock * @param callable $resolve * @param callable $reject * @param callable|null $beforeResolve @@ -342,7 +331,7 @@ class Resolvers Http $utopia, Request $request, Response $response, - stdClass $lock, + ResolverLock $lock, callable $resolve, callable $reject, ?callable $beforeResolve = null, @@ -354,10 +343,12 @@ class Resolvers } $request = clone $request; - $utopia->setResource('request', static fn () => $request); + $container = self::getResolverContainer($utopia); self::acquireLock($lock); try { + $container->set('request', static fn () => $request); + $container->set('response', static fn () => $response); $response->setContentType(Response::CONTENT_TYPE_NULL); $response->clearSent(); @@ -368,14 +359,14 @@ class Resolvers $payload = $response->getPayload(); $statusCode = $response->getStatusCode(); } catch (\Throwable $e) { - self::releaseLock($lock); if ($beforeReject) { $e = $beforeReject($e); } $reject($e); return; + } finally { + self::releaseLock($lock); } - self::releaseLock($lock); if ($statusCode < 200 || $statusCode >= 400) { if ($beforeReject) {