From 06068c04066e41b91992c42e59156cb82c0dc701 Mon Sep 17 00:00:00 2001 From: fogelito Date: Tue, 12 May 2026 20:51:57 +0300 Subject: [PATCH 1/5] skip subQueryTargets --- app/controllers/api/users.php | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 3f52069609..2251520a92 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -731,6 +731,13 @@ Http::get('/v1/users') $cursor->setValue($cursorDocument); } + $skipFilters = ['subQueryAuthenticators', 'subQuerySessions', 'subQueryTokens', 'subQueryChallenges', 'subQueryMemberships']; + + $selects = Query::getByType($queries, [Query::TYPE_SELECT]); + if (empty($selects)) { + $skipFilters[] = 'subQueryTargets'; + } + $users = []; $total = 0; @@ -743,7 +750,28 @@ Http::get('/v1/users') } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - }, ['subQueryAuthenticators', 'subQuerySessions', 'subQueryTokens', 'subQueryChallenges', 'subQueryMemberships']); + }, $skipFilters); + + if (empty($selects) && !empty($users)) { + $sequences = []; + foreach ($users as $user) { + $sequences[] = $user->getSequence(); + } + + $targets = $dbForProject->find('targets', [ + Query::equal('userInternalId', $sequences), + Query::limit(PHP_INT_MAX), + ]); + + $targetsByUser = []; + foreach ($targets as $target) { + $targetsByUser[$target->getAttribute('userInternalId')][] = $target; + } + + foreach ($users as $user) { + $user->setAttribute('targets', $targetsByUser[$user->getSequence()] ?? []); + } + } $response->dynamic(new Document([ 'users' => $users, From 2ea66a0d8ba1ab6ddedcddc0c6d85994aaba5165 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 13 May 2026 08:36:57 +0300 Subject: [PATCH 2/5] APP_LIMIT_SUBQUERY --- app/controllers/api/users.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 2251520a92..38f1fb2159 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -760,7 +760,7 @@ Http::get('/v1/users') $targets = $dbForProject->find('targets', [ Query::equal('userInternalId', $sequences), - Query::limit(PHP_INT_MAX), + Query::limit(\count($sequences) * APP_LIMIT_SUBQUERY), ]); $targetsByUser = []; From 1435e2c526f8472df6ecc622c3d840278b38a9bc Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 13 May 2026 08:41:14 +0300 Subject: [PATCH 3/5] use max --- app/controllers/api/users.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 38f1fb2159..2251520a92 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -760,7 +760,7 @@ Http::get('/v1/users') $targets = $dbForProject->find('targets', [ Query::equal('userInternalId', $sequences), - Query::limit(\count($sequences) * APP_LIMIT_SUBQUERY), + Query::limit(PHP_INT_MAX), ]); $targetsByUser = []; From d9446fa8926bc1743940dbd65868ecf369d5501b Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 14 May 2026 14:45:00 +0300 Subject: [PATCH 4/5] address --- app/controllers/api/users.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 2251520a92..9230ae0190 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -758,10 +758,14 @@ Http::get('/v1/users') $sequences[] = $user->getSequence(); } - $targets = $dbForProject->find('targets', [ - Query::equal('userInternalId', $sequences), - Query::limit(PHP_INT_MAX), - ]); + try { + $targets = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->find('targets', [ + Query::equal('userInternalId', $sequences), + Query::limit(PHP_INT_MAX), + ])); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } $targetsByUser = []; foreach ($targets as $target) { From d6ca57d0089d8a0d74972d5871e99ece2686d4c3 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 25 May 2026 06:36:25 +0000 Subject: [PATCH 5/5] fix(uploads): reset queueForEvents on idempotent chunked-upload retry The early-return branches added in #12138 for already-complete chunked uploads bypass the trailing $queueForEvents->setParam('bucketId', ...) calls. The action returns cleanly, the shutdown hook then calls Event::generateEvents with empty params, throws \InvalidArgumentException, and the error handler maps it to HTTP 500. Reset the queue on each early-return path so the shutdown short-circuits on the empty event string -- the resource was created on a previous request and that request already fired the event; replaying it would double-trigger webhooks/functions. Applied to Storage Files Create, Functions Deployments Create, and Sites Deployments Create -- all three endpoints share the same pattern introduced by #12138. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Platform/Modules/Functions/Http/Deployments/Create.php | 3 +++ .../Platform/Modules/Sites/Http/Deployments/Create.php | 3 +++ .../Platform/Modules/Storage/Http/Buckets/Files/Create.php | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php index 9af5491598..25863d424c 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php @@ -227,6 +227,7 @@ class Create extends Action } if ($completed) { + $queueForEvents->reset(); return; } @@ -249,6 +250,8 @@ class Create extends Action $metadata = \array_merge($deployment->getAttribute('sourceMetadata', []), $metadata); if ($uploaded === $chunks) { + $queueForEvents->reset(); + $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) ->dynamic($deployment, Response::MODEL_DEPLOYMENT); diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php index d27755d106..5fd724346c 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php @@ -227,6 +227,7 @@ class Create extends Action } if ($completed) { + $queueForEvents->reset(); return; } @@ -257,6 +258,8 @@ class Create extends Action $metadata = \array_merge($deployment->getAttribute('sourceMetadata', []), $metadata); if ($uploaded === $chunks) { + $queueForEvents->reset(); + $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) ->dynamic($deployment, Response::MODEL_DEPLOYMENT); diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php index 95ef2cbe28..348b19c039 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php @@ -320,6 +320,7 @@ class Create extends Action } if ($completed) { + $queueForEvents->reset(); return; } @@ -337,6 +338,8 @@ class Create extends Action throw new Exception(Exception::STORAGE_FILE_ALREADY_EXISTS); } + $queueForEvents->reset(); + $response ->setStatusCode(Response::STATUS_CODE_OK) ->dynamic($file, Response::MODEL_FILE);